Commit 447dcc0c authored by Sean Young's avatar Sean Young Committed by Mauro Carvalho Chehab

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: default avatarSean Young <sean@mess.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 8d406881
...@@ -111,6 +111,15 @@ config IR_XMP_DECODER ...@@ -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
......
...@@ -14,6 +14,7 @@ obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o ...@@ -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
......
// 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");
...@@ -134,8 +134,7 @@ static struct rc_map_list imon_pad_map = { ...@@ -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,
} }
}; };
......
...@@ -118,6 +118,12 @@ struct ir_raw_event_ctrl { ...@@ -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 */
......
...@@ -68,6 +68,8 @@ static const struct { ...@@ -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 { ...@@ -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" },
}; };
/** /**
......
...@@ -36,6 +36,7 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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)
......
...@@ -186,6 +186,7 @@ struct lirc_scancode { ...@@ -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 { ...@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment