The attached patch adds support for loadable key maps support to the ati_remote2 driver. This patch is similar to the previous version that I originally posted on 16th February 2008. As requested, I have removed the code that reconfigured this driver to utilise the input core's built in soft auto repeat functionality. Those changes are pending further clarification on how buttons that should not be auto repeated (e.g. mouse buttons) can be excluded from the soft auto repeat implementation. On Tuesday 04 March 2008 22:17:49 Ville Syrjälä wrote: > However, since that seems unlikely to happen, and the input core already > has support for keymaps I'm fine with adding the set/getkeycode stuff. > What I'd like to drop is the support for five different keymaps since > AFAICS that could be handled in a more generic way in user space. I do not agree that the opportunity to remap the resultant keycode generated from any given physical key on the remote dependant upon which 'mode' the remote is currently in should be dropped. Consequently I have left the implementation in the attached patch. I feel that a valid way of thinking about this implementation is that the hardware scancodes consist of the mode and key bytes combined. Given any such implementation it could be assumed that the device has 5 x 46 = 230 keys, it therefore seems entirely reasonable to me to provide a mechanism by which any of those keys can be remapped. On Tuesday 04 March 2008 20:38:22 Ville Syrjälä wrote: > I think you could implement the multiple keymaps thing rather trivially > in user space by having a small daemon listening on the event device > and loading a new keymap when a mode key is pressed. That would limit > the changes to the driver, and it would not require any kernel changes > when if you would need to adapt it to a device that uses a different > driver. I do not agree that it this functionality would be trivial to implement in user space. The primary purpose behind making these changes is to provide full access to all of the keys within X windows, as such one is dependant upon the X input system interfacing to the event device. The goal is to make this device behave as a regular keyboard, not requiring any special case code in any application the user might wish to control using it. I do not feel that my proposed code changes are particularly invasive. I would accept that the static table outlining the default keycode mappings represents a reasonable change. Whilst this table could be eliminated (and generated programmatically) I feel that it serves in some part towards documenting the implementation. All of the changes proposed have no effect upon the operation of the driver out-of-the-box they merely provide a mechanism by which an end user can configure their setup to function as they desire. Best regards Peter Stokes
Signed-off-by: Peter Stokes <linux@xxxxxxxxxxxx> --- linux-2.6.24-orig/drivers/input/misc/ati_remote2.c 2008-01-24 22:58:37.000000000 +0000 +++ linux-2.6.24/drivers/input/misc/ati_remote2.c 2008-04-05 18:34:31.000000000 +0100 @@ -2,7 +2,7 @@ * ati_remote2 - ATI/Philips USB RF remote driver * * Copyright (C) 2005 Ville Syrjala <syrjala@xxxxxx> - * Copyright (C) 2007 Peter Stokes <linux@xxxxxxxxxxxxxxxxxxxxxx> + * Copyright (C) 2007 Peter Stokes <linux@xxxxxxxxxxxx> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -12,7 +12,7 @@ #include <linux/usb/input.h> #define DRIVER_DESC "ATI/Philips USB RF remote driver" -#define DRIVER_VERSION "0.2" +#define DRIVER_VERSION "0.3" MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); @@ -27,7 +27,7 @@ * A remote's "channel" may be altered by pressing and holding the "PC" button for * approximately 3 seconds, after which the button will slowly flash the count of the * currently configured "channel", using the numeric keypad enter a number between 1 and - * 16 and then the "PC" button again, the button will slowly flash the count of the + * 16 and then press the "PC" button again, the button will slowly flash the count of the * newly configured "channel". */ @@ -45,61 +45,66 @@ }; MODULE_DEVICE_TABLE(usb, ati_remote2_id_table); + +static u8 ati_remote2_modes[] = { + 0x01, /* AUX1 */ + 0x02, /* AUX2 */ + 0x04, /* AUX3 */ + 0x08, /* AUX4 */ + 0x10, /* PC */ +}; + static struct { - int hw_code; - int key_code; -} ati_remote2_key_table[] = { - { 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_POWER }, - { 0x0d, KEY_MUTE }, - { 0x10, KEY_VOLUMEUP }, - { 0x11, KEY_VOLUMEDOWN }, - { 0x20, KEY_CHANNELUP }, - { 0x21, KEY_CHANNELDOWN }, - { 0x28, KEY_FORWARD }, - { 0x29, KEY_REWIND }, - { 0x2c, KEY_PLAY }, - { 0x30, KEY_PAUSE }, - { 0x31, KEY_STOP }, - { 0x37, KEY_RECORD }, - { 0x38, KEY_DVD }, - { 0x39, KEY_TV }, - { 0x54, KEY_MENU }, - { 0x58, KEY_UP }, - { 0x59, KEY_DOWN }, - { 0x5a, KEY_LEFT }, - { 0x5b, KEY_RIGHT }, - { 0x5c, KEY_OK }, - { 0x78, KEY_A }, - { 0x79, KEY_B }, - { 0x7a, KEY_C }, - { 0x7b, KEY_D }, - { 0x7c, KEY_E }, - { 0x7d, KEY_F }, - { 0x82, KEY_ENTER }, - { 0x8e, KEY_VENDOR }, - { 0x96, KEY_COFFEE }, - { 0xa9, BTN_LEFT }, - { 0xaa, BTN_RIGHT }, - { 0xbe, KEY_QUESTION }, - { 0xd5, KEY_FRONT }, - { 0xd0, KEY_EDIT }, - { 0xf9, KEY_INFO }, - { (0x00 << 8) | 0x3f, KEY_PROG1 }, - { (0x01 << 8) | 0x3f, KEY_PROG2 }, - { (0x02 << 8) | 0x3f, KEY_PROG3 }, - { (0x03 << 8) | 0x3f, KEY_PROG4 }, - { (0x04 << 8) | 0x3f, KEY_PC }, - { 0, KEY_RESERVED } + u8 hwcode; + u16 keycode[ARRAY_SIZE(ati_remote2_modes)]; +} ati_remote2_keycodes[] = { +/* hwcode AUX1 AUX2 AUX3 AUX4 PC */ + { 0x00, { KEY_0, KEY_0, KEY_0, KEY_0, KEY_0 } }, + { 0x01, { KEY_1, KEY_1, KEY_1, KEY_1, KEY_1 } }, + { 0x02, { KEY_2, KEY_2, KEY_2, KEY_2, KEY_2 } }, + { 0x03, { KEY_3, KEY_3, KEY_3, KEY_3, KEY_3 } }, + { 0x04, { KEY_4, KEY_4, KEY_4, KEY_4, KEY_4 } }, + { 0x05, { KEY_5, KEY_5, KEY_5, KEY_5, KEY_5 } }, + { 0x06, { KEY_6, KEY_6, KEY_6, KEY_6, KEY_6 } }, + { 0x07, { KEY_7, KEY_7, KEY_7, KEY_7, KEY_7 } }, + { 0x08, { KEY_8, KEY_8, KEY_8, KEY_8, KEY_8 } }, + { 0x09, { KEY_9, KEY_9, KEY_9, KEY_9, KEY_9 } }, + { 0x0c, { KEY_POWER, KEY_POWER, KEY_POWER, KEY_POWER, KEY_POWER } }, + { 0x0d, { KEY_MUTE, KEY_MUTE, KEY_MUTE, KEY_MUTE, KEY_MUTE } }, + { 0x10, { KEY_VOLUMEUP, KEY_VOLUMEUP, KEY_VOLUMEUP, KEY_VOLUMEUP, KEY_VOLUMEUP } }, + { 0x11, { KEY_VOLUMEDOWN, KEY_VOLUMEDOWN, KEY_VOLUMEDOWN, KEY_VOLUMEDOWN, KEY_VOLUMEDOWN } }, + { 0x20, { KEY_CHANNELUP, KEY_CHANNELUP, KEY_CHANNELUP, KEY_CHANNELUP, KEY_CHANNELUP } }, + { 0x21, { KEY_CHANNELDOWN, KEY_CHANNELDOWN, KEY_CHANNELDOWN, KEY_CHANNELDOWN, KEY_CHANNELDOWN } }, + { 0x28, { KEY_FORWARD, KEY_FORWARD, KEY_FORWARD, KEY_FORWARD, KEY_FORWARD } }, + { 0x29, { KEY_REWIND, KEY_REWIND, KEY_REWIND, KEY_REWIND, KEY_REWIND } }, + { 0x2c, { KEY_PLAY, KEY_PLAY, KEY_PLAY, KEY_PLAY, KEY_PLAY } }, + { 0x30, { KEY_PAUSE, KEY_PAUSE, KEY_PAUSE, KEY_PAUSE, KEY_PAUSE } }, + { 0x31, { KEY_STOP, KEY_STOP, KEY_STOP, KEY_STOP, KEY_STOP } }, + { 0x37, { KEY_RECORD, KEY_RECORD, KEY_RECORD, KEY_RECORD, KEY_RECORD } }, + { 0x38, { KEY_DVD, KEY_DVD, KEY_DVD, KEY_DVD, KEY_DVD } }, + { 0x39, { KEY_TV, KEY_TV, KEY_TV, KEY_TV, KEY_TV } }, + { 0x3F, { KEY_PROG1, KEY_PROG2, KEY_PROG3, KEY_PROG4, KEY_PC } }, + { 0x54, { KEY_MENU, KEY_MENU, KEY_MENU, KEY_MENU, KEY_MENU } }, + { 0x58, { KEY_UP, KEY_UP, KEY_UP, KEY_UP, KEY_UP } }, + { 0x59, { KEY_DOWN, KEY_DOWN, KEY_DOWN, KEY_DOWN, KEY_DOWN } }, + { 0x5a, { KEY_LEFT, KEY_LEFT, KEY_LEFT, KEY_LEFT, KEY_LEFT } }, + { 0x5b, { KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, KEY_RIGHT } }, + { 0x5c, { KEY_OK, KEY_OK, KEY_OK, KEY_OK, KEY_OK } }, + { 0x78, { KEY_A, KEY_A, KEY_A, KEY_A, KEY_A } }, + { 0x79, { KEY_B, KEY_B, KEY_B, KEY_B, KEY_B } }, + { 0x7a, { KEY_C, KEY_C, KEY_C, KEY_C, KEY_C } }, + { 0x7b, { KEY_D, KEY_D, KEY_D, KEY_D, KEY_D } }, + { 0x7c, { KEY_E, KEY_E, KEY_E, KEY_E, KEY_E } }, + { 0x7d, { KEY_F, KEY_F, KEY_F, KEY_F, KEY_F } }, + { 0x82, { KEY_ENTER, KEY_ENTER, KEY_ENTER, KEY_ENTER, KEY_ENTER } }, + { 0x8e, { KEY_VENDOR, KEY_VENDOR, KEY_VENDOR, KEY_VENDOR, KEY_VENDOR } }, + { 0x96, { KEY_COFFEE, KEY_COFFEE, KEY_COFFEE, KEY_COFFEE, KEY_COFFEE } }, + { 0xa9, { BTN_LEFT, BTN_LEFT, BTN_LEFT, BTN_LEFT, BTN_LEFT } }, + { 0xaa, { BTN_RIGHT, BTN_RIGHT, BTN_RIGHT, BTN_RIGHT, BTN_RIGHT, } }, + { 0xbe, { KEY_QUESTION, KEY_QUESTION, KEY_QUESTION, KEY_QUESTION, KEY_QUESTION, } }, + { 0xd0, { KEY_EDIT, KEY_EDIT, KEY_EDIT, KEY_EDIT, KEY_EDIT } }, + { 0xd5, { KEY_FRONT, KEY_FRONT, KEY_FRONT, KEY_FRONT, KEY_FRONT } }, + { 0xf9, { KEY_INFO, KEY_INFO, KEY_INFO, KEY_INFO, KEY_INFO } } }; struct ati_remote2 { @@ -117,6 +122,8 @@ char name[64]; char phys[64]; + + u32 keycode[ARRAY_SIZE(ati_remote2_keycodes)][ARRAY_SIZE(ati_remote2_modes)]; }; static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id); @@ -159,11 +166,84 @@ usb_kill_urb(ar2->urb[1]); } +static int ati_remote2_lookup(u8 hwcode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ati_remote2_keycodes); i++) + if (ati_remote2_keycodes[i].hwcode == hwcode) + return i; + + return -1; +} + +static int ati_remote2_getkeycode(struct input_dev *idev, + int scancode, int *keycode) +{ + struct ati_remote2 *ar2 = input_get_drvdata(idev); + int index, mode; + + if (((scancode >> 8) & mode_mask) != (scancode >> 8)) + return -EINVAL; + + index = ati_remote2_lookup(scancode & 0xFF); + if (index == -1) + return -EINVAL; + + for (mode = 0; mode < ARRAY_SIZE(ati_remote2_modes); mode++) { + if ((1 << mode) & (scancode >> 8)) { + *keycode = ar2->keycode[index][mode]; + return 0; + } + } + + return -EINVAL; +} + +static int ati_remote2_setkeycode(struct input_dev *idev, + int scancode, int keycode) +{ + struct ati_remote2 *ar2 = input_get_drvdata(idev); + int old_keycode = -1; + int index, mode; + + if (((scancode >> 8) & mode_mask) != (scancode >> 8)) + return -EINVAL; + + index = ati_remote2_lookup(scancode & 0xFF); + if (index == -1) + return -EINVAL; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + for (mode = 0; mode < ARRAY_SIZE(ati_remote2_modes); mode++) { + if ((1 << mode) & (scancode >> 8)) { + old_keycode = ar2->keycode[index][mode]; + ar2->keycode[index][mode] = keycode; + } + } + + set_bit(keycode, idev->keybit); + + for (index = 0; index < ARRAY_SIZE(ati_remote2_keycodes); index++) { + for (mode = 0; mode < ARRAY_SIZE(ati_remote2_modes); mode++) { + if (ar2->keycode[index][mode] == old_keycode) + return 0; + } + } + + clear_bit(old_keycode, idev->keybit); + + return 0; +} + + static void ati_remote2_input_mouse(struct ati_remote2 *ar2) { struct input_dev *idev = ar2->idev; u8 *data = ar2->buf[0]; - int channel, mode; + u8 channel, mode; channel = data[0] >> 4; @@ -187,22 +267,12 @@ input_sync(idev); } -static int ati_remote2_lookup(unsigned int hw_code) -{ - int i; - - for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) - if (ati_remote2_key_table[i].hw_code == hw_code) - return i; - - return -1; -} - static void ati_remote2_input_key(struct ati_remote2 *ar2) { struct input_dev *idev = ar2->idev; u8 *data = ar2->buf[1]; - int channel, mode, hw_code, index; + u8 channel, mode; + int index; channel = data[0] >> 4; @@ -218,12 +288,10 @@ return; } - hw_code = data[2]; - /* - * Mode keys (AUX1-AUX4, PC) all generate the same code byte. - * Use the mode byte to figure out which one was pressed. - */ - if (hw_code == 0x3f) { + if (!((1 << mode) & mode_mask)) + return; + + if (data[2] == 0x3f) { /* * For some incomprehensible reason the mouse pad generates * events which look identical to the events from the last @@ -236,14 +304,9 @@ if (data[1] == 0) ar2->mode = mode; - - hw_code |= mode << 8; } - if (!((1 << mode) & mode_mask)) - return; - - index = ati_remote2_lookup(hw_code); + index = ati_remote2_lookup(data[2]); if (index < 0) { dev_err(&ar2->intf[1]->dev, "Unknown code byte (%02x %02x %02x %02x)\n", @@ -260,8 +323,7 @@ case 2: /* repeat */ /* No repeat for mouse buttons. */ - if (ati_remote2_key_table[index].key_code == BTN_LEFT || - ati_remote2_key_table[index].key_code == BTN_RIGHT) + if (data[2] == 0xa9 || data[2] == 0xaa) return; if (!time_after_eq(jiffies, ar2->jiffies)) @@ -276,7 +338,7 @@ return; } - input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]); + input_event(idev, EV_KEY, ar2->keycode[index][mode], data[1]); input_sync(idev); } @@ -334,10 +396,11 @@ "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r); } + static int ati_remote2_input_init(struct ati_remote2 *ar2) { struct input_dev *idev; - int i, retval; + int index, mode, retval; idev = input_allocate_device(); if (!idev) @@ -347,11 +410,15 @@ input_set_drvdata(idev, ar2); idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_REL); - idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT); + idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) - set_bit(ati_remote2_key_table[i].key_code, idev->keybit); + + for (index = 0; index < ARRAY_SIZE(ati_remote2_keycodes); index++) { + for (mode = 0; mode < ARRAY_SIZE(ati_remote2_modes); mode++) { + ar2->keycode[index][mode] = ati_remote2_keycodes[index].keycode[mode]; + set_bit(ar2->keycode[index][mode], idev->keybit); + } + } idev->rep[REP_DELAY] = 250; idev->rep[REP_PERIOD] = 33; @@ -359,6 +426,9 @@ idev->open = ati_remote2_open; idev->close = ati_remote2_close; + idev->getkeycode = ati_remote2_getkeycode; + idev->setkeycode = ati_remote2_setkeycode; + idev->name = ar2->name; idev->phys = ar2->phys; @@ -532,6 +602,9 @@ else printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n"); + mode_mask &= 0x1F; + channel_mask &= 0xFFFF; + return r; }