]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
HID: Add driver for ION iCade
authorBastien Nocera <hadess@hadess.net>
Wed, 31 Oct 2012 11:10:10 +0000 (12:10 +0100)
committerJiri Kosina <jkosina@suse.cz>
Wed, 31 Oct 2012 14:52:59 +0000 (15:52 +0100)
Add a driver for the ION iCade mini arcade cabinet [1]. The device generates a
key press and release for each joystick movement or button press or release.
For example, moving the stick to the left will generate the "A" key being
pressed and then released.

A list of all the combinations is available in the iCade developer guide [2].

This driver hides all this and makes the device work as a generic joystick.

[1]: http://www.ionaudio.com/products/details/icade
[2]: http://www.ionaudio.com/downloads/iCade_Dev_Resource_v1.3.pdf

Signed-off-by: Bastien Nocera <hadess@hadess.net>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-icade.c [new file with mode: 0644]
drivers/hid/hid-ids.h

index 1630150ad2b19e1cf362881ff91e850c044e72d5..813f0fe7711dcd1831fcd9e9774eca2e77ba6d72 100644 (file)
@@ -265,6 +265,15 @@ config HID_GYRATION
        ---help---
        Support for Gyration remote control.
 
+config HID_ICADE
+       tristate "ION iCade arcade controller"
+       depends on (BT_HIDP)
+       ---help---
+       Support for the ION iCade arcade controller to work as a joystick.
+
+       To compile this driver as a module, choose M here: the
+       module will be called hid-icade.
+
 config HID_TWINHAN
        tristate "Twinhan IR remote control"
        depends on USB_HID
index cef68ca859d335e7a58853a7fd55dc898b9c825e..85c910c6d9d265dc8af69af6fc2acaa9d0bcdd8b 100644 (file)
@@ -52,6 +52,7 @@ obj-$(CONFIG_HID_GYRATION)    += hid-gyration.o
 obj-$(CONFIG_HID_HOLTEK)       += hid-holtek-kbd.o
 obj-$(CONFIG_HID_HOLTEK)       += hid-holtekff.o
 obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o
+obj-$(CONFIG_HID_ICADE)                += hid-icade.o
 obj-$(CONFIG_HID_KENSINGTON)   += hid-kensington.o
 obj-$(CONFIG_HID_KEYTOUCH)     += hid-keytouch.o
 obj-$(CONFIG_HID_KYE)          += hid-kye.o
index bd3971bf31bf7bc4369911a648b02c8729a3004d..0a6b36fbb4ce9bd87bd2cc1d59c3500733a4e16f 100644 (file)
@@ -1572,6 +1572,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_09FA) },
        { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_1020) },
        { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_09FA) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },
        { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
diff --git a/drivers/hid/hid-icade.c b/drivers/hid/hid-icade.c
new file mode 100644 (file)
index 0000000..1d6565e
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ *  ION iCade input driver
+ *
+ *  Copyright (c) 2012 Bastien Nocera <hadess@hadess.net>
+ *  Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ *   ↑      A C Y L
+ *  ← →
+ *   ↓      B X Z R
+ *
+ *
+ *  UP ON,OFF  = w,e
+ *  RT ON,OFF  = d,c
+ *  DN ON,OFF  = x,z
+ *  LT ON,OFF  = a,q
+ *  A  ON,OFF  = y,t
+ *  B  ON,OFF  = h,r
+ *  C  ON,OFF  = u,f
+ *  X  ON,OFF  = j,n
+ *  Y  ON,OFF  = i,m
+ *  Z  ON,OFF  = k,p
+ *  L  ON,OFF  = o,g
+ *  R  ON,OFF  = l,v
+ */
+
+/* The translation code uses HID usage instead of input layer
+ * keys. This code generates a lookup table that makes
+ * translation quick.
+ *
+ * #include <linux/input.h>
+ * #include <stdio.h>
+ * #include <assert.h>
+ *
+ * #define unk     KEY_UNKNOWN
+ *
+ * < copy of hid_keyboard[] from hid-input.c >
+ *
+ * struct icade_key_translation {
+ *     int         from;
+ *     const char *to;
+ *     int         press;
+ * };
+ *
+ * static const struct icade_key_translation icade_keys[] = {
+ *    { KEY_W,        "KEY_UP",         1 },
+ *    { KEY_E,        "KEY_UP",         0 },
+ *    { KEY_D,        "KEY_RIGHT",      1 },
+ *    { KEY_C,        "KEY_RIGHT",      0 },
+ *    { KEY_X,        "KEY_DOWN",       1 },
+ *    { KEY_Z,        "KEY_DOWN",       0 },
+ *    { KEY_A,        "KEY_LEFT",       1 },
+ *    { KEY_Q,        "KEY_LEFT",       0 },
+ *    { KEY_Y,        "BTN_A",          1 },
+ *    { KEY_T,        "BTN_A",          0 },
+ *    { KEY_H,        "BTN_B",          1 },
+ *    { KEY_R,        "BTN_B",          0 },
+ *    { KEY_U,        "BTN_C",          1 },
+ *    { KEY_F,        "BTN_C",          0 },
+ *    { KEY_J,        "BTN_X",          1 },
+ *    { KEY_N,        "BTN_X",          0 },
+ *    { KEY_I,        "BTN_Y",          1 },
+ *    { KEY_M,        "BTN_Y",          0 },
+ *    { KEY_K,        "BTN_Z",          1 },
+ *    { KEY_P,        "BTN_Z",          0 },
+ *    { KEY_O,        "BTN_THUMBL",     1 },
+ *    { KEY_G,        "BTN_THUMBL",     0 },
+ *    { KEY_L,        "BTN_THUMBR",     1 },
+ *    { KEY_V,        "BTN_THUMBR",     0 },
+ *
+ *    { }
+ * };
+ *
+ * static int
+ * usage_for_key (int key)
+ * {
+ *     int i;
+ *     for (i = 0; i < 256; i++) {
+ *     if (hid_keyboard[i] == key)
+ *         return i;
+ *     }
+ *     assert(0);
+ * }
+ *
+ * int main (int argc, char **argv)
+ * {
+ *     const struct icade_key_translation *trans;
+ *     int max_usage = 0;
+ *
+ *     for (trans = icade_keys; trans->from; trans++) {
+ *         int usage = usage_for_key (trans->from);
+ *         max_usage = usage > max_usage ? usage : max_usage;
+ *     }
+ *
+ *     printf ("#define ICADE_MAX_USAGE %d\n\n", max_usage);
+ *     printf ("struct icade_key {\n");
+ *     printf ("\tu16 to;\n");
+ *     printf ("\tu8 press:1;\n");
+ *     printf ("};\n\n");
+ *     printf ("static const struct icade_key "
+ *             "icade_usage_table[%d] = {\n", max_usage + 1);
+ *     for (trans = icade_keys; trans->from; trans++) {
+ *         printf ("\t[%d] = { %s, %d },\n",
+ *                 usage_for_key (trans->from), trans->to, trans->press);
+ *     }
+ *     printf ("};\n");
+ *
+ *     return 0;
+ * }
+ */
+
+#define ICADE_MAX_USAGE 29
+
+struct icade_key {
+       u16 to;
+       u8 press:1;
+};
+
+static const struct icade_key icade_usage_table[30] = {
+       [26] = { KEY_UP, 1 },
+       [8] = { KEY_UP, 0 },
+       [7] = { KEY_RIGHT, 1 },
+       [6] = { KEY_RIGHT, 0 },
+       [27] = { KEY_DOWN, 1 },
+       [29] = { KEY_DOWN, 0 },
+       [4] = { KEY_LEFT, 1 },
+       [20] = { KEY_LEFT, 0 },
+       [28] = { BTN_A, 1 },
+       [23] = { BTN_A, 0 },
+       [11] = { BTN_B, 1 },
+       [21] = { BTN_B, 0 },
+       [24] = { BTN_C, 1 },
+       [9] = { BTN_C, 0 },
+       [13] = { BTN_X, 1 },
+       [17] = { BTN_X, 0 },
+       [12] = { BTN_Y, 1 },
+       [16] = { BTN_Y, 0 },
+       [14] = { BTN_Z, 1 },
+       [19] = { BTN_Z, 0 },
+       [18] = { BTN_THUMBL, 1 },
+       [10] = { BTN_THUMBL, 0 },
+       [15] = { BTN_THUMBR, 1 },
+       [25] = { BTN_THUMBR, 0 },
+};
+
+static const struct icade_key *icade_find_translation(u16 from)
+{
+       if (from < 0 || from > ICADE_MAX_USAGE)
+               return NULL;
+       return &icade_usage_table[from];
+}
+
+static int icade_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       const struct icade_key *trans;
+
+       if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+                       !usage->type)
+               return 0;
+
+       /* We ignore the fake key up, and act only on key down */
+       if (!value)
+               return 1;
+
+       trans = icade_find_translation(usage->hid & HID_USAGE);
+
+       if (!trans)
+               return 1;
+
+       input_event(field->hidinput->input, usage->type,
+                       trans->to, trans->press);
+
+       return 1;
+}
+
+static int icade_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       const struct icade_key *trans;
+
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD) {
+               trans = icade_find_translation(usage->hid & HID_USAGE);
+
+               if (!trans)
+                       return -1;
+
+               hid_map_usage(hi, usage, bit, max, EV_KEY, trans->to);
+               set_bit(trans->to, hi->input->keybit);
+
+               return 1;
+       }
+
+       /* ignore others */
+       return -1;
+
+}
+
+static int icade_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if (usage->type == EV_KEY)
+               set_bit(usage->type, hi->input->evbit);
+
+       return -1;
+}
+
+static const struct hid_device_id icade_devices[] = {
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) },
+
+       { }
+};
+MODULE_DEVICE_TABLE(hid, icade_devices);
+
+static struct hid_driver icade_driver = {
+       .name = "icade",
+       .id_table = icade_devices,
+       .event = icade_event,
+       .input_mapped = icade_input_mapped,
+       .input_mapping = icade_input_mapping,
+};
+
+static int __init icade_init(void)
+{
+       int ret;
+
+       ret = hid_register_driver(&icade_driver);
+       if (ret)
+               pr_err("can't register icade driver\n");
+
+       return ret;
+}
+
+static void __exit icade_exit(void)
+{
+       hid_unregister_driver(&icade_driver);
+}
+
+module_init(icade_init);
+module_exit(icade_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
+MODULE_DESCRIPTION("ION iCade input driver");
index 269b50912a4ae9207d1d04f2e014b3c950f20324..9bc8d570777a1605879781f7ed77420c46ded922 100644 (file)
 #define USB_VENDOR_ID_ILITEK           0x222a
 #define USB_DEVICE_ID_ILITEK_MULTITOUCH        0x0001
 
+#define USB_VENDOR_ID_ION              0x15e4
+#define USB_DEVICE_ID_ICADE            0x0132
+
 #define USB_VENDOR_ID_HOLTEK           0x1241
 #define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP      0x5015