From af9e0eacdc072ba28fd139b90de27023d9cb0598 Mon Sep 17 00:00:00 2001
From: Jiri Kosina <jkosina@suse.cz>
Date: Wed, 14 Nov 2007 12:13:26 +0100
Subject: [PATCH] HID: add full support for Genius KB-29E

Genius KB-29E has broken report descriptor, which causes some of the
Consumer usages to appear incorrectly as Button usages. We fix it by
fixing the report descriptor before it is being parsed.

Also a few of the keys violate the HUT standard, so they need a special
handling. They currently fall into "Reserved" range as per HUT 1.12.

Reported-by: Szekeres Istvan <szekeres@iii.hu>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
---
 drivers/hid/hid-input.c         |  6 ++++++
 drivers/hid/usbhid/hid-quirks.c | 16 ++++++++++++++++
 include/linux/hid.h             |  1 +
 3 files changed, 23 insertions(+)

diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index de270b16bde3..0da29cf43713 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -630,6 +630,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
 				case 0x0f6: map_key_clear(KEY_NEXT);		break;
 				case 0x0fa: map_key_clear(KEY_BACK);		break;
 
+				/* reserved in HUT 1.12. Reported on Genius KB29E */
+				case 0x156: map_key_clear(KEY_WORDPROCESSOR);	break;
+				case 0x157: map_key_clear(KEY_SPREADSHEET);	break;
+				case 0x158: map_key_clear(KEY_PRESENTATION);	break;
+				case 0x15c: map_key_clear(KEY_STOP);		break;
+
 				case 0x182: map_key_clear(KEY_BOOKMARKS);	break;
 				case 0x183: map_key_clear(KEY_CONFIG);		break;
 				case 0x184: map_key_clear(KEY_WORDPROCESSOR);	break;
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 836b06a86173..0ab18a401b77 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -301,6 +301,9 @@
 #define USB_VENDOR_ID_MICROSOFT		0x045e
 #define USB_DEVICE_ID_SIDEWINDER_GV	0x003b
 
+#define USB_VENDOR_ID_MONTEREY		0x0566
+#define USB_DEVICE_ID_GENIUS_KB29E	0x3004
+
 #define USB_VENDOR_ID_NCR		0x0404
 #define USB_DEVICE_ID_NCR_FIRST		0x0300
 #define USB_DEVICE_ID_NCR_LAST		0x03ff
@@ -646,6 +649,8 @@ static const struct hid_rdesc_blacklist {
 	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_RDESC_LOGITECH },
 	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_RDESC_LOGITECH },
 
+	{ USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER },
+
 	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_RDESC_MACBOOK_JIS },
 
 	{ USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX },
@@ -973,6 +978,14 @@ static void usbhid_fixup_macbook_descriptor(unsigned char *rdesc, int rsize)
 	}
 }
 
+static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rsize)
+{
+	if (rsize >= 30 && rdesc[29] == 0x05
+			&& rdesc[30] == 0x09) {
+		printk(KERN_INFO "Fixing up button/consumer in HID report descriptor\n");
+		rdesc[30] = 0x0c;
+	}
+}
 
 static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize)
 {
@@ -990,6 +1003,9 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned
 
 	if (quirks & HID_QUIRK_RDESC_MACBOOK_JIS)
 		usbhid_fixup_macbook_descriptor(rdesc, rsize);
+
+	if (quirks & HID_QUIRK_RDESC_BUTTON_CONSUMER)
+		usbhid_fixup_button_consumer_descriptor(rdesc, rsize);
 }
 
 /**
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 991bbcdc1ca7..c67eeb516040 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -292,6 +292,7 @@ struct hid_item {
 #define HID_QUIRK_RDESC_SWAPPED_MIN_MAX		0x00000004
 #define HID_QUIRK_RDESC_PETALYNX		0x00000008
 #define HID_QUIRK_RDESC_MACBOOK_JIS		0x00000010
+#define HID_QUIRK_RDESC_BUTTON_CONSUMER		0x00000020
 
 /*
  * This is the global environment of the parser. This information is