[media] rc: ir-rc6-decoder: Add encode capability
authorAntti Seppälä <a.seppala@gmail.com>
Tue, 31 Mar 2015 17:48:09 +0000 (14:48 -0300)
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>
Thu, 14 May 2015 17:33:00 +0000 (14:33 -0300)
Add the capability to encode RC-6 and RC-6A scancodes as raw events.
The protocol is chosen based on the specified protocol mask, and
whether all the required bits are set in the scancode mask, and none of
the unused bits are set in the scancode data.

The Manchester modulation helper is used several times with various
timings so that RC-6 header preamble, the header, header trailing bit
and the data itself can be modulated correctly.

Signed-off-by: Antti Seppälä <a.seppala@gmail.com>
Cc: James Hogan <james@albanarts.com>
Cc: David Härdeman <david@hardeman.nu>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
drivers/media/rc/ir-rc6-decoder.c

index d16bc67af732251998fb280b86d887835cf39e1b..f9c70baf6e0cb1f071f0a6f7e2ac9e81cf131817 100644 (file)
@@ -291,11 +291,133 @@ out:
        return -EINVAL;
 }
 
+static struct ir_raw_timings_manchester ir_rc6_timings[4] = {
+       {
+               .leader                 = RC6_PREFIX_PULSE,
+               .pulse_space_start      = 0,
+               .clock                  = RC6_UNIT,
+               .invert                 = 1,
+               .trailer_space          = RC6_PREFIX_SPACE,
+       },
+       {
+               .clock                  = RC6_UNIT,
+               .invert                 = 1,
+       },
+       {
+               .clock                  = RC6_UNIT * 2,
+               .invert                 = 1,
+       },
+       {
+               .clock                  = RC6_UNIT,
+               .invert                 = 1,
+               .trailer_space          = RC6_SUFFIX_SPACE,
+       },
+};
+
+static int ir_rc6_validate_filter(const struct rc_scancode_filter *scancode,
+                                 unsigned int important_bits)
+{
+       /* all important bits of scancode should be set in mask */
+       if (~scancode->mask & important_bits)
+               return -EINVAL;
+       /* extra bits in mask should be zero in data */
+       if (scancode->mask & scancode->data & ~important_bits)
+               return -EINVAL;
+       return 0;
+}
+
+/**
+ * ir_rc6_encode() - Encode a scancode as a stream of raw events
+ *
+ * @protocols: allowed protocols
+ * @scancode:  scancode filter describing scancode (helps distinguish between
+ *             protocol subtypes when scancode is ambiguous)
+ * @events:    array of raw ir events to write into
+ * @max:       maximum size of @events
+ *
+ * Returns:    The number of events written.
+ *             -ENOBUFS if there isn't enough space in the array to fit the
+ *             encoding. In this case all @max events will have been written.
+ *             -EINVAL if the scancode is ambiguous or invalid.
+ */
+static int ir_rc6_encode(u64 protocols,
+                        const struct rc_scancode_filter *scancode,
+                        struct ir_raw_event *events, unsigned int max)
+{
+       int ret;
+       struct ir_raw_event *e = events;
+
+       if (protocols & RC_BIT_RC6_0 &&
+           !ir_rc6_validate_filter(scancode, 0xffff)) {
+
+               /* Modulate the preamble */
+               ret = ir_raw_gen_manchester(&e, max, &ir_rc6_timings[0], 0, 0);
+               if (ret < 0)
+                       return ret;
+
+               /* Modulate the header (Start Bit & Mode-0) */
+               ret = ir_raw_gen_manchester(&e, max - (e - events),
+                                           &ir_rc6_timings[1],
+                                           RC6_HEADER_NBITS, (1 << 3));
+               if (ret < 0)
+                       return ret;
+
+               /* Modulate Trailer Bit */
+               ret = ir_raw_gen_manchester(&e, max - (e - events),
+                                           &ir_rc6_timings[2], 1, 0);
+               if (ret < 0)
+                       return ret;
+
+               /* Modulate rest of the data */
+               ret = ir_raw_gen_manchester(&e, max - (e - events),
+                                           &ir_rc6_timings[3], RC6_0_NBITS,
+                                           scancode->data);
+               if (ret < 0)
+                       return ret;
+
+       } else if (protocols & (RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 |
+                               RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE) &&
+                  !ir_rc6_validate_filter(scancode, 0x8fffffff)) {
+
+               /* Modulate the preamble */
+               ret = ir_raw_gen_manchester(&e, max, &ir_rc6_timings[0], 0, 0);
+               if (ret < 0)
+                       return ret;
+
+               /* Modulate the header (Start Bit & Header-version 6 */
+               ret = ir_raw_gen_manchester(&e, max - (e - events),
+                                           &ir_rc6_timings[1],
+                                           RC6_HEADER_NBITS, (1 << 3 | 6));
+               if (ret < 0)
+                       return ret;
+
+               /* Modulate Trailer Bit */
+               ret = ir_raw_gen_manchester(&e, max - (e - events),
+                                           &ir_rc6_timings[2], 1, 0);
+               if (ret < 0)
+                       return ret;
+
+               /* Modulate rest of the data */
+               ret = ir_raw_gen_manchester(&e, max - (e - events),
+                                           &ir_rc6_timings[3],
+                                           fls(scancode->mask),
+                                           scancode->data);
+               if (ret < 0)
+                       return ret;
+
+       } else {
+               return -EINVAL;
+       }
+
+       return e - events;
+}
+
 static struct ir_raw_handler rc6_handler = {
        .protocols      = RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 |
                          RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 |
                          RC_BIT_RC6_MCE,
        .decode         = ir_rc6_decode,
+       .encode         = ir_rc6_encode,
 };
 
 static int __init ir_rc6_decode_init(void)