Hi Bastien, this v2 is good for me: Reviewed-by: Benjamin Tissoires <benjamin.tissoires@xxxxxxxxx> Cheers, Benjamin On Wed, Oct 31, 2012 at 12:10 PM, Bastien Nocera <hadess@xxxxxxxxxx> wrote: > > 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 the 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@xxxxxxxxxx> > --- > drivers/hid/Kconfig | 9 ++ > drivers/hid/Makefile | 1 + > drivers/hid/hid-core.c | 1 + > drivers/hid/hid-icade.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++++ > drivers/hid/hid-ids.h | 3 + > 5 files changed, 273 insertions(+) > create mode 100644 drivers/hid/hid-icade.c > > diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig > index 1630150..813f0fe 100644 > --- a/drivers/hid/Kconfig > +++ b/drivers/hid/Kconfig > @@ -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 > diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile > index cef68ca..85c910c 100644 > --- a/drivers/hid/Makefile > +++ b/drivers/hid/Makefile > @@ -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 > diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c > index bd3971b..0a6b36f 100644 > --- a/drivers/hid/hid-core.c > +++ b/drivers/hid/hid-core.c > @@ -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 > index 0000000..1d6565e > --- /dev/null > +++ b/drivers/hid/hid-icade.c > @@ -0,0 +1,259 @@ > +/* > + * ION iCade input driver > + * > + * Copyright (c) 2012 Bastien Nocera <hadess@xxxxxxxxxx> > + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@xxxxxxxxx> > + */ > + > +/* > + * 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@xxxxxxxxxx>"); > +MODULE_DESCRIPTION("ION iCade input driver"); > diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h > index 269b509..9bc8d57 100644 > --- a/drivers/hid/hid-ids.h > +++ b/drivers/hid/hid-ids.h > @@ -420,6 +420,9 @@ > #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 > > -- > 1.7.12.1 > > -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html