Button and wheel translation for Chic Technology Corp Browser Mouse via USB

This patch implements a USB HID quirk for Chic Technology Corp Browser Mouse
devices. These are sold in at least two guises; I've encountered one, the
Advent 5-button wireless rechargeable optical mouse.

This mouse has the usual inputs common to ExplorerPS/2 mice; additionally,
the wheel is tiltable and there's an extra button which the documenation
calls a "hot-key button".

It identifies itself as
  Vendor=0x5fe ProdID=0011 Rev= 1.01
  Manufacturer=Wireless Mouse
  Product=Wireless Mouse
(but see also http://www.qbik.ch/usb/devices/showdescr.php?id=3244 for a
different device with the same vendor and product IDs).

As far as I can tell, its buttons behave identically whether the mouse is
connected via PS/2 or USB. I've tested with a USB snooper on a Windows box
(with the mouse's driver installed) and it still reported the buttons as I'd
already observed on Linux.

The mouse always reports the extra three buttons as follows:

* Pushing the wheel to the left  => buttons 3 and 4 (middle + side).
* Pushing the wheel to the right => buttons 3 and 5 (middle + extra).
* Pressing the "hot-key" button  => buttons 2 and 4 (right + side).

The quirk handles these by making the wheel pushes look like REL_HWHEEL
events and the hot-key button look like BTN_FORWARD. (X needs to be told that
the mouse has 10 buttons. The hot-key button could be mapped to BTN_TASK,
making it a "12-button" mouse as far as X is concerned.)

REL_HWHEEL events are repeated using an extra timer which is allocated as
part of struct hid_device.

Signed-off-by: Darren Salt <linux@youmustbejoking.demon.co.uk>

--- linux-2.6.16.orig/drivers/usb/input/hid-core.c	2006-04-18 10:58:47.000000000 +0100
+++ linux-2.6.16.11/drivers/usb/input/hid-core.c	2006-04-29 15:38:41.000000000 +0100
@@ -847,6 +847,9 @@
 				hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt, regs);
 	}
 
+	if (hid->claimed & HID_CLAIMED_INPUT)
+		hidinput_hid_quirks(hid, field, regs);
+
 	memcpy(field->value, value, count * sizeof(__s32));
 exit:
 	kfree(value);
@@ -1395,6 +1398,7 @@
 #define USB_DEVICE_ID_NEC_USB_GAME_PAD	0x0301
 
 #define USB_VENDOR_ID_CHIC		0x05fe
+#define USB_DEVICE_ID_CHIC_BROWSER_MOUSE 0x0011
 #define USB_DEVICE_ID_CHIC_GAMEPAD	0x0014
 
 #define USB_VENDOR_ID_GLAB		0x06c2
@@ -1583,6 +1587,7 @@
 	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_POWERMOUSE, HID_QUIRK_2WHEEL_POWERMOUSE },
 	{ USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
 	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
+	{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_BROWSER_MOUSE, HID_QUIRK_CHIC_PSEUDOBUTTONS_HACK },
 
 	{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
--- linux-2.6.16.orig/drivers/usb/input/hid-input.c	2006-04-18 10:58:47.000000000 +0100
+++ linux-2.6.16.11/drivers/usb/input/hid-input.c	2006-04-29 20:29:03.000000000 +0100
@@ -227,6 +227,86 @@
 }
 #endif
 
+struct hid_pseudobutton_mappings_t {
+	__u16 mask, type, button;
+	__s16 value;
+};
+
+/* Button mappings for the Chic browser mouse (USB ID 05FE:0011).
+ * This is sold as (at least):
+ *	* Advent 5-button wireless rechargeable mouse
+ * It has three extra buttons (wheel pushed sideways, task button) which are
+ * signalled as pairs of standard buttons.
+ */
+static const struct hid_pseudobutton_mappings_t chic_browser_mouse_buttons[] = {
+#if 0
+	{ 1 << 1 | 1 << 3, EV_KEY, BTN_TASK, 1 },	/* 2 and 4 (hot-key) */
+	{ 1 << 2 | 1 << 3, EV_KEY, BTN_BACK, 1 },	/* 3 and 4 (wheel left) */
+	{ 1 << 2 | 1 << 4, EV_KEY, BTN_FORWARD, 1 },	/* 3 and 5 (wheel right) */
+#else
+	{ 1 << 1 | 1 << 3, EV_KEY, BTN_FORWARD, 1 },	/* 2 and 4 (hot-key) */
+	{ 1 << 2 | 1 << 3, EV_REL, REL_HWHEEL, 1 },	/* 3 and 4 (wheel left) */
+	{ 1 << 2 | 1 << 4, EV_REL, REL_HWHEEL, -1 },	/* 3 and 5 (wheel right) */
+#endif
+	{}
+};
+
+/* Repeat for e.g. pseudo-wheels.  We use the input device's own repeat values. */
+static void hidinput_repeat_event(unsigned long data)
+{
+	struct input_dev *input = (void *) data;
+	struct hid_device *device = input->private;
+
+	if (!device->repeat.value)
+		return;
+
+	input_event(input, device->repeat.type, device->repeat.button, device->repeat.value);
+	input_sync(input);
+
+	if (input->rep[REP_PERIOD])
+		mod_timer(&device->repeat.timer, jiffies + msecs_to_jiffies(input->rep[REP_PERIOD]));
+}
+
+static void hidinput_register_pseudobuttons (struct hid_input *hidinput,
+					     struct hid_field *field,
+					     struct hid_usage *usage,
+					     unsigned long *bit,
+					     const struct hid_pseudobutton_mappings_t *buttons,
+					     int allow_repeat)
+{
+	int i;
+	int have_wheel = 0;
+	struct hid_device *device = hidinput->input->private;
+
+	for (i = 0; buttons[i].mask; ++i)
+	{
+		if (usage->type != buttons[i].type)
+			continue;
+
+		switch (buttons[i].type)
+		{
+			case EV_KEY:
+				if (usage->code == BTN_EXTRA)
+					set_bit(buttons[i].button, bit);
+				break;
+			case EV_REL:
+				if (usage->code == REL_WHEEL) {
+					have_wheel = allow_repeat;
+					set_bit(buttons[i].button, bit);
+				}
+				break;
+		}
+	}
+
+	/* If the quirk has introduced an extra wheel (for example),
+	 * we need to handle our own repeats. */
+	if (have_wheel && !device->repeat.timer.function) {
+		init_timer(&device->repeat.timer);
+		device->repeat.timer.data = (long) hidinput->input;
+		device->repeat.timer.function = hidinput_repeat_event;
+	}
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -578,6 +658,9 @@
 	if (((device->quirks & (HID_QUIRK_2WHEEL_POWERMOUSE)) && (usage->hid == 0x00010032)))
 		map_rel(REL_HWHEEL);
 
+	if (device->quirks & HID_QUIRK_CHIC_PSEUDOBUTTONS_HACK)
+		hidinput_register_pseudobuttons (hidinput, field, usage, bit, chic_browser_mouse_buttons, 1);
+
 	if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5)) &&
 		 (usage->type == EV_REL) && (usage->code == REL_WHEEL))
 			set_bit(REL_HWHEEL, bit);
@@ -647,6 +730,12 @@
 		return;
 	}
 
+	if (!(hid->quirks & HID_QUIRK_CHIC_PSEUDOBUTTONS_HACK) && 
+	    (hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) {
+		input_event(input, usage->type, REL_HWHEEL, value);
+		return;
+	}
+
 	if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) {
 		input_event(input, usage->type, REL_HWHEEL, value);
 		return;
@@ -697,9 +786,20 @@
 		return;
 	}
 
+	/* For quirks where one physical button looks like (at least) two other physical buttons */
+	if ((usage->type == EV_KEY) && (usage->hid > 0x00090000) && (usage->hid <= 0x00090010)) {
+		if (value)
+			hid->btn_state |= 1 << (usage->hid - 0x00090001);
+		else
+			hid->btn_state &= ~(1 << (usage->hid - 0x00090001));
+	}
+
 	if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
 		return;
 
+	if ((usage->type == EV_KEY) && (hid->quirks & HID_QUIRK_CHIC_PSEUDOBUTTONS_HACK)) /* some buttons may have quirks - *don't* generate events just yet */
+		return;
+
 	input_event(input, usage->type, usage->code, value);
 
 	if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
@@ -714,6 +814,60 @@
 		input_sync(hidinput->input);
 }
 
+static void hidinput_hid_quirk_process_pseudobuttons (struct hid_device *hid, struct input_dev *input,
+						      const struct hid_pseudobutton_mappings_t *mapping)
+{
+	__u16 buttons = hid->btn_state;
+	__u16 previous = hid->btn_prev_state;
+	/*input_regs(input, regs);*/
+	int i;
+
+	hid->btn_prev_state = hid->btn_state;
+
+	for (i = 0; mapping[i].mask; ++i) {
+		/* both clear -> both set => button is now pressed */
+		if (!(hid->btn_quirks & (1 << i))
+		    && ((previous & mapping[i].mask) == 0)
+		    && ((buttons & mapping[i].mask) == mapping[i].mask)) {
+			buttons &= ~mapping[i].mask;
+			hid->btn_quirks |= 1 << i;
+			input_event (input, mapping[i].type, mapping[i].button, mapping[i].value);
+			/* If it's something like a pseudowheel, start repeating it. */
+			if (((mapping[i].button == REL_WHEEL) || (mapping[i].button == REL_HWHEEL)) &&
+			    hid->repeat.timer.function && input->rep[REP_PERIOD] && input->rep[REP_DELAY]) {
+				hid->repeat.type = mapping[i].type;
+				hid->repeat.button = mapping[i].button;
+				hid->repeat.value = mapping[i].value;
+				mod_timer(&hid->repeat.timer, jiffies + msecs_to_jiffies(input->rep[REP_DELAY]));
+			}
+		}
+		/* both set -> either clear => button is now released */
+		else
+		if ((hid->btn_quirks & (1 << i))
+		    && ((previous & mapping[i].mask) == mapping[i].mask)
+		    && ((buttons & mapping[i].mask) != mapping[i].mask)) {
+			buttons &= ~mapping[i].mask;
+			hid->btn_quirks &= ~(1 << i);
+			input_event (input, mapping[i].type, mapping[i].button, 0);
+			if ((mapping[i].button == REL_WHEEL) || (mapping[i].button == REL_HWHEEL))
+				hid->repeat.value = 0;
+		}
+	}
+
+	for (i = 0; i < 16; ++i)
+		if ((buttons ^ previous) & (1 << i))
+			input_event (input, EV_KEY, BTN_MOUSE + i, (buttons >> i) & 1);
+}
+
+void hidinput_hid_quirks(struct hid_device *hid, struct hid_field *field, struct pt_regs *regs)
+{
+	if (!field->hidinput)
+		return;
+	/* Chic browser mouse (sold as, at least, Advent 5-button) */
+	if (hid->quirks & HID_QUIRK_CHIC_PSEUDOBUTTONS_HACK)
+		hidinput_hid_quirk_process_pseudobuttons (hid, field->hidinput->input, chic_browser_mouse_buttons);
+}
+
 static int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field)
 {
 	struct hid_report *report;
@@ -846,6 +1000,10 @@
 		input_register_device(hidinput->input);
 	}
 
+	hid->btn_state = 0;
+	hid->btn_prev_state = 0;
+	hid->btn_quirks = 0;
+
 	return 0;
 }
 
@@ -854,6 +1012,11 @@
 	struct hid_input *hidinput, *next;
 
 	list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
+		/* Stop any repeat timer which may have been registered. */
+		struct hid_device *device = hidinput->input->private;
+		if (device->repeat.timer.function)
+			del_timer_sync(&device->repeat.timer);
+		/* Unregister the device and clean up. */
 		list_del(&hidinput->list);
 		input_unregister_device(hidinput->input);
 		kfree(hidinput);
--- linux-2.6.16.orig/drivers/usb/input/hid.h	2006-04-18 10:58:47.000000000 +0100
+++ linux-2.6.16.11/drivers/usb/input/hid.h	2006-04-29 16:00:16.000000000 +0100
@@ -249,6 +249,7 @@
 #define HID_QUIRK_CYMOTION			0x00000800
 #define HID_QUIRK_POWERBOOK_HAS_FN		0x00001000
 #define HID_QUIRK_POWERBOOK_FN_ON		0x00002000
+#define HID_QUIRK_CHIC_PSEUDOBUTTONS_HACK	0x00004000
 
 /*
  * This is the global environment of the parser. This information is
@@ -439,6 +440,16 @@
 	unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
 	unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
 #endif
+
+	/* For quirks where one physical button looks like (at least) two other physical buttons */
+	__u16 btn_state, btn_prev_state;				/* Button map data */
+	__u32 btn_quirks;						/* Which of the extra buttons are active */
+
+	struct {
+		__u16 type, button;
+		__s16 value;
+		struct timer_list timer;
+	} repeat;
 };
 
 #define HID_GLOBAL_STACK_SIZE 4
@@ -487,6 +498,7 @@
 #define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001))
 extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32, struct pt_regs *regs);
 extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report);
+extern void hidinput_hid_quirks(struct hid_device *, struct hid_field *, struct pt_regs *);
 extern int hidinput_connect(struct hid_device *);
 extern void hidinput_disconnect(struct hid_device *);
 #else
