This patch will let hid-microsoft handle the Microsoft Sidewinder X4 and X6 
keyboards.

Signed-off-by: Tolga Cakir <to...@cevel.net>
---
 drivers/hid/hid-core.c      |   2 +
 drivers/hid/hid-ids.h       |   2 +
 drivers/hid/hid-microsoft.c | 114 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 118 insertions(+)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index dbe548b..5de5ba1 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1815,6 +1815,8 @@ static const struct hid_device_id 
hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, 
USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, 
USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) 
},
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_X6) 
},
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_X4) 
},
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 548c1a5..21be65d 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -626,6 +626,8 @@
 #define USB_DEVICE_ID_MS_PRESENTER_8K_BT       0x0701
 #define USB_DEVICE_ID_MS_PRESENTER_8K_USB      0x0713
 #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K      0x0730
+#define USB_DEVICE_ID_SIDEWINDER_X6    0x074b
+#define USB_DEVICE_ID_SIDEWINDER_X4    0x0768
 #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500    0x076c
 #define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7
 #define USB_DEVICE_ID_MS_TYPE_COVER_2  0x07a9
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 0a61403..5b5d40f 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -29,12 +29,28 @@
 #define MS_NOGET               0x10
 #define MS_DUPLICATE_USAGES    0x20
 #define MS_RDESC_3K            0x40
+#define MS_SIDEWINDER  0x80
 
 struct ms_data {
        unsigned long quirks;
        void *extra;
 };
 
+/*
+ * For Sidewinder X4 / X6 devices.
+ * @profile: for storing profile status.
+ * @status: holds information about LED states and numpad mode (X6
+ * only). The 1st bit is for numpad mode, bits 2 - 7 are reserved for
+ * LED configuration and the last bit is currently unused.
+ * @key_mask: holds information about pressed special keys. It's
+ * readable via sysfs, so user-space tools can handle keys.
+ */
+struct ms_sidewinder_extra {
+       unsigned profile;
+       __u8 status;
+       unsigned long key_mask;
+};
+
 static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                unsigned int *rsize)
 {
@@ -143,6 +159,56 @@ static int ms_presenter_8k_quirk(struct hid_input *hi, 
struct hid_usage *usage,
        return 1;
 }
 
+static int ms_sidewinder_kb_quirk(struct hid_input *hi, struct hid_usage 
*usage,
+               unsigned long **bit, int *max)
+{
+       set_bit(EV_REP, hi->input->evbit);
+       switch (usage->hid & HID_USAGE) {
+       /*
+        * Registering Sidewinder X4 / X6 special keys. S1 - S6 macro keys
+        * are shared between Sidewinder X4 & X6 and are programmable.
+        */
+       case 0xfb01: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S1 */
+       case 0xfb02: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S2 */
+       case 0xfb03: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S3 */
+       case 0xfb04: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S4 */
+       case 0xfb05: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S5 */
+       case 0xfb06: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S6 */
+       /* S7 - S30 macro keys are only present on the Sidewinder X6 */
+       case 0xfb07: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S7 */
+       case 0xfb08: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S8 */
+       case 0xfb09: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S9 */
+       case 0xfb0a: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S10 */
+       case 0xfb0b: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S11 */
+       case 0xfb0c: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S12 */
+       case 0xfb0d: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S13 */
+       case 0xfb0e: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S14 */
+       case 0xfb0f: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S15 */
+       case 0xfb10: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S16 */
+       case 0xfb11: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S17 */
+       case 0xfb12: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S18 */
+       case 0xfb13: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S19 */
+       case 0xfb14: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S20 */
+       case 0xfb15: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S21 */
+       case 0xfb16: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S22 */
+       case 0xfb17: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S23 */
+       case 0xfb18: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S24 */
+       case 0xfb19: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S25 */
+       case 0xfb1a: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S26 */
+       case 0xfb1b: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S27 */
+       case 0xfb1c: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S28 */
+       case 0xfb1d: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S29 */
+       case 0xfb1e: ms_map_key_clear(KEY_UNKNOWN);     break;  /* S30 */
+       /* Not programmable keys: Profile, Game Center (X6 only) and Macro */
+       case 0xfd11: ms_map_key_clear(KEY_GAMES);       break;  /* X6: Game 
Center*/
+       case 0xfd12: ms_map_key_clear(KEY_MACRO);       break;  /* Macro */
+       case 0xfd15: ms_map_key_clear(KEY_UNKNOWN);     break;  /* Profile */
+       default:
+               return 0;
+       }
+       return 1;
+}
+
 static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                struct hid_field *field, struct hid_usage *usage,
                unsigned long **bit, int *max)
@@ -159,6 +225,10 @@ static int ms_input_mapping(struct hid_device *hdev, 
struct hid_input *hi,
                        ms_presenter_8k_quirk(hi, usage, bit, max))
                return 1;
 
+       if ((sc->quirks & MS_SIDEWINDER) &&
+                       ms_sidewinder_kb_quirk(hi, usage, bit, max))
+               return 1;
+
        return 0;
 }
 
@@ -229,6 +299,34 @@ static int ms_event(struct hid_device *hdev, struct 
hid_field *field,
                return 1;
        }
 
+       /*
+        * Sidewinder special button handling & profile switching
+        *
+        * Pressing S1 - S30 macro keys will not send out any keycodes, but
+        * set bits on key_mask (readable via sysfs). It's possible to press
+        * multiple special keys at the same time.
+        */
+       if (sc->quirks & MS_SIDEWINDER) {
+               struct input_dev *input = field->hidinput->input;
+               struct ms_sidewinder_extra *sidewinder = sc->extra;
+               int i;
+
+               for (i = 0; i <= 29; i++) {     /* Run through S1 - S30 keys */
+                       if ((usage->hid & HID_USAGE) == (0xfb01 + i)) {
+                               value ? set_bit(i, &sidewinder->key_mask) : 
clear_bit(i, &sidewinder->key_mask);
+                               break;  /* Exit loop, when correct hid usage 
has been found */
+                       }
+               }
+
+               switch (usage->hid & HID_USAGE) {
+               case 0xfd11: input_event(input, usage->type, KEY_GAMES, value); 
break;
+               case 0xfd12: input_event(input, usage->type, KEY_MACRO, value); 
break;
+               case 0xfd15: value ? set_bit(30, &sidewinder->key_mask) : 
clear_bit(30, &sidewinder->key_mask); break;
+               }
+
+               return 1;
+       }
+
        return 0;
 }
 
@@ -249,6 +347,18 @@ static int ms_probe(struct hid_device *hdev, const struct 
hid_device_id *id)
        if (sc->quirks & MS_NOGET)
                hdev->quirks |= HID_QUIRK_NOGET;
 
+       if (sc->quirks & MS_SIDEWINDER) {
+               struct ms_sidewinder_extra *sidewinder;
+
+               sidewinder = devm_kzalloc(&hdev->dev, sizeof(struct 
ms_sidewinder_extra),
+                                       GFP_KERNEL);
+               if (!sidewinder) {
+                       hid_err(hdev, "can't alloc microsoft descriptor\n");
+                       return -ENOMEM;
+               }
+               sc->extra = sidewinder;
+       }
+
        ret = hid_parse(hdev);
        if (ret) {
                hid_err(hdev, "parse failed\n");
@@ -270,6 +380,10 @@ err_free:
 static const struct hid_device_id ms_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),
                .driver_data = MS_HIDINPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_X6),
+               .driver_data = MS_SIDEWINDER },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_X4),
+               .driver_data = MS_SIDEWINDER },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB),
                .driver_data = MS_ERGONOMY },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K),
-- 
1.9.1

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

Reply via email to