media: rc: add new imon protocol decoder and encoder

This makes it possible to use the various iMON remotes with any raw IR
RC device.

Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
This commit is contained in:
Sean Young 2017-12-03 11:06:54 -05:00 committed by Mauro Carvalho Chehab
parent 8d4068810d
commit 447dcc0cf1
8 changed files with 220 additions and 5 deletions

View File

@ -111,6 +111,15 @@ config IR_XMP_DECODER
---help--- ---help---
Enable this option if you have IR with XMP protocol, and Enable this option if you have IR with XMP protocol, and
if the IR is decoded in software if the IR is decoded in software
config IR_IMON_DECODER
tristate "Enable IR raw decoder for the iMON protocol"
depends on RC_CORE
---help---
Enable this option if you have iMON PAD or Antec Veris infrared
remote control and you would like to use it with a raw IR
receiver, or if you wish to use an encoder to transmit this IR.
endif #RC_DECODERS endif #RC_DECODERS
menuconfig RC_DEVICES menuconfig RC_DEVICES

View File

@ -14,6 +14,7 @@ obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o
obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o
obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o
obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o
obj-$(CONFIG_IR_IMON_DECODER) += ir-imon-decoder.o
# stand-alone IR receivers/transmitters # stand-alone IR receivers/transmitters
obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o

View File

@ -0,0 +1,193 @@
// SPDX-License-Identifier: GPL-2.0+
// ir-imon-decoder.c - handle iMon protocol
//
// Copyright (C) 2018 by Sean Young <sean@mess.org>
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include "rc-core-priv.h"
#define IMON_UNIT 415662 /* ns */
#define IMON_BITS 30
#define IMON_CHKBITS (BIT(30) | BIT(25) | BIT(24) | BIT(22) | \
BIT(21) | BIT(20) | BIT(19) | BIT(18) | \
BIT(17) | BIT(16) | BIT(14) | BIT(13) | \
BIT(12) | BIT(11) | BIT(10) | BIT(9))
/*
* This protocol has 30 bits. The format is one IMON_UNIT header pulse,
* followed by 30 bits. Each bit is one IMON_UNIT check field, and then
* one IMON_UNIT field with the actual bit (1=space, 0=pulse).
* The check field is always space for some bits, for others it is pulse if
* both the preceding and current bit are zero, else space. IMON_CHKBITS
* defines which bits are of type check.
*
* There is no way to distinguish an incomplete message from one where
* the lower bits are all set, iow. the last pulse is for the lowest
* bit which is 0.
*/
enum imon_state {
STATE_INACTIVE,
STATE_BIT_CHK,
STATE_BIT_START,
STATE_FINISHED
};
/**
* ir_imon_decode() - Decode one iMON pulse or space
* @dev: the struct rc_dev descriptor of the device
* @ev: the struct ir_raw_event descriptor of the pulse/space
*
* This function returns -EINVAL if the pulse violates the state machine
*/
static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev)
{
struct imon_dec *data = &dev->raw->imon;
if (!is_timing_event(ev)) {
if (ev.reset)
data->state = STATE_INACTIVE;
return 0;
}
dev_dbg(&dev->dev,
"iMON decode started at state %d bitno %d (%uus %s)\n",
data->state, data->count, TO_US(ev.duration),
TO_STR(ev.pulse));
for (;;) {
if (!geq_margin(ev.duration, IMON_UNIT, IMON_UNIT / 2))
return 0;
decrease_duration(&ev, IMON_UNIT);
switch (data->state) {
case STATE_INACTIVE:
if (ev.pulse) {
data->state = STATE_BIT_CHK;
data->bits = 0;
data->count = IMON_BITS;
}
break;
case STATE_BIT_CHK:
if (IMON_CHKBITS & BIT(data->count))
data->last_chk = ev.pulse;
else if (ev.pulse)
goto err_out;
data->state = STATE_BIT_START;
break;
case STATE_BIT_START:
data->bits <<= 1;
if (!ev.pulse)
data->bits |= 1;
if (IMON_CHKBITS & BIT(data->count)) {
if (data->last_chk != !(data->bits & 3))
goto err_out;
}
if (!data->count--)
data->state = STATE_FINISHED;
else
data->state = STATE_BIT_CHK;
break;
case STATE_FINISHED:
if (ev.pulse)
goto err_out;
rc_keydown(dev, RC_PROTO_IMON, data->bits, 0);
data->state = STATE_INACTIVE;
break;
}
}
err_out:
dev_dbg(&dev->dev,
"iMON decode failed at state %d bitno %d (%uus %s)\n",
data->state, data->count, TO_US(ev.duration),
TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
}
/**
* ir_imon_encode() - Encode a scancode as a stream of raw events
*
* @protocol: protocol to encode
* @scancode: scancode to encode
* @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.
*/
static int ir_imon_encode(enum rc_proto protocol, u32 scancode,
struct ir_raw_event *events, unsigned int max)
{
struct ir_raw_event *e = events;
int i, pulse;
if (!max--)
return -ENOBUFS;
init_ir_raw_event_duration(e, 1, IMON_UNIT);
for (i = IMON_BITS; i >= 0; i--) {
if (BIT(i) & IMON_CHKBITS)
pulse = !(scancode & (BIT(i) | BIT(i + 1)));
else
pulse = 0;
if (pulse == e->pulse) {
e->duration += IMON_UNIT;
} else {
if (!max--)
return -ENOBUFS;
init_ir_raw_event_duration(++e, pulse, IMON_UNIT);
}
pulse = !(scancode & BIT(i));
if (pulse == e->pulse) {
e->duration += IMON_UNIT;
} else {
if (!max--)
return -ENOBUFS;
init_ir_raw_event_duration(++e, pulse, IMON_UNIT);
}
}
if (e->pulse)
e++;
return e - events;
}
static struct ir_raw_handler imon_handler = {
.protocols = RC_PROTO_BIT_IMON,
.decode = ir_imon_decode,
.encode = ir_imon_encode,
.carrier = 38000,
};
static int __init ir_imon_decode_init(void)
{
ir_raw_handler_register(&imon_handler);
pr_info("IR iMON protocol handler initialized\n");
return 0;
}
static void __exit ir_imon_decode_exit(void)
{
ir_raw_handler_unregister(&imon_handler);
}
module_init(ir_imon_decode_init);
module_exit(ir_imon_decode_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sean Young <sean@mess.org>");
MODULE_DESCRIPTION("iMON IR protocol decoder");

View File

@ -134,8 +134,7 @@ static struct rc_map_list imon_pad_map = {
.map = { .map = {
.scan = imon_pad, .scan = imon_pad,
.size = ARRAY_SIZE(imon_pad), .size = ARRAY_SIZE(imon_pad),
/* actual protocol details unknown, hardware decoder */ .rc_proto = RC_PROTO_IMON,
.rc_proto = RC_PROTO_OTHER,
.name = RC_MAP_IMON_PAD, .name = RC_MAP_IMON_PAD,
} }
}; };

View File

@ -118,6 +118,12 @@ struct ir_raw_event_ctrl {
unsigned count; unsigned count;
u32 durations[16]; u32 durations[16];
} xmp; } xmp;
struct imon_dec {
int state;
int count;
int last_chk;
unsigned int bits;
} imon;
}; };
/* macros for IR decoders */ /* macros for IR decoders */

View File

@ -68,6 +68,8 @@ static const struct {
.scancode_bits = 0x1fff, .repeat_period = 250 }, .scancode_bits = 0x1fff, .repeat_period = 250 },
[RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 250 }, [RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 250 },
[RC_PROTO_CEC] = { .name = "cec", .repeat_period = 550 }, [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 550 },
[RC_PROTO_IMON] = { .name = "imon",
.scancode_bits = 0x7fffffff, .repeat_period = 250 },
}; };
/* Used to keep track of known keymaps */ /* Used to keep track of known keymaps */
@ -1004,6 +1006,7 @@ static const struct {
RC_PROTO_BIT_MCIR2_MSE, "mce_kbd", "ir-mce_kbd-decoder" }, RC_PROTO_BIT_MCIR2_MSE, "mce_kbd", "ir-mce_kbd-decoder" },
{ RC_PROTO_BIT_XMP, "xmp", "ir-xmp-decoder" }, { RC_PROTO_BIT_XMP, "xmp", "ir-xmp-decoder" },
{ RC_PROTO_BIT_CEC, "cec", NULL }, { RC_PROTO_BIT_CEC, "cec", NULL },
{ RC_PROTO_BIT_IMON, "imon", "ir-imon-decoder" },
}; };
/** /**

View File

@ -36,6 +36,7 @@
#define RC_PROTO_BIT_SHARP BIT_ULL(RC_PROTO_SHARP) #define RC_PROTO_BIT_SHARP BIT_ULL(RC_PROTO_SHARP)
#define RC_PROTO_BIT_XMP BIT_ULL(RC_PROTO_XMP) #define RC_PROTO_BIT_XMP BIT_ULL(RC_PROTO_XMP)
#define RC_PROTO_BIT_CEC BIT_ULL(RC_PROTO_CEC) #define RC_PROTO_BIT_CEC BIT_ULL(RC_PROTO_CEC)
#define RC_PROTO_BIT_IMON BIT_ULL(RC_PROTO_IMON)
#define RC_PROTO_BIT_ALL \ #define RC_PROTO_BIT_ALL \
(RC_PROTO_BIT_UNKNOWN | RC_PROTO_BIT_OTHER | \ (RC_PROTO_BIT_UNKNOWN | RC_PROTO_BIT_OTHER | \
@ -49,7 +50,8 @@
RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \
RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \
RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \
RC_PROTO_BIT_XMP | RC_PROTO_BIT_CEC) RC_PROTO_BIT_XMP | RC_PROTO_BIT_CEC | \
RC_PROTO_BIT_IMON)
/* All rc protocols for which we have decoders */ /* All rc protocols for which we have decoders */
#define RC_PROTO_BIT_ALL_IR_DECODER \ #define RC_PROTO_BIT_ALL_IR_DECODER \
(RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \
@ -62,7 +64,7 @@
RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \
RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \
RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \
RC_PROTO_BIT_XMP) RC_PROTO_BIT_XMP | RC_PROTO_BIT_IMON)
#define RC_PROTO_BIT_ALL_IR_ENCODER \ #define RC_PROTO_BIT_ALL_IR_ENCODER \
(RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \
@ -75,7 +77,7 @@
RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \
RC_PROTO_BIT_RC6_6A_24 | \ RC_PROTO_BIT_RC6_6A_24 | \
RC_PROTO_BIT_RC6_6A_32 | RC_PROTO_BIT_RC6_MCE | \ RC_PROTO_BIT_RC6_6A_32 | RC_PROTO_BIT_RC6_MCE | \
RC_PROTO_BIT_SHARP) RC_PROTO_BIT_SHARP | RC_PROTO_BIT_IMON)
#define RC_SCANCODE_UNKNOWN(x) (x) #define RC_SCANCODE_UNKNOWN(x) (x)
#define RC_SCANCODE_OTHER(x) (x) #define RC_SCANCODE_OTHER(x) (x)

View File

@ -186,6 +186,7 @@ struct lirc_scancode {
* @RC_PROTO_SHARP: Sharp protocol * @RC_PROTO_SHARP: Sharp protocol
* @RC_PROTO_XMP: XMP protocol * @RC_PROTO_XMP: XMP protocol
* @RC_PROTO_CEC: CEC protocol * @RC_PROTO_CEC: CEC protocol
* @RC_PROTO_IMON: iMon Pad protocol
*/ */
enum rc_proto { enum rc_proto {
RC_PROTO_UNKNOWN = 0, RC_PROTO_UNKNOWN = 0,
@ -211,6 +212,7 @@ enum rc_proto {
RC_PROTO_SHARP = 20, RC_PROTO_SHARP = 20,
RC_PROTO_XMP = 21, RC_PROTO_XMP = 21,
RC_PROTO_CEC = 22, RC_PROTO_CEC = 22,
RC_PROTO_IMON = 23,
}; };
#endif #endif