[PATCH] ati_remote2 loadable keymap support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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;
 }
 

[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux