Re: RFC: Remote control sensors and Linux input layer

From: Krzysztof Halasa
Date: Tue Jun 30 2009 - 14:04:24 EST


> I'm attaching a test patch for "1" and "3". It's incomplete, breaks
> bttv, but it currently works with my Philips RC and Super 007 DVB-T
> board.

Well, the patch (against 2.6.30):

--- a/drivers/media/common/ir-functions.c
+++ b/drivers/media/common/ir-functions.c
@@ -260,47 +260,12 @@ int ir_decode_biphase(u32 *samples, int count, int low, int high)
}
EXPORT_SYMBOL_GPL(ir_decode_biphase);

-/* RC5 decoding stuff, moved from bttv-input.c to share it with
- * saa7134 */
-
-/* decode raw bit pattern to RC5 code */
-static u32 ir_rc5_decode(unsigned int code)
-{
- unsigned int org_code = code;
- unsigned int pair;
- unsigned int rc5 = 0;
- int i;
-
- for (i = 0; i < 14; ++i) {
- pair = code & 0x3;
- code >>= 2;
-
- rc5 <<= 1;
- switch (pair) {
- case 0:
- case 2:
- break;
- case 1:
- rc5 |= 1;
- break;
- case 3:
- dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code);
- return 0;
- }
- }
- dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
- "instr=%x\n", rc5, org_code, RC5_START(rc5),
- RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5));
- return rc5;
-}
-
void ir_rc5_timer_end(unsigned long data)
{
struct card_ir *ir = (struct card_ir *)data;
struct timeval tv;
unsigned long current_jiffies, timeout;
u32 gap;
- u32 rc5 = 0;

/* get time */
current_jiffies = jiffies;
@@ -317,46 +282,46 @@ void ir_rc5_timer_end(unsigned long data)
/* signal we're ready to start a new code */
ir->active = 0;

- /* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */
- if (gap < 28000) {
- dprintk(1, "ir-common: spurious timer_end\n");
+ if (ir->last_bit == 1 && (ir->code & 2))
+ ir->last_bit--; /* bit1: 1 -> bit0: 0 doesn't generate an IRQ */
+
+ if (ir->last_bit) {
+ /* ignore spurious codes (caused by light/other remotes) */
+ dprintk(1, "ir-common: short code: %X\n", ir->code);
return;
}

- if (ir->last_bit < 20) {
- /* ignore spurious codes (caused by light/other remotes) */
- dprintk(1, "ir-common: short code: %x\n", ir->code);
- } else {
- ir->code = (ir->code << ir->shift_by) | 1;
- rc5 = ir_rc5_decode(ir->code);
-
- /* two start bits? */
- if (RC5_START(rc5) != ir->start) {
- dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5));
-
- /* right address? */
- } else if (RC5_ADDR(rc5) == ir->addr) {
- u32 toggle = RC5_TOGGLE(rc5);
- u32 instr = RC5_INSTR(rc5);
-
- /* Good code, decide if repeat/repress */
- if (toggle != RC5_TOGGLE(ir->last_rc5) ||
- instr != RC5_INSTR(ir->last_rc5)) {
- dprintk(1, "ir-common: instruction %x, toggle %x\n", instr,
- toggle);
- ir_input_nokey(ir->dev, &ir->ir);
- ir_input_keydown(ir->dev, &ir->ir, instr,
- instr);
- }
+ //ir->code = (ir->code << ir->shift_by) | 1; /* FIXME check bttv */

- /* Set/reset key-up timer */
- timeout = current_jiffies +
- msecs_to_jiffies(ir->rc5_key_timeout);
- mod_timer(&ir->timer_keyup, timeout);
+ if (!RC5_ADDR(ir->code)) {
+ dprintk(1, "ir-common: bad RC5 code 0x%x\n", ir->code);
+ return;
+ }

- /* Save code for repeat test */
- ir->last_rc5 = rc5;
+ dprintk(1, "ir-common: rc5=0x%X, toggle=0x%X, address=0x%X, instr=%x\n",
+ ir->code, RC5_TOGGLE(ir->code), RC5_ADDR(ir->code),
+ RC5_INSTR(ir->code));
+
+ if (RC5_ADDR(ir->code) == ir->addr) {
+ u32 toggle = RC5_TOGGLE(ir->code);
+ u32 instr = RC5_INSTR(ir->code);
+
+ /* Good code, decide if repeat/repress */
+ if (toggle != RC5_TOGGLE(ir->last_rc5) ||
+ instr != RC5_INSTR(ir->last_rc5)) {
+ dprintk(1, "ir-common: instruction %x, toggle %x\n",
+ instr, toggle);
+ ir_input_nokey(ir->dev, &ir->ir);
+ ir_input_keydown(ir->dev, &ir->ir, instr, instr);
}
+
+ /* Set/reset key-up timer */
+ timeout = current_jiffies +
+ msecs_to_jiffies(ir->rc5_key_timeout);
+ mod_timer(&ir->timer_keyup, timeout);
+
+ /* Save code for repeat test */
+ ir->last_rc5 = ir->code;
}
}
EXPORT_SYMBOL_GPL(ir_rc5_timer_end);
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/common/ir-keymaps.c
@@ -1487,6 +1487,53 @@ IR_KEYTAB_TYPE ir_codes_pv951[IR_KEYTAB_SIZE] = {

EXPORT_SYMBOL_GPL(ir_codes_pv951);

+/*
+ Philips RC2592 with MODE pressed, used by Philips TV sets
+ RC5 group = 8, commands are 7-bit wide (1 start bit)
+*/
+IR_KEYTAB_TYPE ir_codes_rc2592_sat[IR_KEYTAB_SIZE] = {
+ [ 0x00 ] = KEY_0,
+ [ 0x01 ] = KEY_1,
+ [ 0x02 ] = KEY_2,
+ [ 0x03 ] = KEY_3,
+ [ 0x04 ] = KEY_4,
+ [ 0x05 ] = KEY_5,
+ [ 0x06 ] = KEY_6,
+ [ 0x07 ] = KEY_7,
+ [ 0x08 ] = KEY_8,
+ [ 0x09 ] = KEY_9,
+
+ [ 0x0C ] = KEY_SUSPEND,
+
+ [ 0x6B ] = KEY_RED,
+ [ 0x6C ] = KEY_GREEN,
+ [ 0x6D ] = KEY_YELLOW,
+ [ 0x6E ] = KEY_BLUE,
+ [ 0x6F ] = KEY_OPTION, /* white */
+
+ [ 0x2A ] = KEY_TIME,
+ [ 0x2C ] = KEY_QUESTION,
+ [ 0x2B ] = KEY_ZOOM,
+ [ 0x3C ] = KEY_TEXT,
+
+ [ 0x52 ] = KEY_MENU,
+ [ 0x57 ] = KEY_OK,
+ [ 0x50 ] = KEY_UP,
+ [ 0x51 ] = KEY_DOWN,
+ [ 0x55 ] = KEY_LEFT,
+ [ 0x56 ] = KEY_RIGHT,
+
+ [ 0x20 ] = KEY_CHANNELUP,
+ [ 0x21 ] = KEY_CHANNELDOWN,
+
+ [ 0x0F ] = KEY_INFO, /* i+ */
+ [ 0x22 ] = KEY_PREVIOUS, /* P<P */
+ [ 0x23 ] = KEY_LANGUAGE, /* I-II */
+ [ 0x38 ] = KEY_SUBTITLE /* [1][2] */
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_rc2592_sat);
+
/* generic RC5 keytable */
/* see http://users.pandora.be/nenya/electronics/rc5/codes00.htm */
/* used by old (black) Hauppauge remotes */
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -6105,6 +6105,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_AVERMEDIA_307:
case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
+ case SAA7134_BOARD_AVERMEDIA_SUPER_007:
case SAA7134_BOARD_AVERMEDIA_777:
case SAA7134_BOARD_AVERMEDIA_M135A:
/* case SAA7134_BOARD_SABRENT_SBTTVFM: */ /* not finished yet */
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -695,9 +695,9 @@ static int saa7134_hw_enable2(struct saa7134_dev *dev)
if (dev->has_remote == SAA7134_REMOTE_GPIO && dev->remote) {
if (dev->remote->mask_keydown & 0x10000)
irq2_mask |= SAA7134_IRQ2_INTE_GPIO16;
- else if (dev->remote->mask_keydown & 0x40000)
+ if (dev->remote->mask_keydown & 0x40000)
irq2_mask |= SAA7134_IRQ2_INTE_GPIO18;
- else if (dev->remote->mask_keyup & 0x40000)
+ if (dev->remote->mask_keyup & 0x40000)
irq2_mask |= SAA7134_IRQ2_INTE_GPIO18A;
}

--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -374,7 +374,6 @@ void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir)
ir->timer_keyup.data = (unsigned long)ir;
ir->shift_by = 2;
ir->start = 0x2;
- ir->addr = 0x17;
ir->rc5_key_timeout = ir_rc5_key_timeout;
ir->rc5_remote_gap = ir_rc5_remote_gap;
} else if (ir->nec_gpio) {
@@ -402,6 +401,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
int rc5_gpio = 0;
int nec_gpio = 0;
int ir_type = IR_TYPE_OTHER;
+ int addr = 0;
int err;

if (dev->has_remote != SAA7134_REMOTE_GPIO)
@@ -483,6 +483,12 @@ int saa7134_input_init1(struct saa7134_dev *dev)
saa_setb(SAA7134_GPIO_GPMODE1, 0x1);
saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1);
break;
+ case SAA7134_BOARD_AVERMEDIA_SUPER_007:
+ ir_codes = ir_codes_rc2592_sat;
+ addr = 8;
+ mask_keyup = 0x40000;
+ rc5_gpio = 1;
+ break;
case SAA7134_BOARD_KWORLD_TERMINATOR:
ir_codes = ir_codes_pixelview;
mask_keycode = 0x00001f;
@@ -562,6 +568,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
ir_codes = ir_codes_asus_pc39;
+ addr = 0x17;
mask_keydown = 0x0040000;
rc5_gpio = 1;
break;
@@ -623,6 +630,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
ir->mask_keyup = mask_keyup;
ir->polling = polling;
ir->rc5_gpio = rc5_gpio;
+ ir->addr = addr;
ir->nec_gpio = nec_gpio;

/* init input device */
@@ -748,26 +756,60 @@ static int saa7134_rc5_irq(struct saa7134_dev *dev)
tv.tv_usec - ir->base_time.tv_usec;
}

+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
+ printk(KERN_DEBUG "rc5 irq: %lu %lu %u gap %u\n", tv.tv_sec, tv.tv_usec,
+ (saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) / 0x40000) & 1, gap);
+
/* active code => add bit */
if (ir->active) {
/* only if in the code (otherwise spurious IRQ or timer
late) */
- if (ir->last_bit < 28) {
- ir->last_bit = (gap - ir_rc5_remote_gap / 2) /
- ir_rc5_remote_gap;
- ir->code |= 1 << ir->last_bit;
+ gap = (gap + ir_rc5_remote_gap / 2) / ir_rc5_remote_gap;
+ if (ir->last_bit && ir->code) {
+ u32 last = ir->code >> ir->last_bit;
+
+ printk(KERN_DEBUG "RC5 gap %u last %X last_bit %u code "
+ "0x%X\n", gap, last, ir->last_bit, ir->code);
+
+ if ((last & 1) == 1) {
+ if (gap == 2) /* 1 -> 1 */
+ ir->code |= 1 << --ir->last_bit;
+ else if (gap == 3) /* 1 -> 00 */
+ ir->last_bit -= 2;
+ else if (gap == 4) /* 1 -> 01 */
+ ir->code |= 1 << (ir->last_bit -= 2);
+ else
+ goto invalid;
+ } else if ((last & 3) == 0) {
+ if (gap == 2) /* 00 -> 0 */
+ ir->last_bit--;
+ else if (gap == 3) /* 00 -> 1 */
+ ir->code |= 1 << --ir->last_bit;
+ else
+ goto invalid;
+ } else
+ goto invalid;
+
+ printk(KERN_DEBUG "RC5: last_bit %u, code 0x%X\n",
+ ir->last_bit, ir->code);
}
/* starting new code */
} else {
ir->active = 1;
- ir->code = 0;
- ir->base_time = tv;
- ir->last_bit = 0;
+ ir->code = 0x2000; /* start bit is always '1' */
+ ir->last_bit = 13;

timeout = current_jiffies + (500 + 30 * HZ) / 1000;
mod_timer(&ir->timer_end, timeout);
}

+ ir->base_time = tv;
+ return 1;
+
+invalid:
+ ir->code = 0; /* mark as invalid */
return 1;
}

--- a/include/media/ir-common.h
+++ b/include/media/ir-common.h
@@ -37,10 +37,10 @@
#define IR_KEYCODE(tab,code) (((unsigned)code < IR_KEYTAB_SIZE) \
? tab[code] : KEY_RESERVED)

-#define RC5_START(x) (((x)>>12)&3)
+#define RC5_START(x) (((x) >> 13) & 1)
#define RC5_TOGGLE(x) (((x)>>11)&1)
#define RC5_ADDR(x) (((x)>>6)&31)
-#define RC5_INSTR(x) ((x)&63)
+#define RC5_INSTR(x) ((((~(x)) >> 6) & 0x40) | ((x) & 0x3F))

struct ir_input_state {
/* configuration */
@@ -136,6 +136,7 @@ extern IR_KEYTAB_TYPE ir_codes_gotview7135[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_purpletv[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_pctv_sedna[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_pv951[IR_KEYTAB_SIZE];
+extern IR_KEYTAB_TYPE ir_codes_rc2592_sat[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_rc5_tv[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_pinnacle_color[IR_KEYTAB_SIZE];

--
Krzysztof Halasa
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/