diff options
71 files changed, 1405 insertions, 824 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-srws1 b/Documentation/ABI/testing/sysfs-driver-hid-srws1 new file mode 100644 index 000000000000..d0eba70c7d40 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-srws1 @@ -0,0 +1,21 @@ +What: /sys/class/leds/SRWS1::<serial>::RPM1 +What: /sys/class/leds/SRWS1::<serial>::RPM2 +What: /sys/class/leds/SRWS1::<serial>::RPM3 +What: /sys/class/leds/SRWS1::<serial>::RPM4 +What: /sys/class/leds/SRWS1::<serial>::RPM5 +What: /sys/class/leds/SRWS1::<serial>::RPM6 +What: /sys/class/leds/SRWS1::<serial>::RPM7 +What: /sys/class/leds/SRWS1::<serial>::RPM8 +What: /sys/class/leds/SRWS1::<serial>::RPM9 +What: /sys/class/leds/SRWS1::<serial>::RPM10 +What: /sys/class/leds/SRWS1::<serial>::RPM11 +What: /sys/class/leds/SRWS1::<serial>::RPM12 +What: /sys/class/leds/SRWS1::<serial>::RPM13 +What: /sys/class/leds/SRWS1::<serial>::RPM14 +What: /sys/class/leds/SRWS1::<serial>::RPM15 +What: /sys/class/leds/SRWS1::<serial>::RPMALL +Date: Jan 2013 +KernelVersion: 3.9 +Contact: Simon Wood <simon@mungewell.org> +Description: Provides a control for turning on/off the LEDs which form + an RPM meter on the front of the controller diff --git a/Documentation/ABI/testing/sysfs-driver-hid-thingm b/Documentation/ABI/testing/sysfs-driver-hid-thingm new file mode 100644 index 000000000000..abcffeedd20a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-thingm @@ -0,0 +1,23 @@ +What: /sys/class/leds/blink1::<serial>/rgb +Date: January 2013 +Contact: Vivien Didelot <vivien.didelot@savoirfairelinux.com> +Description: The ThingM blink1 is an USB RGB LED. The color notation is + 3-byte hexadecimal. Read this attribute to get the last set + color. Write the 24-bit hexadecimal color to change the current + LED color. The default color is full white (0xFFFFFF). + For instance, set the color to green with: echo 00FF00 > rgb + +What: /sys/class/leds/blink1::<serial>/fade +Date: January 2013 +Contact: Vivien Didelot <vivien.didelot@savoirfairelinux.com> +Description: This attribute allows to set a fade time in milliseconds for + the next color change. Read the attribute to know the current + fade time. The default value is set to 0 (no fade time). For + instance, set a fade time of 2 seconds with: echo 2000 > fade + +What: /sys/class/leds/blink1::<serial>/play +Date: January 2013 +Contact: Vivien Didelot <vivien.didelot@savoirfairelinux.com> +Description: This attribute is used to play/pause the light patterns. Write 1 + to start playing, 0 to stop. Reading this attribute returns the + current playing status. diff --git a/MAINTAINERS b/MAINTAINERS index 81e4ad85dd5c..db061e961d37 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7611,6 +7611,11 @@ S: Supported F: drivers/thermal/ F: include/linux/thermal.h +THINGM BLINK(1) USB RGB LED DRIVER +M: Vivien Didelot <vivien.didelot@savoirfairelinux.com> +S: Maintained +F: drivers/hid/hid-thingm.c + THINKPAD ACPI EXTRAS DRIVER M: Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br> L: ibm-acpi-devel@lists.sourceforge.net diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index eae0c7e468cf..5f07d85c4189 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -596,6 +596,12 @@ config HID_SPEEDLINK ---help--- Support for Speedlink Vicious and Divine Cezanne mouse. +config HID_STEELSERIES + tristate "Steelseries SRW-S1 steering wheel support" + depends on USB_HID + ---help--- + Support for Steelseries SRW-S1 steering wheel + config HID_SUNPLUS tristate "Sunplus wireless desktop" depends on USB_HID @@ -655,6 +661,16 @@ config HID_TOPSEED Say Y if you have a TopSeed Cyberlink or BTC Emprex or Conceptronic CLLRCMCE remote control. +config HID_THINGM + tristate "ThingM blink(1) USB RGB LED" + depends on USB_HID + depends on LEDS_CLASS + ---help--- + Support for the ThingM blink(1) USB RGB LED. This driver registers a + Linux LED class instance, plus additional sysfs attributes to control + RGB colors, fade time and playing. The device is exposed through hidraw + to access other functions. + config HID_THRUSTMASTER tristate "ThrustMaster devices support" depends on USB_HID @@ -719,7 +735,7 @@ config HID_ZYDACRON config HID_SENSOR_HUB tristate "HID Sensors framework support" - depends on USB_HID + depends on USB_HID && GENERIC_HARDIRQS select MFD_CORE default n -- help--- diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index b62215716b2f..72d1b0bc0a97 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -101,8 +101,10 @@ obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o obj-$(CONFIG_HID_SONY) += hid-sony.o obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o +obj-$(CONFIG_HID_STEELSERIES) += hid-steelseries.o obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o +obj-$(CONFIG_HID_THINGM) += hid-thingm.o obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o obj-$(CONFIG_HID_TIVO) += hid-tivo.o obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index 0a239885e67c..7c5507e94820 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c @@ -146,17 +146,6 @@ static struct hid_driver a4_driver = { .probe = a4_probe, .remove = a4_remove, }; +module_hid_driver(a4_driver); -static int __init a4_init(void) -{ - return hid_register_driver(&a4_driver); -} - -static void __exit a4_exit(void) -{ - hid_unregister_driver(&a4_driver); -} - -module_init(a4_init); -module_exit(a4_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index d0f7662aacca..320a958d4139 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -555,23 +555,6 @@ static struct hid_driver apple_driver = { .input_mapping = apple_input_mapping, .input_mapped = apple_input_mapped, }; +module_hid_driver(apple_driver); -static int __init apple_init(void) -{ - int ret; - - ret = hid_register_driver(&apple_driver); - if (ret) - pr_err("can't register apple driver\n"); - - return ret; -} - -static void __exit apple_exit(void) -{ - hid_unregister_driver(&apple_driver); -} - -module_init(apple_init); -module_exit(apple_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-aureal.c b/drivers/hid/hid-aureal.c index 7968187ddf7b..340ba9d394a0 100644 --- a/drivers/hid/hid-aureal.c +++ b/drivers/hid/hid-aureal.c @@ -37,17 +37,6 @@ static struct hid_driver aureal_driver = { .id_table = aureal_devices, .report_fixup = aureal_report_fixup, }; +module_hid_driver(aureal_driver); -static int __init aureal_init(void) -{ - return hid_register_driver(&aureal_driver); -} - -static void __exit aureal_exit(void) -{ - hid_unregister_driver(&aureal_driver); -} - -module_init(aureal_init); -module_exit(aureal_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c index 5be858dd9a15..62f0cee032ba 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -192,19 +192,7 @@ static struct hid_driver ax_driver = { .probe = ax_probe, .remove = ax_remove, }; - -static int __init ax_init(void) -{ - return hid_register_driver(&ax_driver); -} - -static void __exit ax_exit(void) -{ - hid_unregister_driver(&ax_driver); -} - -module_init(ax_init); -module_exit(ax_exit); +module_hid_driver(ax_driver); MODULE_AUTHOR("Sergei Kolzun"); MODULE_DESCRIPTION("Force feedback support for ACRUX game controllers"); diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c index a1a5a12c3a6b..cc4cf138bef5 100644 --- a/drivers/hid/hid-belkin.c +++ b/drivers/hid/hid-belkin.c @@ -86,17 +86,6 @@ static struct hid_driver belkin_driver = { .input_mapping = belkin_input_mapping, .probe = belkin_probe, }; +module_hid_driver(belkin_driver); -static int __init belkin_init(void) -{ - return hid_register_driver(&belkin_driver); -} - -static void __exit belkin_exit(void) -{ - hid_unregister_driver(&belkin_driver); -} - -module_init(belkin_init); -module_exit(belkin_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c index af034d3d9256..1bdcccc54a1d 100644 --- a/drivers/hid/hid-cherry.c +++ b/drivers/hid/hid-cherry.c @@ -69,17 +69,6 @@ static struct hid_driver ch_driver = { .report_fixup = ch_report_fixup, .input_mapping = ch_input_mapping, }; +module_hid_driver(ch_driver); -static int __init ch_init(void) -{ - return hid_register_driver(&ch_driver); -} - -static void __exit ch_exit(void) -{ - hid_unregister_driver(&ch_driver); -} - -module_init(ch_init); -module_exit(ch_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c index a2abb8e15727..b613d5a79684 100644 --- a/drivers/hid/hid-chicony.c +++ b/drivers/hid/hid-chicony.c @@ -70,17 +70,6 @@ static struct hid_driver ch_driver = { .id_table = ch_devices, .input_mapping = ch_input_mapping, }; +module_hid_driver(ch_driver); -static int __init ch_init(void) -{ - return hid_register_driver(&ch_driver); -} - -static void __exit ch_exit(void) -{ - hid_unregister_driver(&ch_driver); -} - -module_init(ch_init); -module_exit(ch_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index eb2ee11b6412..ff75cabf7393 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -729,7 +729,7 @@ static int hid_scan_report(struct hid_device *hid) item.type == HID_ITEM_TYPE_MAIN && item.tag == HID_MAIN_ITEM_TAG_BEGIN_COLLECTION && (item_udata(&item) & 0xff) == HID_COLLECTION_PHYSICAL && - hid->bus == BUS_USB) + (hid->bus == BUS_USB || hid->bus == BUS_I2C)) hid->group = HID_GROUP_SENSOR_HUB; } @@ -1195,6 +1195,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, { struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report *report; + struct hid_driver *hdrv; unsigned int a; int rsize, csize = size; u8 *cdata = data; @@ -1231,6 +1232,9 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, if (hid->claimed != HID_CLAIMED_HIDRAW) { for (a = 0; a < report->maxfield; a++) hid_input_field(hid, report->field[a], cdata, interrupt); + hdrv = hid->driver; + if (hdrv && hdrv->report) + hdrv->report(hid, report); } if (hid->claimed & HID_CLAIMED_INPUT) @@ -1599,6 +1603,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) }, { 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) }, @@ -1697,7 +1702,9 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) }, @@ -2228,6 +2235,14 @@ bool hid_ignore(struct hid_device *hdev) hdev->type != HID_TYPE_USBMOUSE) return true; break; + case USB_VENDOR_ID_VELLEMAN: + /* These are not HID devices. They are handled by comedi. */ + if ((hdev->product >= USB_DEVICE_ID_VELLEMAN_K8055_FIRST && + hdev->product <= USB_DEVICE_ID_VELLEMAN_K8055_LAST) || + (hdev->product >= USB_DEVICE_ID_VELLEMAN_K8061_FIRST && + hdev->product <= USB_DEVICE_ID_VELLEMAN_K8061_LAST)) + return true; + break; } if (hdev->type == HID_TYPE_USBMOUSE && diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c index 3e159a50dac7..c4ef3bc726e3 100644 --- a/drivers/hid/hid-cypress.c +++ b/drivers/hid/hid-cypress.c @@ -144,17 +144,6 @@ static struct hid_driver cp_driver = { .event = cp_event, .probe = cp_probe, }; +module_hid_driver(cp_driver); -static int __init cp_init(void) -{ - return hid_register_driver(&cp_driver); -} - -static void __exit cp_exit(void) -{ - hid_unregister_driver(&cp_driver); -} - -module_init(cp_init); -module_exit(cp_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c index e832f44ae383..0fe8f65ef01a 100644 --- a/drivers/hid/hid-dr.c +++ b/drivers/hid/hid-dr.c @@ -297,17 +297,6 @@ static struct hid_driver dr_driver = { .report_fixup = dr_report_fixup, .probe = dr_probe, }; +module_hid_driver(dr_driver); -static int __init dr_init(void) -{ - return hid_register_driver(&dr_driver); -} - -static void __exit dr_exit(void) -{ - hid_unregister_driver(&dr_driver); -} - -module_init(dr_init); -module_exit(dr_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c index 79d0c61e7214..d0bd13b62dc2 100644 --- a/drivers/hid/hid-elecom.c +++ b/drivers/hid/hid-elecom.c @@ -41,17 +41,6 @@ static struct hid_driver elecom_driver = { .id_table = elecom_devices, .report_fixup = elecom_report_fixup }; +module_hid_driver(elecom_driver); -static int __init elecom_init(void) -{ - return hid_register_driver(&elecom_driver); -} - -static void __exit elecom_exit(void) -{ - hid_unregister_driver(&elecom_driver); -} - -module_init(elecom_init); -module_exit(elecom_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-emsff.c b/drivers/hid/hid-emsff.c index 2630d483d262..2e093ab99b43 100644 --- a/drivers/hid/hid-emsff.c +++ b/drivers/hid/hid-emsff.c @@ -150,18 +150,7 @@ static struct hid_driver ems_driver = { .id_table = ems_devices, .probe = ems_probe, }; +module_hid_driver(ems_driver); -static int ems_init(void) -{ - return hid_register_driver(&ems_driver); -} - -static void ems_exit(void) -{ - hid_unregister_driver(&ems_driver); -} - -module_init(ems_init); -module_exit(ems_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c index 6540af2871a7..212ac6be2451 100644 --- a/drivers/hid/hid-ezkey.c +++ b/drivers/hid/hid-ezkey.c @@ -76,17 +76,6 @@ static struct hid_driver ez_driver = { .input_mapping = ez_input_mapping, .event = ez_event, }; +module_hid_driver(ez_driver); -static int __init ez_init(void) -{ - return hid_register_driver(&ez_driver); -} - -static void __exit ez_exit(void) -{ - hid_unregister_driver(&ez_driver); -} - -module_init(ez_init); -module_exit(ez_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c index f1e1bcf67427..04d2e6aca778 100644 --- a/drivers/hid/hid-gaff.c +++ b/drivers/hid/hid-gaff.c @@ -176,17 +176,6 @@ static struct hid_driver ga_driver = { .id_table = ga_devices, .probe = ga_probe, }; +module_hid_driver(ga_driver); -static int __init ga_init(void) -{ - return hid_register_driver(&ga_driver); -} - -static void __exit ga_exit(void) -{ - hid_unregister_driver(&ga_driver); -} - -module_init(ga_init); -module_exit(ga_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c index a8b3148e03a2..e288a4a06fe8 100644 --- a/drivers/hid/hid-generic.c +++ b/drivers/hid/hid-generic.c @@ -34,19 +34,7 @@ static struct hid_driver hid_generic = { .name = "hid-generic", .id_table = hid_table, }; - -static int __init hid_init(void) -{ - return hid_register_driver(&hid_generic); -} - -static void __exit hid_exit(void) -{ - hid_unregister_driver(&hid_generic); -} - -module_init(hid_init); -module_exit(hid_exit); +module_hid_driver(hid_generic); MODULE_AUTHOR("Henrik Rydberg"); MODULE_DESCRIPTION("HID generic driver"); diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c index 4442c30ef531..288d61c9748e 100644 --- a/drivers/hid/hid-gyration.c +++ b/drivers/hid/hid-gyration.c @@ -88,17 +88,6 @@ static struct hid_driver gyration_driver = { .input_mapping = gyration_input_mapping, .event = gyration_event, }; +module_hid_driver(gyration_driver); -static int __init gyration_init(void) -{ - return hid_register_driver(&gyration_driver); -} - -static void __exit gyration_exit(void) -{ - hid_unregister_driver(&gyration_driver); -} - -module_init(gyration_init); -module_exit(gyration_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-holtek-kbd.c b/drivers/hid/hid-holtek-kbd.c index e0a5d1739fc3..6e1a4a4fc0c1 100644 --- a/drivers/hid/hid-holtek-kbd.c +++ b/drivers/hid/hid-holtek-kbd.c @@ -167,17 +167,6 @@ static struct hid_driver holtek_kbd_driver = { .report_fixup = holtek_kbd_report_fixup, .probe = holtek_kbd_probe }; +module_hid_driver(holtek_kbd_driver); -static int __init holtek_kbd_init(void) -{ - return hid_register_driver(&holtek_kbd_driver); -} - -static void __exit holtek_kbd_exit(void) -{ - hid_unregister_driver(&holtek_kbd_driver); -} - -module_exit(holtek_kbd_exit); -module_init(holtek_kbd_init); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c index ff295e60059b..f34d1186a3e1 100644 --- a/drivers/hid/hid-holtekff.c +++ b/drivers/hid/hid-holtekff.c @@ -224,17 +224,4 @@ static struct hid_driver holtek_driver = { .id_table = holtek_devices, .probe = holtek_probe, }; - -static int __init holtek_init(void) -{ - return hid_register_driver(&holtek_driver); -} - -static void __exit holtek_exit(void) -{ - hid_unregister_driver(&holtek_driver); -} - -module_init(holtek_init); -module_exit(holtek_exit); - +module_hid_driver(holtek_driver); diff --git a/drivers/hid/hid-icade.c b/drivers/hid/hid-icade.c index 1d6565e37ba3..09dcc04595f3 100644 --- a/drivers/hid/hid-icade.c +++ b/drivers/hid/hid-icade.c @@ -235,25 +235,8 @@ static struct hid_driver icade_driver = { .input_mapped = icade_input_mapped, .input_mapping = icade_input_mapping, }; +module_hid_driver(icade_driver); -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"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 34e25471aeaa..6e5c2ffa8d96 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -445,6 +445,9 @@ #define USB_VENDOR_ID_JESS 0x0c45 #define USB_DEVICE_ID_JESS_YUREX 0x1010 +#define USB_VENDOR_ID_JESS2 0x0f30 +#define USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD 0x0111 + #define USB_VENDOR_ID_KBGEAR 0x084e #define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 @@ -525,8 +528,8 @@ #define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 #define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 #define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287 -#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 +#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 #define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298 #define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 @@ -597,6 +600,9 @@ #define USB_VENDOR_ID_NEC 0x073e #define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 +#define USB_VENDOR_ID_NEXIO 0x1870 +#define USB_DEVICE_ID_NEXIO_MULTITOUCH_420 0x010d + #define USB_VENDOR_ID_NEXTWINDOW 0x1926 #define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN 0x0003 @@ -709,6 +715,7 @@ #define USB_VENDOR_ID_SONY 0x054c #define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b +#define USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE 0x0374 #define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306 #define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f @@ -726,6 +733,9 @@ #define USB_VENDOR_ID_STANTUM_SITRONIX 0x1403 #define USB_DEVICE_ID_MTP_SITRONIX 0x5001 +#define USB_VENDOR_ID_STEELSERIES 0x1038 +#define USB_DEVICE_ID_STEELSERIES_SRWS1 0x1410 + #define USB_VENDOR_ID_SUN 0x0430 #define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab @@ -747,6 +757,9 @@ #define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 #define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 +#define USB_VENDOR_ID_THINGM 0x27b8 +#define USB_DEVICE_ID_BLINK1 0x01ed + #define USB_VENDOR_ID_THRUSTMASTER 0x044f #define USB_VENDOR_ID_TIVO 0x150a @@ -794,6 +807,12 @@ #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19 0x0a19 +#define USB_VENDOR_ID_VELLEMAN 0x10cf +#define USB_DEVICE_ID_VELLEMAN_K8055_FIRST 0x5500 +#define USB_DEVICE_ID_VELLEMAN_K8055_LAST 0x5503 +#define USB_DEVICE_ID_VELLEMAN_K8061_FIRST 0x8061 +#define USB_DEVICE_ID_VELLEMAN_K8061_LAST 0x8068 + #define USB_VENDOR_ID_VERNIER 0x08f7 #define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 #define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 diff --git a/drivers/hid/hid-kensington.c b/drivers/hid/hid-kensington.c index a5b4016e9bd7..fe9a99dd8d08 100644 --- a/drivers/hid/hid-kensington.c +++ b/drivers/hid/hid-kensington.c @@ -47,17 +47,6 @@ static struct hid_driver ks_driver = { .id_table = ks_devices, .input_mapping = ks_input_mapping, }; +module_hid_driver(ks_driver); -static int __init ks_init(void) -{ - return hid_register_driver(&ks_driver); -} - -static void __exit ks_exit(void) -{ - hid_unregister_driver(&ks_driver); -} - -module_init(ks_init); -module_exit(ks_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-keytouch.c b/drivers/hid/hid-keytouch.c index 07cd825f6f01..3074671b7d6a 100644 --- a/drivers/hid/hid-keytouch.c +++ b/drivers/hid/hid-keytouch.c @@ -49,18 +49,7 @@ static struct hid_driver keytouch_driver = { .id_table = keytouch_devices, .report_fixup = keytouch_report_fixup, }; +module_hid_driver(keytouch_driver); -static int __init keytouch_init(void) -{ - return hid_register_driver(&keytouch_driver); -} - -static void __exit keytouch_exit(void) -{ - hid_unregister_driver(&keytouch_driver); -} - -module_init(keytouch_init); -module_exit(keytouch_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jiri Kosina"); diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index b4f0d8216fd0..ef72daecfa16 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -419,17 +419,6 @@ static struct hid_driver kye_driver = { .probe = kye_probe, .report_fixup = kye_report_fixup, }; +module_hid_driver(kye_driver); -static int __init kye_init(void) -{ - return hid_register_driver(&kye_driver); -} - -static void __exit kye_exit(void) -{ - hid_unregister_driver(&kye_driver); -} - -module_init(kye_init); -module_exit(kye_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lcpower.c b/drivers/hid/hid-lcpower.c index 22bc14abdfa3..6424cfdb7737 100644 --- a/drivers/hid/hid-lcpower.c +++ b/drivers/hid/hid-lcpower.c @@ -54,17 +54,6 @@ static struct hid_driver ts_driver = { .id_table = ts_devices, .input_mapping = ts_input_mapping, }; +module_hid_driver(ts_driver); -static int __init ts_init(void) -{ - return hid_register_driver(&ts_driver); -} - -static void __exit ts_exit(void) -{ - hid_unregister_driver(&ts_driver); -} - -module_init(ts_init); -module_exit(ts_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c index cea016e94f43..956c3b135f64 100644 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ b/drivers/hid/hid-lenovo-tpkbd.c @@ -468,18 +468,6 @@ static struct hid_driver tpkbd_driver = { .probe = tpkbd_probe, .remove = tpkbd_remove, }; - -static int __init tpkbd_init(void) -{ - return hid_register_driver(&tpkbd_driver); -} - -static void __exit tpkbd_exit(void) -{ - hid_unregister_driver(&tpkbd_driver); -} - -module_init(tpkbd_init); -module_exit(tpkbd_exit); +module_hid_driver(tpkbd_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index a2f8e88b9fa2..6f12ecd36c88 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -21,8 +21,10 @@ #include <linux/module.h> #include <linux/random.h> #include <linux/sched.h> +#include <linux/usb.h> #include <linux/wait.h> +#include "usbhid/usbhid.h" #include "hid-ids.h" #include "hid-lg.h" @@ -40,17 +42,86 @@ #define LG_FF3 0x1000 #define LG_FF4 0x2000 -/* Size of the original descriptor of the Driving Force Pro wheel */ +/* Size of the original descriptors of the Driving Force (and Pro) wheels */ +#define DF_RDESC_ORIG_SIZE 130 #define DFP_RDESC_ORIG_SIZE 97 +#define MOMO_RDESC_ORIG_SIZE 87 -/* Fixed report descriptor for Logitech Driving Force Pro wheel controller +/* Fixed report descriptors for Logitech Driving Force (and Pro) + * wheel controllers * - * The original descriptor hides the separate throttle and brake axes in + * The original descriptors hide the separate throttle and brake axes in * a custom vendor usage page, providing only a combined value as * GenericDesktop.Y. - * This descriptor removes the combined Y axis and instead reports + * These descriptors remove the combined Y axis and instead report * separate throttle (Y) and brake (RZ). */ +static __u8 df_rdesc_fixed[] = { +0x05, 0x01, /* Usage Page (Desktop), */ +0x09, 0x04, /* Usage (Joystik), */ +0xA1, 0x01, /* Collection (Application), */ +0xA1, 0x02, /* Collection (Logical), */ +0x95, 0x01, /* Report Count (1), */ +0x75, 0x0A, /* Report Size (10), */ +0x14, /* Logical Minimum (0), */ +0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ +0x34, /* Physical Minimum (0), */ +0x46, 0xFF, 0x03, /* Physical Maximum (1023), */ +0x09, 0x30, /* Usage (X), */ +0x81, 0x02, /* Input (Variable), */ +0x95, 0x0C, /* Report Count (12), */ +0x75, 0x01, /* Report Size (1), */ +0x25, 0x01, /* Logical Maximum (1), */ +0x45, 0x01, /* Physical Maximum (1), */ +0x05, 0x09, /* Usage (Buttons), */ +0x19, 0x01, /* Usage Minimum (1), */ +0x29, 0x0c, /* Usage Maximum (12), */ +0x81, 0x02, /* Input (Variable), */ +0x95, 0x02, /* Report Count (2), */ +0x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */ +0x09, 0x01, /* Usage (?: 1), */ +0x81, 0x02, /* Input (Variable), */ +0x05, 0x01, /* Usage Page (Desktop), */ +0x26, 0xFF, 0x00, /* Logical Maximum (255), */ +0x46, 0xFF, 0x00, /* Physical Maximum (255), */ +0x95, 0x01, /* Report Count (1), */ +0x75, 0x08, /* Report Size (8), */ +0x81, 0x02, /* Input (Variable), */ +0x25, 0x07, /* Logical Maximum (7), */ +0x46, 0x3B, 0x01, /* Physical Maximum (315), */ +0x75, 0x04, /* Report Size (4), */ +0x65, 0x14, /* Unit (Degrees), */ +0x09, 0x39, /* Usage (Hat Switch), */ +0x81, 0x42, /* Input (Variable, Null State), */ +0x75, 0x01, /* Report Size (1), */ +0x95, 0x04, /* Report Count (4), */ +0x65, 0x00, /* Unit (none), */ +0x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */ +0x09, 0x01, /* Usage (?: 1), */ +0x25, 0x01, /* Logical Maximum (1), */ +0x45, 0x01, /* Physical Maximum (1), */ +0x81, 0x02, /* Input (Variable), */ +0x05, 0x01, /* Usage Page (Desktop), */ +0x95, 0x01, /* Report Count (1), */ +0x75, 0x08, /* Report Size (8), */ +0x26, 0xFF, 0x00, /* Logical Maximum (255), */ +0x46, 0xFF, 0x00, /* Physical Maximum (255), */ +0x09, 0x31, /* Usage (Y), */ +0x81, 0x02, /* Input (Variable), */ +0x09, 0x35, /* Usage (Rz), */ +0x81, 0x02, /* Input (Variable), */ +0xC0, /* End Collection, */ +0xA1, 0x02, /* Collection (Logical), */ +0x26, 0xFF, 0x00, /* Logical Maximum (255), */ +0x46, 0xFF, 0x00, /* Physical Maximum (255), */ +0x95, 0x07, /* Report Count (7), */ +0x75, 0x08, /* Report Size (8), */ +0x09, 0x03, /* Usage (?: 3), */ +0x91, 0x02, /* Output (Variable), */ +0xC0, /* End Collection, */ +0xC0 /* End Collection */ +}; + static __u8 dfp_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x04, /* Usage (Joystik), */ @@ -99,6 +170,51 @@ static __u8 dfp_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +static __u8 momo_rdesc_fixed[] = { +0x05, 0x01, /* Usage Page (Desktop), */ +0x09, 0x04, /* Usage (Joystik), */ +0xA1, 0x01, /* Collection (Application), */ +0xA1, 0x02, /* Collection (Logical), */ +0x95, 0x01, /* Report Count (1), */ +0x75, 0x0A, /* Report Size (10), */ +0x15, 0x00, /* Logical Minimum (0), */ +0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ +0x35, 0x00, /* Physical Minimum (0), */ +0x46, 0xFF, 0x03, /* Physical Maximum (1023), */ +0x09, 0x30, /* Usage (X), */ +0x81, 0x02, /* Input (Variable), */ +0x95, 0x08, /* Report Count (8), */ +0x75, 0x01, /* Report Size (1), */ +0x25, 0x01, /* Logical Maximum (1), */ +0x45, 0x01, /* Physical Maximum (1), */ +0x05, 0x09, /* Usage Page (Button), */ +0x19, 0x01, /* Usage Minimum (01h), */ +0x29, 0x08, /* Usage Maximum (08h), */ +0x81, 0x02, /* Input (Variable), */ +0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ +0x75, 0x0E, /* Report Size (14), */ +0x95, 0x01, /* Report Count (1), */ +0x26, 0xFF, 0x00, /* Logical Maximum (255), */ +0x46, 0xFF, 0x00, /* Physical Maximum (255), */ +0x09, 0x00, /* Usage (00h), */ +0x81, 0x02, /* Input (Variable), */ +0x05, 0x01, /* Usage Page (Desktop), */ +0x75, 0x08, /* Report Size (8), */ +0x09, 0x31, /* Usage (Y), */ +0x81, 0x02, /* Input (Variable), */ +0x09, 0x32, /* Usage (Z), */ +0x81, 0x02, /* Input (Variable), */ +0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ +0x09, 0x01, /* Usage (01h), */ +0x81, 0x02, /* Input (Variable), */ +0xC0, /* End Collection, */ +0xA1, 0x02, /* Collection (Logical), */ +0x09, 0x02, /* Usage (02h), */ +0x95, 0x07, /* Report Count (7), */ +0x91, 0x02, /* Output (Variable), */ +0xC0, /* End Collection, */ +0xC0 /* End Collection */ +}; /* * Certain Logitech keyboards send in report #3 keys which are far @@ -109,6 +225,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct lg_drv_data *drv_data = hid_get_drvdata(hdev); + struct usb_device_descriptor *udesc; + __u16 bcdDevice, rev_maj, rev_min; if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 && rdesc[84] == 0x8c && rdesc[85] == 0x02) { @@ -124,17 +242,39 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, "fixing up rel/abs in Logitech report descriptor\n"); rdesc[33] = rdesc[50] = 0x02; } - if ((drv_data->quirks & LG_FF4) && *rsize >= 101 && - rdesc[41] == 0x95 && rdesc[42] == 0x0B && - rdesc[47] == 0x05 && rdesc[48] == 0x09) { - hid_info(hdev, "fixing up Logitech Speed Force Wireless button descriptor\n"); - rdesc[41] = 0x05; - rdesc[42] = 0x09; - rdesc[47] = 0x95; - rdesc[48] = 0x0B; - } switch (hdev->product) { + + /* Several wheels report as this id when operating in emulation mode. */ + case USB_DEVICE_ID_LOGITECH_WHEEL: + udesc = &(hid_to_usb_dev(hdev)->descriptor); + if (!udesc) { + hid_err(hdev, "NULL USB device descriptor\n"); + break; + } + bcdDevice = le16_to_cpu(udesc->bcdDevice); + rev_maj = bcdDevice >> 8; + rev_min = bcdDevice & 0xff; + + /* Update the report descriptor for only the Driving Force wheel */ + if (rev_maj == 1 && rev_min == 2 && + *rsize == DF_RDESC_ORIG_SIZE) { + hid_info(hdev, + "fixing up Logitech Driving Force report descriptor\n"); + rdesc = df_rdesc_fixed; + *rsize = sizeof(df_rdesc_fixed); + } + break; + + case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: + if (*rsize == MOMO_RDESC_ORIG_SIZE) { + hid_info(hdev, + "fixing up Logitech Momo Force (Red) report descriptor\n"); + rdesc = momo_rdesc_fixed; + *rsize = sizeof(momo_rdesc_fixed); + } + break; + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: if (*rsize == DFP_RDESC_ORIG_SIZE) { hid_info(hdev, @@ -143,6 +283,17 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(dfp_rdesc_fixed); } break; + + case USB_DEVICE_ID_LOGITECH_WII_WHEEL: + if (*rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B && + rdesc[47] == 0x05 && rdesc[48] == 0x09) { + hid_info(hdev, "fixing up Logitech Speed Force Wireless report descriptor\n"); + rdesc[41] = 0x05; + rdesc[42] = 0x09; + rdesc[47] = 0x95; + rdesc[48] = 0x0B; + } + break; } return rdesc; @@ -328,6 +479,26 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, usage->type == EV_REL || usage->type == EV_ABS)) clear_bit(usage->code, *bit); + /* Ensure that Logitech wheels are not given a default fuzz/flat value */ + if (usage->type == EV_ABS && (usage->code == ABS_X || + usage->code == ABS_Y || usage->code == ABS_Z || + usage->code == ABS_RZ)) { + switch (hdev->product) { + case USB_DEVICE_ID_LOGITECH_WHEEL: + case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: + case USB_DEVICE_ID_LOGITECH_G25_WHEEL: + case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: + case USB_DEVICE_ID_LOGITECH_G27_WHEEL: + case USB_DEVICE_ID_LOGITECH_WII_WHEEL: + case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2: + field->application = HID_GD_MULTIAXIS; + break; + default: + break; + } + } + return 0; } @@ -465,7 +636,7 @@ static const struct hid_device_id lg_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO), .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL), - .driver_data = LG_FF4 }, + .driver_data = LG_NOGET | LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2), .driver_data = LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), @@ -503,17 +674,6 @@ static struct hid_driver lg_driver = { .probe = lg_probe, .remove = lg_remove, }; +module_hid_driver(lg_driver); -static int __init lg_init(void) -{ - return hid_register_driver(&lg_driver); -} - -static void __exit lg_exit(void) -{ - hid_unregister_driver(&lg_driver); -} - -module_init(lg_init); -module_exit(lg_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index d7947c701f30..65a6ec8d3742 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -43,11 +43,6 @@ #define G27_REV_MAJ 0x12 #define G27_REV_MIN 0x38 -#define DFP_X_MIN 0 -#define DFP_X_MAX 16383 -#define DFP_PEDAL_MIN 0 -#define DFP_PEDAL_MAX 255 - #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range); @@ -598,18 +593,6 @@ int lg4ff_init(struct hid_device *hid) return error; dbg_hid("sysfs interface created\n"); - /* Set default axes parameters */ - switch (lg4ff_devices[i].product_id) { - case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: - dbg_hid("Setting axes parameters for Driving Force Pro\n"); - input_set_abs_params(dev, ABS_X, DFP_X_MIN, DFP_X_MAX, 0, 0); - input_set_abs_params(dev, ABS_Y, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0); - input_set_abs_params(dev, ABS_RZ, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0); - break; - default: - break; - } - /* Set the maximum range to start with */ entry->range = entry->max_range; if (entry->set_range != NULL) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 25ddf3e3aec6..f7f113ba083e 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -569,23 +569,6 @@ static struct hid_driver magicmouse_driver = { .raw_event = magicmouse_raw_event, .input_mapping = magicmouse_input_mapping, }; +module_hid_driver(magicmouse_driver); -static int __init magicmouse_init(void) -{ - int ret; - - ret = hid_register_driver(&magicmouse_driver); - if (ret) - pr_err("can't register magicmouse driver\n"); - - return ret; -} - -static void __exit magicmouse_exit(void) -{ - hid_unregister_driver(&magicmouse_driver); -} - -module_init(magicmouse_init); -module_exit(magicmouse_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 6fcd466d0825..29d27f65a118 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -221,17 +221,6 @@ static struct hid_driver ms_driver = { .event = ms_event, .probe = ms_probe, }; +module_hid_driver(ms_driver); -static int __init ms_init(void) -{ - return hid_register_driver(&ms_driver); -} - -static void __exit ms_exit(void) -{ - hid_unregister_driver(&ms_driver); -} - -module_init(ms_init); -module_exit(ms_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c index cd3643e06fa6..9e14c00eb1b6 100644 --- a/drivers/hid/hid-monterey.c +++ b/drivers/hid/hid-monterey.c @@ -63,17 +63,6 @@ static struct hid_driver mr_driver = { .report_fixup = mr_report_fixup, .input_mapping = mr_input_mapping, }; +module_hid_driver(mr_driver); -static int __init mr_init(void) -{ - return hid_register_driver(&mr_driver); -} - -static void __exit mr_exit(void) -{ - hid_unregister_driver(&mr_driver); -} - -module_init(mr_init); -module_exit(mr_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 61543c02ea0b..7a1ebb867cf4 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -54,6 +54,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_NO_AREA (1 << 9) #define MT_QUIRK_IGNORE_DUPLICATES (1 << 10) #define MT_QUIRK_HOVERING (1 << 11) +#define MT_QUIRK_CONTACT_CNT_ACCURATE (1 << 12) struct mt_slot { __s32 x, y, cx, cy, p, w, h; @@ -83,8 +84,11 @@ struct mt_device { struct mt_class mtclass; /* our mt device class */ struct mt_fields *fields; /* temporary placeholder for storing the multitouch fields */ + int cc_index; /* contact count field index in the report */ + int cc_value_index; /* contact count value index in the field */ unsigned last_field_index; /* last field index of the report */ unsigned last_slot_field; /* the last field of a slot */ + unsigned mt_report_id; /* the report ID of the multitouch device */ __s8 inputmode; /* InputMode HID feature, -1 if non-existent */ __s8 inputmode_index; /* InputMode HID feature index in the report */ __s8 maxcontact_report_id; /* Maximum Contact Number HID feature, @@ -111,6 +115,9 @@ struct mt_device { #define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0007 #define MT_CLS_DUAL_NSMU_CONTACTID 0x0008 #define MT_CLS_INRANGE_CONTACTNUMBER 0x0009 +#define MT_CLS_NSMU 0x000a +#define MT_CLS_DUAL_CONTACT_NUMBER 0x0010 +#define MT_CLS_DUAL_CONTACT_ID 0x0011 /* vendor specific classes */ #define MT_CLS_3M 0x0101 @@ -144,6 +151,9 @@ static int cypress_compute_slot(struct mt_device *td) static struct mt_class mt_classes[] = { { .name = MT_CLS_DEFAULT, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE }, + { .name = MT_CLS_NSMU, .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP }, { .name = MT_CLS_SERIAL, .quirks = MT_QUIRK_ALWAYS_VALID}, @@ -170,6 +180,16 @@ static struct mt_class mt_classes[] = { { .name = MT_CLS_INRANGE_CONTACTNUMBER, .quirks = MT_QUIRK_VALID_IS_INRANGE | MT_QUIRK_SLOT_IS_CONTACTNUMBER }, + { .name = MT_CLS_DUAL_CONTACT_NUMBER, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_SLOT_IS_CONTACTNUMBER, + .maxcontacts = 2 }, + { .name = MT_CLS_DUAL_CONTACT_ID, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_SLOT_IS_CONTACTID, + .maxcontacts = 2 }, /* * vendor specific classes @@ -250,6 +270,9 @@ static ssize_t mt_set_quirks(struct device *dev, td->mtclass.quirks = val; + if (td->cc_index < 0) + td->mtclass.quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; + return count; } @@ -301,6 +324,7 @@ static void mt_feature_mapping(struct hid_device *hdev, *quirks |= MT_QUIRK_ALWAYS_VALID; *quirks |= MT_QUIRK_IGNORE_DUPLICATES; *quirks |= MT_QUIRK_HOVERING; + *quirks |= MT_QUIRK_CONTACT_CNT_ACCURATE; *quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; *quirks &= ~MT_QUIRK_VALID_IS_INRANGE; *quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; @@ -428,6 +452,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, mt_store_field(usage, td, hi); td->last_field_index = field->index; td->touches_by_report++; + td->mt_report_id = field->report->id; return 1; case HID_DG_WIDTH: hid_map_usage(hi, usage, bit, max, @@ -459,6 +484,8 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, td->last_field_index = field->index; return 1; case HID_DG_CONTACTCOUNT: + td->cc_index = field->index; + td->cc_value_index = usage->usage_index; td->last_field_index = field->index; return 1; case HID_DG_CONTACTMAX: @@ -523,6 +550,10 @@ static int mt_compute_slot(struct mt_device *td, struct input_dev *input) */ static void mt_complete_slot(struct mt_device *td, struct input_dev *input) { + if ((td->mtclass.quirks & MT_QUIRK_CONTACT_CNT_ACCURATE) && + td->num_received >= td->num_expected) + return; + if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) { int slotnum = mt_compute_slot(td, input); struct mt_slot *s = &td->curdata; @@ -578,6 +609,16 @@ static void mt_sync_frame(struct mt_device *td, struct input_dev *input) static int mt_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { + /* we will handle the hidinput part later, now remains hiddev */ + if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) + hid->hiddev_hid_event(hid, field, usage, value); + + return 1; +} + +static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ struct mt_device *td = hid_get_drvdata(hid); __s32 quirks = td->mtclass.quirks; @@ -623,20 +664,13 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, td->curdata.h = value; break; case HID_DG_CONTACTCOUNT: - /* - * Includes multi-packet support where subsequent - * packets are sent with zero contactcount. - */ - if (value) - td->num_expected = value; break; case HID_DG_TOUCH: /* do nothing */ break; default: - /* fallback to the generic hidinput handling */ - return 0; + return; } if (usage->usage_index + 1 == field->report_count) { @@ -650,12 +684,43 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, } } +} - /* we have handled the hidinput part, now remains hiddev */ - if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) - hid->hiddev_hid_event(hid, field, usage, value); +static void mt_report(struct hid_device *hid, struct hid_report *report) +{ + struct mt_device *td = hid_get_drvdata(hid); + struct hid_field *field; + unsigned count; + int r, n; - return 1; + if (report->id != td->mt_report_id) + return; + + if (!(hid->claimed & HID_CLAIMED_INPUT)) + return; + + /* + * Includes multi-packet support where subsequent + * packets are sent with zero contactcount. + */ + if (td->cc_index >= 0) { + struct hid_field *field = report->field[td->cc_index]; + int value = field->value[td->cc_value_index]; + if (value) + td->num_expected = value; + } + + for (r = 0; r < report->maxfield; r++) { + field = report->field[r]; + count = field->report_count; + + if (!(HID_MAIN_ITEM_VARIABLE & field->flags)) + continue; + + for (n = 0; n < count; n++) + mt_process_mt_event(hid, field, &field->usage[n], + field->value[n]); + } } static void mt_set_input_mode(struct hid_device *hdev) @@ -711,6 +776,7 @@ static void mt_post_parse_default_settings(struct mt_device *td) quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; quirks &= ~MT_QUIRK_VALID_IS_INRANGE; quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; + quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } td->mtclass.quirks = quirks; @@ -719,11 +785,15 @@ static void mt_post_parse_default_settings(struct mt_device *td) static void mt_post_parse(struct mt_device *td) { struct mt_fields *f = td->fields; + struct mt_class *cls = &td->mtclass; if (td->touches_by_report > 0) { int field_count_per_touch = f->length / td->touches_by_report; td->last_slot_field = f->usages[field_count_per_touch - 1]; } + + if (td->cc_index < 0) + cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) @@ -781,6 +851,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) td->mtclass = *mtclass; td->inputmode = -1; td->maxcontact_report_id = -1; + td->cc_index = -1; hid_set_drvdata(hdev, td); td->fields = kzalloc(sizeof(struct mt_fields), GFP_KERNEL); @@ -875,7 +946,7 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_3M3266) }, /* ActionStar panels */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR, USB_DEVICE_ID_ACTIONSTAR_1011) }, @@ -888,14 +959,14 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_ATMEL_MXT_DIGITIZER) }, /* Baanto multitouch devices */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_BAANTO, USB_DEVICE_ID_BAANTO_MT_190W2) }, /* Cando panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, MT_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH) }, - { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, + { .driver_data = MT_CLS_DUAL_CONTACT_NUMBER, MT_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) }, { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, @@ -906,12 +977,12 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) }, /* Chunghwa Telecom touch panels */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT, USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH) }, /* CVTouch panels */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_CVTOUCH, USB_DEVICE_ID_CVTOUCH_SCREEN) }, @@ -982,7 +1053,7 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) }, /* Elo TouchSystems IntelliTouch Plus panel */ - { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID, + { .driver_data = MT_CLS_DUAL_CONTACT_ID, MT_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2515) }, @@ -1000,12 +1071,12 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS) }, /* Gametel game controller */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_BT_DEVICE(USB_VENDOR_ID_FRUCTEL, USB_DEVICE_ID_GAMETEL_MT_MODE) }, /* GoodTouch panels */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH, USB_DEVICE_ID_GOODTOUCH_000f) }, @@ -1023,7 +1094,7 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_IDEACOM_IDC6651) }, /* Ilitek dual touch panel */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, USB_DEVICE_ID_ILITEK_MULTITOUCH) }, @@ -1056,6 +1127,11 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) }, + /* Nexio panels */ + { .driver_data = MT_CLS_DEFAULT, + MT_USB_DEVICE(USB_VENDOR_ID_NEXIO, + USB_DEVICE_ID_NEXIO_MULTITOUCH_420)}, + /* Panasonic panels */ { .driver_data = MT_CLS_PANASONIC, MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC, @@ -1065,7 +1141,7 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_PANABOARD_UBT880) }, /* Novatek Panel */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_PCT) }, @@ -1111,7 +1187,7 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_CONFIDENCE, MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_MTP_STM)}, - { .driver_data = MT_CLS_CONFIDENCE, + { .driver_data = MT_CLS_DEFAULT, MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, USB_DEVICE_ID_MTP_SITRONIX)}, @@ -1121,48 +1197,48 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_TOPSEED2_PERIPAD_701) }, /* Touch International panels */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_TOUCH_INTL, USB_DEVICE_ID_TOUCH_INTL_MULTI_TOUCH) }, /* Unitec panels */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0709) }, - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) }, /* XAT */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) }, /* Xiroku */ - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX) }, - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX) }, - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR) }, - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX1) }, - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX1) }, - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR1) }, - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX2) }, - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX2) }, - { .driver_data = MT_CLS_DEFAULT, + { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR2) }, @@ -1193,21 +1269,10 @@ static struct hid_driver mt_driver = { .feature_mapping = mt_feature_mapping, .usage_table = mt_grabbed_usages, .event = mt_event, + .report = mt_report, #ifdef CONFIG_PM .reset_resume = mt_reset_resume, .resume = mt_resume, #endif }; - -static int __init mt_init(void) -{ - return hid_register_driver(&mt_driver); -} - -static void __exit mt_exit(void) -{ - hid_unregister_driver(&mt_driver); -} - -module_init(mt_init); -module_exit(mt_exit); +module_hid_driver(mt_driver); diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 86a969f63292..7757e82416e7 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -858,12 +858,43 @@ not_claimed_input: return 1; } +static void ntrig_input_configured(struct hid_device *hid, + struct hid_input *hidinput) + +{ + struct input_dev *input = hidinput->input; + + if (hidinput->report->maxfield < 1) + return; + + switch (hidinput->report->field[0]->application) { + case HID_DG_PEN: + input->name = "N-Trig Pen"; + break; + case HID_DG_TOUCHSCREEN: + /* These keys are redundant for fingers, clear them + * to prevent incorrect identification */ + __clear_bit(BTN_TOOL_PEN, input->keybit); + __clear_bit(BTN_TOOL_FINGER, input->keybit); + __clear_bit(BTN_0, input->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); + /* + * The physical touchscreen (single touch) + * input has a value for physical, whereas + * the multitouch only has logical input + * fields. + */ + input->name = (hidinput->report->field[0]->physical) ? + "N-Trig Touchscreen" : + "N-Trig MultiTouch"; + break; + } +} + static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; struct ntrig_data *nd; - struct hid_input *hidinput; - struct input_dev *input; struct hid_report *report; if (id->driver_data) @@ -901,38 +932,6 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - - list_for_each_entry(hidinput, &hdev->inputs, list) { - if (hidinput->report->maxfield < 1) - continue; - - input = hidinput->input; - switch (hidinput->report->field[0]->application) { - case HID_DG_PEN: - input->name = "N-Trig Pen"; - break; - case HID_DG_TOUCHSCREEN: - /* These keys are redundant for fingers, clear them - * to prevent incorrect identification */ - __clear_bit(BTN_TOOL_PEN, input->keybit); - __clear_bit(BTN_TOOL_FINGER, input->keybit); - __clear_bit(BTN_0, input->keybit); - __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); - /* - * The physical touchscreen (single touch) - * input has a value for physical, whereas - * the multitouch only has logical input - * fields. - */ - input->name = - (hidinput->report->field[0] - ->physical) ? - "N-Trig Touchscreen" : - "N-Trig MultiTouch"; - break; - } - } - /* This is needed for devices with more recent firmware versions */ report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0x0a]; if (report) { @@ -1023,20 +1022,10 @@ static struct hid_driver ntrig_driver = { .remove = ntrig_remove, .input_mapping = ntrig_input_mapping, .input_mapped = ntrig_input_mapped, + .input_configured = ntrig_input_configured, .usage_table = ntrig_grabbed_usages, .event = ntrig_event, }; +module_hid_driver(ntrig_driver); -static int __init ntrig_init(void) -{ - return hid_register_driver(&ntrig_driver); -} - -static void __exit ntrig_exit(void) -{ - hid_unregister_driver(&ntrig_driver); -} - -module_init(ntrig_init); -module_exit(ntrig_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ortek.c b/drivers/hid/hid-ortek.c index 0ffa1d2d64f0..6620f15fec22 100644 --- a/drivers/hid/hid-ortek.c +++ b/drivers/hid/hid-ortek.c @@ -50,17 +50,6 @@ static struct hid_driver ortek_driver = { .id_table = ortek_devices, .report_fixup = ortek_report_fixup }; +module_hid_driver(ortek_driver); -static int __init ortek_init(void) -{ - return hid_register_driver(&ortek_driver); -} - -static void __exit ortek_exit(void) -{ - hid_unregister_driver(&ortek_driver); -} - -module_init(ortek_init); -module_exit(ortek_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c index 4c521de4e7e6..736b2502df4f 100644 --- a/drivers/hid/hid-petalynx.c +++ b/drivers/hid/hid-petalynx.c @@ -103,17 +103,6 @@ static struct hid_driver pl_driver = { .input_mapping = pl_input_mapping, .probe = pl_probe, }; +module_hid_driver(pl_driver); -static int __init pl_init(void) -{ - return hid_register_driver(&pl_driver); -} - -static void __exit pl_exit(void) -{ - hid_unregister_driver(&pl_driver); -} - -module_init(pl_init); -module_exit(pl_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index 86df26e58aba..31cd93fc3d4b 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -672,18 +672,7 @@ static struct hid_driver picolcd_driver = { .reset_resume = picolcd_reset_resume, #endif }; +module_hid_driver(picolcd_driver); -static int __init picolcd_init(void) -{ - return hid_register_driver(&picolcd_driver); -} - -static void __exit picolcd_exit(void) -{ - hid_unregister_driver(&picolcd_driver); -} - -module_init(picolcd_init); -module_exit(picolcd_exit); MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index 47ed74c46b6b..b0199d27787b 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -14,6 +14,8 @@ * 0e8f:0003 "GASIA USB Gamepad" * - another version of the König gamepad * + * 0f30:0111 "Saitek Color Rumble Pad" + * * Copyright (c) 2007, 2009 Anssi Hannula <anssi.hannula@gmail.com> */ @@ -51,6 +53,7 @@ struct plff_device { struct hid_report *report; + s32 maxval; s32 *strong; s32 *weak; }; @@ -66,8 +69,8 @@ static int hid_plff_play(struct input_dev *dev, void *data, right = effect->u.rumble.weak_magnitude; debug("called with 0x%04x 0x%04x", left, right); - left = left * 0x7f / 0xffff; - right = right * 0x7f / 0xffff; + left = left * plff->maxval / 0xffff; + right = right * plff->maxval / 0xffff; *plff->strong = left; *plff->weak = right; @@ -87,6 +90,7 @@ static int plff_init(struct hid_device *hid) struct list_head *report_ptr = report_list; struct input_dev *dev; int error; + s32 maxval; s32 *strong; s32 *weak; @@ -123,6 +127,7 @@ static int plff_init(struct hid_device *hid) return -ENODEV; } + maxval = 0x7f; if (report->field[0]->report_count >= 4) { report->field[0]->value[0] = 0x00; report->field[0]->value[1] = 0x00; @@ -135,6 +140,8 @@ static int plff_init(struct hid_device *hid) report->field[1]->value[0] = 0x00; strong = &report->field[2]->value[0]; weak = &report->field[3]->value[0]; + if (hid->vendor == USB_VENDOR_ID_JESS2) + maxval = 0xff; debug("detected 4-field device"); } else { hid_err(hid, "not enough fields or values\n"); @@ -158,6 +165,7 @@ static int plff_init(struct hid_device *hid) plff->report = report; plff->strong = strong; plff->weak = weak; + plff->maxval = maxval; *strong = 0x00; *weak = 0x00; @@ -207,6 +215,7 @@ static const struct hid_device_id pl_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR), .driver_data = 1 }, /* Twin USB Joystick */ { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, + { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD), }, { } }; MODULE_DEVICE_TABLE(hid, pl_devices); @@ -216,17 +225,6 @@ static struct hid_driver pl_driver = { .id_table = pl_devices, .probe = pl_probe, }; +module_hid_driver(pl_driver); -static int __init pl_init(void) -{ - return hid_register_driver(&pl_driver); -} - -static void __exit pl_exit(void) -{ - hid_unregister_driver(&pl_driver); -} - -module_init(pl_init); -module_exit(pl_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c index c15adb0c98a1..3a1c3c4c50dc 100644 --- a/drivers/hid/hid-primax.c +++ b/drivers/hid/hid-primax.c @@ -75,18 +75,7 @@ static struct hid_driver px_driver = { .id_table = px_devices, .raw_event = px_raw_event, }; +module_hid_driver(px_driver); -static int __init px_init(void) -{ - return hid_register_driver(&px_driver); -} - -static void __exit px_exit(void) -{ - hid_unregister_driver(&px_driver); -} - -module_init(px_init); -module_exit(px_exit); MODULE_AUTHOR("Terry Lambert <tlambert@google.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c index ec8ca3336315..4e1c4bcbdc03 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c @@ -889,23 +889,6 @@ static struct hid_driver pk_driver = { .probe = pk_probe, .remove = pk_remove, }; +module_hid_driver(pk_driver); -static int pk_init(void) -{ - int ret; - - ret = hid_register_driver(&pk_driver); - if (ret) - pr_err("can't register prodikeys driver\n"); - - return ret; -} - -static void pk_exit(void) -{ - hid_unregister_driver(&pk_driver); -} - -module_init(pk_init); -module_exit(pk_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ps3remote.c b/drivers/hid/hid-ps3remote.c index 03811e539d71..f1239d3c5b14 100644 --- a/drivers/hid/hid-ps3remote.c +++ b/drivers/hid/hid-ps3remote.c @@ -198,18 +198,7 @@ static struct hid_driver ps3remote_driver = { .report_fixup = ps3remote_fixup, .input_mapping = ps3remote_mapping, }; +module_hid_driver(ps3remote_driver); -static int __init ps3remote_init(void) -{ - return hid_register_driver(&ps3remote_driver); -} - -static void __exit ps3remote_exit(void) -{ - hid_unregister_driver(&ps3remote_driver); -} - -module_init(ps3remote_init); -module_exit(ps3remote_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Dillow <dave@thedillows.org>, Antonio Ospite <ospite@studenti.unina.it>"); diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c index 5084fb4b7e91..6adc0fa08d96 100644 --- a/drivers/hid/hid-roccat-lua.c +++ b/drivers/hid/hid-roccat-lua.c @@ -208,19 +208,7 @@ static struct hid_driver lua_driver = { .probe = lua_probe, .remove = lua_remove }; - -static int __init lua_init(void) -{ - return hid_register_driver(&lua_driver); -} - -static void __exit lua_exit(void) -{ - hid_unregister_driver(&lua_driver); -} - -module_init(lua_init); -module_exit(lua_exit); +module_hid_driver(lua_driver); MODULE_AUTHOR("Stefan Achatz"); MODULE_DESCRIPTION("USB Roccat Lua driver"); diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c index 45aea77bb611..37961c7e397d 100644 --- a/drivers/hid/hid-saitek.c +++ b/drivers/hid/hid-saitek.c @@ -54,17 +54,6 @@ static struct hid_driver saitek_driver = { .id_table = saitek_devices, .report_fixup = saitek_report_fixup }; +module_hid_driver(saitek_driver); -static int __init saitek_init(void) -{ - return hid_register_driver(&saitek_driver); -} - -static void __exit saitek_exit(void) -{ - hid_unregister_driver(&saitek_driver); -} - -module_init(saitek_init); -module_exit(saitek_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c index a5821d317229..7cbb067d4a9e 100644 --- a/drivers/hid/hid-samsung.c +++ b/drivers/hid/hid-samsung.c @@ -196,17 +196,6 @@ static struct hid_driver samsung_driver = { .input_mapping = samsung_input_mapping, .probe = samsung_probe, }; +module_hid_driver(samsung_driver); -static int __init samsung_init(void) -{ - return hid_register_driver(&samsung_driver); -} - -static void __exit samsung_exit(void) -{ - hid_unregister_driver(&samsung_driver); -} - -module_init(samsung_init); -module_exit(samsung_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 0bc58bd8d4f5..6679788bf75a 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -605,16 +605,12 @@ static void sensor_hub_remove(struct hid_device *hdev) } static const struct hid_device_id sensor_hub_devices[] = { - { HID_DEVICE(BUS_USB, HID_GROUP_SENSOR_HUB, HID_ANY_ID, HID_ANY_ID) }, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID, + HID_ANY_ID) }, { } }; MODULE_DEVICE_TABLE(hid, sensor_hub_devices); -static const struct hid_usage_id sensor_hub_grabbed_usages[] = { - { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, - { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 } -}; - static struct hid_driver sensor_hub_driver = { .name = "hid-sensor-hub", .id_table = sensor_hub_devices, @@ -627,19 +623,7 @@ static struct hid_driver sensor_hub_driver = { .reset_resume = sensor_hub_reset_resume, #endif }; - -static int __init sensor_hub_init(void) -{ - return hid_register_driver(&sensor_hub_driver); -} - -static void __exit sensor_hub_exit(void) -{ - hid_unregister_driver(&sensor_hub_driver); -} - -module_init(sensor_hub_init); -module_exit(sensor_hub_exit); +module_hid_driver(sensor_hub_driver); MODULE_DESCRIPTION("HID Sensor Hub driver"); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c index 42257acfeb73..28f774003f03 100644 --- a/drivers/hid/hid-sjoy.c +++ b/drivers/hid/hid-sjoy.c @@ -177,19 +177,8 @@ static struct hid_driver sjoy_driver = { .id_table = sjoy_devices, .probe = sjoy_probe, }; +module_hid_driver(sjoy_driver); -static int __init sjoy_init(void) -{ - return hid_register_driver(&sjoy_driver); -} - -static void __exit sjoy_exit(void) -{ - hid_unregister_driver(&sjoy_driver); -} - -module_init(sjoy_init); -module_exit(sjoy_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jussi Kivilinna"); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 7f33ebf299c2..312098e4af4f 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -33,6 +33,28 @@ static const u8 sixaxis_rdesc_fixup[] = { 0x03, 0x46, 0xFF, 0x03, 0x09, 0x01, 0x81, 0x02 }; +static const u8 sixaxis_rdesc_fixup2[] = { + 0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0xa1, 0x02, + 0x85, 0x01, 0x75, 0x08, 0x95, 0x01, 0x15, 0x00, + 0x26, 0xff, 0x00, 0x81, 0x03, 0x75, 0x01, 0x95, + 0x13, 0x15, 0x00, 0x25, 0x01, 0x35, 0x00, 0x45, + 0x01, 0x05, 0x09, 0x19, 0x01, 0x29, 0x13, 0x81, + 0x02, 0x75, 0x01, 0x95, 0x0d, 0x06, 0x00, 0xff, + 0x81, 0x03, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05, + 0x01, 0x09, 0x01, 0xa1, 0x00, 0x75, 0x08, 0x95, + 0x04, 0x35, 0x00, 0x46, 0xff, 0x00, 0x09, 0x30, + 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x81, 0x02, + 0xc0, 0x05, 0x01, 0x95, 0x13, 0x09, 0x01, 0x81, + 0x02, 0x95, 0x0c, 0x81, 0x01, 0x75, 0x10, 0x95, + 0x04, 0x26, 0xff, 0x03, 0x46, 0xff, 0x03, 0x09, + 0x01, 0x81, 0x02, 0xc0, 0xa1, 0x02, 0x85, 0x02, + 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, + 0xc0, 0xa1, 0x02, 0x85, 0xee, 0x75, 0x08, 0x95, + 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, + 0x85, 0xef, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, + 0xb1, 0x02, 0xc0, 0xc0, +}; + struct sony_sc { unsigned long quirks; }; @@ -43,9 +65,19 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, { struct sony_sc *sc = hid_get_drvdata(hdev); - if ((sc->quirks & VAIO_RDESC_CONSTANT) && - *rsize >= 56 && rdesc[54] == 0x81 && rdesc[55] == 0x07) { - hid_info(hdev, "Fixing up Sony Vaio VGX report descriptor\n"); + /* + * Some Sony RF receivers wrongly declare the mouse pointer as a + * a constant non-data variable. + */ + if ((sc->quirks & VAIO_RDESC_CONSTANT) && *rsize >= 56 && + /* usage page: generic desktop controls */ + /* rdesc[0] == 0x05 && rdesc[1] == 0x01 && */ + /* usage: mouse */ + rdesc[2] == 0x09 && rdesc[3] == 0x02 && + /* input (usage page for x,y axes): constant, variable, relative */ + rdesc[54] == 0x81 && rdesc[55] == 0x07) { + hid_info(hdev, "Fixing up Sony RF Receiver report descriptor\n"); + /* input: data, variable, relative */ rdesc[55] = 0x06; } @@ -56,6 +88,12 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, hid_info(hdev, "Fixing up Sony Sixaxis report descriptor\n"); memcpy((void *)&rdesc[83], (void *)&sixaxis_rdesc_fixup, sizeof(sixaxis_rdesc_fixup)); + } else if (sc->quirks & SIXAXIS_CONTROLLER_USB && + *rsize > sizeof(sixaxis_rdesc_fixup2)) { + hid_info(hdev, "Sony Sixaxis clone detected. Using original report descriptor (size: %d clone; %d new)\n", + *rsize, (int)sizeof(sixaxis_rdesc_fixup2)); + *rsize = sizeof(sixaxis_rdesc_fixup2); + memcpy(rdesc, &sixaxis_rdesc_fixup2, *rsize); } return rdesc; } @@ -217,6 +255,8 @@ static const struct hid_device_id sony_devices[] = { .driver_data = SIXAXIS_CONTROLLER_BT }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE), .driver_data = VAIO_RDESC_CONSTANT }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE), + .driver_data = VAIO_RDESC_CONSTANT }, { } }; MODULE_DEVICE_TABLE(hid, sony_devices); @@ -229,17 +269,6 @@ static struct hid_driver sony_driver = { .report_fixup = sony_report_fixup, .raw_event = sony_raw_event }; +module_hid_driver(sony_driver); -static int __init sony_init(void) -{ - return hid_register_driver(&sony_driver); -} - -static void __exit sony_exit(void) -{ - hid_unregister_driver(&sony_driver); -} - -module_init(sony_init); -module_exit(sony_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-speedlink.c b/drivers/hid/hid-speedlink.c index 602013741718..e94371a059cb 100644 --- a/drivers/hid/hid-speedlink.c +++ b/drivers/hid/hid-speedlink.c @@ -73,17 +73,6 @@ static struct hid_driver speedlink_driver = { .input_mapping = speedlink_input_mapping, .event = speedlink_event, }; +module_hid_driver(speedlink_driver); -static int __init speedlink_init(void) -{ - return hid_register_driver(&speedlink_driver); -} - -static void __exit speedlink_exit(void) -{ - hid_unregister_driver(&speedlink_driver); -} - -module_init(speedlink_init); -module_exit(speedlink_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c new file mode 100644 index 000000000000..2ed995cda44a --- /dev/null +++ b/drivers/hid/hid-steelseries.c @@ -0,0 +1,393 @@ +/* + * HID driver for Steelseries SRW-S1 + * + * Copyright (c) 2013 Simon Wood + */ + +/* + * 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/usb.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "usbhid/usbhid.h" +#include "hid-ids.h" + +#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) +#define SRWS1_NUMBER_LEDS 15 +struct steelseries_srws1_data { + __u16 led_state; + /* the last element is used for setting all leds simultaneously */ + struct led_classdev *led[SRWS1_NUMBER_LEDS + 1]; +}; +#endif + +/* Fixed report descriptor for Steelseries SRW-S1 wheel controller + * + * The original descriptor hides the sensitivity and assists dials + * a custom vendor usage page. This inserts a patch to make them + * appear in the 'Generic Desktop' usage. + */ + +static __u8 steelseries_srws1_rdesc_fixed[] = { +0x05, 0x01, /* Usage Page (Desktop) */ +0x09, 0x08, /* Usage (MultiAxis), Changed */ +0xA1, 0x01, /* Collection (Application), */ +0xA1, 0x02, /* Collection (Logical), */ +0x95, 0x01, /* Report Count (1), */ +0x05, 0x01, /* Changed Usage Page (Desktop), */ +0x09, 0x30, /* Changed Usage (X), */ +0x16, 0xF8, 0xF8, /* Logical Minimum (-1800), */ +0x26, 0x08, 0x07, /* Logical Maximum (1800), */ +0x65, 0x14, /* Unit (Degrees), */ +0x55, 0x0F, /* Unit Exponent (15), */ +0x75, 0x10, /* Report Size (16), */ +0x81, 0x02, /* Input (Variable), */ +0x09, 0x31, /* Changed Usage (Y), */ +0x15, 0x00, /* Logical Minimum (0), */ +0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ +0x75, 0x0C, /* Report Size (12), */ +0x81, 0x02, /* Input (Variable), */ +0x09, 0x32, /* Changed Usage (Z), */ +0x15, 0x00, /* Logical Minimum (0), */ +0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ +0x75, 0x0C, /* Report Size (12), */ +0x81, 0x02, /* Input (Variable), */ +0x05, 0x01, /* Usage Page (Desktop), */ +0x09, 0x39, /* Usage (Hat Switch), */ +0x25, 0x07, /* Logical Maximum (7), */ +0x35, 0x00, /* Physical Minimum (0), */ +0x46, 0x3B, 0x01, /* Physical Maximum (315), */ +0x65, 0x14, /* Unit (Degrees), */ +0x75, 0x04, /* Report Size (4), */ +0x95, 0x01, /* Report Count (1), */ +0x81, 0x02, /* Input (Variable), */ +0x25, 0x01, /* Logical Maximum (1), */ +0x45, 0x01, /* Physical Maximum (1), */ +0x65, 0x00, /* Unit, */ +0x75, 0x01, /* Report Size (1), */ +0x95, 0x03, /* Report Count (3), */ +0x81, 0x01, /* Input (Constant), */ +0x05, 0x09, /* Usage Page (Button), */ +0x19, 0x01, /* Usage Minimum (01h), */ +0x29, 0x11, /* Usage Maximum (11h), */ +0x95, 0x11, /* Report Count (17), */ +0x81, 0x02, /* Input (Variable), */ + /* ---- Dial patch starts here ---- */ +0x05, 0x01, /* Usage Page (Desktop), */ +0x09, 0x33, /* Usage (RX), */ +0x75, 0x04, /* Report Size (4), */ +0x95, 0x02, /* Report Count (2), */ +0x15, 0x00, /* Logical Minimum (0), */ +0x25, 0x0b, /* Logical Maximum (b), */ +0x81, 0x02, /* Input (Variable), */ +0x09, 0x35, /* Usage (RZ), */ +0x75, 0x04, /* Report Size (4), */ +0x95, 0x01, /* Report Count (1), */ +0x25, 0x03, /* Logical Maximum (3), */ +0x81, 0x02, /* Input (Variable), */ + /* ---- Dial patch ends here ---- */ +0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ +0x09, 0x01, /* Usage (01h), */ +0x75, 0x04, /* Changed Report Size (4), */ +0x95, 0x0D, /* Changed Report Count (13), */ +0x81, 0x02, /* Input (Variable), */ +0xC0, /* End Collection, */ +0xA1, 0x02, /* Collection (Logical), */ +0x09, 0x02, /* Usage (02h), */ +0x75, 0x08, /* Report Size (8), */ +0x95, 0x10, /* Report Count (16), */ +0x91, 0x02, /* Output (Variable), */ +0xC0, /* End Collection, */ +0xC0 /* End Collection */ +}; + +#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) +static void steelseries_srws1_set_leds(struct hid_device *hdev, __u16 leds) +{ + struct list_head *report_list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + __s32 *value = report->field[0]->value; + + value[0] = 0x40; + value[1] = leds & 0xFF; + value[2] = leds >> 8; + value[3] = 0x00; + value[4] = 0x00; + value[5] = 0x00; + value[6] = 0x00; + value[7] = 0x00; + value[8] = 0x00; + value[9] = 0x00; + value[10] = 0x00; + value[11] = 0x00; + value[12] = 0x00; + value[13] = 0x00; + value[14] = 0x00; + value[15] = 0x00; + + usbhid_submit_report(hdev, report, USB_DIR_OUT); + + /* Note: LED change does not show on device until the device is read/polled */ +} + +static void steelseries_srws1_led_all_set_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct device *dev = led_cdev->dev->parent; + struct hid_device *hid = container_of(dev, struct hid_device, dev); + struct steelseries_srws1_data *drv_data = hid_get_drvdata(hid); + + if (!drv_data) { + hid_err(hid, "Device data not found."); + return; + } + + if (value == LED_OFF) + drv_data->led_state = 0; + else + drv_data->led_state = (1 << (SRWS1_NUMBER_LEDS + 1)) - 1; + + steelseries_srws1_set_leds(hid, drv_data->led_state); +} + +static enum led_brightness steelseries_srws1_led_all_get_brightness(struct led_classdev *led_cdev) +{ + struct device *dev = led_cdev->dev->parent; + struct hid_device *hid = container_of(dev, struct hid_device, dev); + struct steelseries_srws1_data *drv_data; + + drv_data = hid_get_drvdata(hid); + + if (!drv_data) { + hid_err(hid, "Device data not found."); + return LED_OFF; + } + + return (drv_data->led_state >> SRWS1_NUMBER_LEDS) ? LED_FULL : LED_OFF; +} + +static void steelseries_srws1_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct device *dev = led_cdev->dev->parent; + struct hid_device *hid = container_of(dev, struct hid_device, dev); + struct steelseries_srws1_data *drv_data = hid_get_drvdata(hid); + int i, state = 0; + + if (!drv_data) { + hid_err(hid, "Device data not found."); + return; + } + + for (i = 0; i < SRWS1_NUMBER_LEDS; i++) { + if (led_cdev != drv_data->led[i]) + continue; + + state = (drv_data->led_state >> i) & 1; + if (value == LED_OFF && state) { + drv_data->led_state &= ~(1 << i); + steelseries_srws1_set_leds(hid, drv_data->led_state); + } else if (value != LED_OFF && !state) { + drv_data->led_state |= 1 << i; + steelseries_srws1_set_leds(hid, drv_data->led_state); + } + break; + } +} + +static enum led_brightness steelseries_srws1_led_get_brightness(struct led_classdev *led_cdev) +{ + struct device *dev = led_cdev->dev->parent; + struct hid_device *hid = container_of(dev, struct hid_device, dev); + struct steelseries_srws1_data *drv_data; + int i, value = 0; + + drv_data = hid_get_drvdata(hid); + + if (!drv_data) { + hid_err(hid, "Device data not found."); + return LED_OFF; + } + + for (i = 0; i < SRWS1_NUMBER_LEDS; i++) + if (led_cdev == drv_data->led[i]) { + value = (drv_data->led_state >> i) & 1; + break; + } + + return value ? LED_FULL : LED_OFF; +} + +static int steelseries_srws1_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret, i; + struct led_classdev *led; + size_t name_sz; + char *name; + + struct steelseries_srws1_data *drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL); + + if (drv_data == NULL) { + hid_err(hdev, "can't alloc SRW-S1 memory\n"); + return -ENOMEM; + } + + hid_set_drvdata(hdev, drv_data); + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + goto err_free; + } + + /* register led subsystem */ + drv_data->led_state = 0; + for (i = 0; i < SRWS1_NUMBER_LEDS + 1; i++) + drv_data->led[i] = NULL; + + steelseries_srws1_set_leds(hdev, 0); + + name_sz = strlen(hdev->uniq) + 16; + + /* 'ALL', for setting all LEDs simultaneously */ + led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); + if (!led) { + hid_err(hdev, "can't allocate memory for LED ALL\n"); + goto err_led; + } + + name = (void *)(&led[1]); + snprintf(name, name_sz, "SRWS1::%s::RPMALL", hdev->uniq); + led->name = name; + led->brightness = 0; + led->max_brightness = 1; + led->brightness_get = steelseries_srws1_led_all_get_brightness; + led->brightness_set = steelseries_srws1_led_all_set_brightness; + + drv_data->led[SRWS1_NUMBER_LEDS] = led; + ret = led_classdev_register(&hdev->dev, led); + if (ret) + goto err_led; + + /* Each individual LED */ + for (i = 0; i < SRWS1_NUMBER_LEDS; i++) { + led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); + if (!led) { + hid_err(hdev, "can't allocate memory for LED %d\n", i); + goto err_led; + } + + name = (void *)(&led[1]); + snprintf(name, name_sz, "SRWS1::%s::RPM%d", hdev->uniq, i+1); + led->name = name; + led->brightness = 0; + led->max_brightness = 1; + led->brightness_get = steelseries_srws1_led_get_brightness; + led->brightness_set = steelseries_srws1_led_set_brightness; + + drv_data->led[i] = led; + ret = led_classdev_register(&hdev->dev, led); + + if (ret) { + hid_err(hdev, "failed to register LED %d. Aborting.\n", i); +err_led: + /* Deregister all LEDs (if any) */ + for (i = 0; i < SRWS1_NUMBER_LEDS + 1; i++) { + led = drv_data->led[i]; + drv_data->led[i] = NULL; + if (!led) + continue; + led_classdev_unregister(led); + kfree(led); + } + goto out; /* but let the driver continue without LEDs */ + } + } +out: + return 0; +err_free: + kfree(drv_data); + return ret; +} + +static void steelseries_srws1_remove(struct hid_device *hdev) +{ + int i; + struct led_classdev *led; + + struct steelseries_srws1_data *drv_data = hid_get_drvdata(hdev); + + if (drv_data) { + /* Deregister LEDs (if any) */ + for (i = 0; i < SRWS1_NUMBER_LEDS + 1; i++) { + led = drv_data->led[i]; + drv_data->led[i] = NULL; + if (!led) + continue; + led_classdev_unregister(led); + kfree(led); + } + + } + + hid_hw_stop(hdev); + kfree(drv_data); + return; +} +#endif + +static __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + if (*rsize >= 115 && rdesc[11] == 0x02 && rdesc[13] == 0xc8 + && rdesc[29] == 0xbb && rdesc[40] == 0xc5) { + hid_info(hdev, "Fixing up Steelseries SRW-S1 report descriptor\n"); + rdesc = steelseries_srws1_rdesc_fixed; + *rsize = sizeof(steelseries_srws1_rdesc_fixed); + } + return rdesc; +} + +static const struct hid_device_id steelseries_srws1_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, + { } +}; +MODULE_DEVICE_TABLE(hid, steelseries_srws1_devices); + +static struct hid_driver steelseries_srws1_driver = { + .name = "steelseries_srws1", + .id_table = steelseries_srws1_devices, +#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) + .probe = steelseries_srws1_probe, + .remove = steelseries_srws1_remove, +#endif + .report_fixup = steelseries_srws1_report_fixup +}; + +static int __init steelseries_srws1_init(void) +{ + return hid_register_driver(&steelseries_srws1_driver); +} + +static void __exit steelseries_srws1_exit(void) +{ + hid_unregister_driver(&steelseries_srws1_driver); +} + +module_init(steelseries_srws1_init); +module_exit(steelseries_srws1_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c index 45b4b066a262..87fc91e1c8de 100644 --- a/drivers/hid/hid-sunplus.c +++ b/drivers/hid/hid-sunplus.c @@ -63,17 +63,6 @@ static struct hid_driver sp_driver = { .report_fixup = sp_report_fixup, .input_mapping = sp_input_mapping, }; +module_hid_driver(sp_driver); -static int __init sp_init(void) -{ - return hid_register_driver(&sp_driver); -} - -static void __exit sp_exit(void) -{ - hid_unregister_driver(&sp_driver); -} - -module_init(sp_init); -module_exit(sp_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c new file mode 100644 index 000000000000..2055a52e9a20 --- /dev/null +++ b/drivers/hid/hid-thingm.c @@ -0,0 +1,272 @@ +/* + * ThingM blink(1) USB RGB LED driver + * + * Copyright 2013 Savoir-faire Linux Inc. + * Vivien Didelot <vivien.didelot@savoirfairelinux.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, version 2. + */ + +#include <linux/hid.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/usb.h> + +#include "hid-ids.h" + +#define BLINK1_CMD_SIZE 9 + +#define blink1_rgb_to_r(rgb) ((rgb & 0xFF0000) >> 16) +#define blink1_rgb_to_g(rgb) ((rgb & 0x00FF00) >> 8) +#define blink1_rgb_to_b(rgb) ((rgb & 0x0000FF) >> 0) + +/** + * struct blink1_data - blink(1) device specific data + * @hdev: HID device. + * @led_cdev: LED class instance. + * @rgb: 8-bit per channel RGB notation. + * @fade: fade time in hundredths of a second. + * @brightness: brightness coefficient. + * @play: play/pause in-memory patterns. + */ +struct blink1_data { + struct hid_device *hdev; + struct led_classdev led_cdev; + u32 rgb; + u16 fade; + u8 brightness; + bool play; +}; + +static int blink1_send_command(struct blink1_data *data, + u8 buf[BLINK1_CMD_SIZE]) +{ + int ret; + + hid_dbg(data->hdev, "command: %d%c%.2x%.2x%.2x%.2x%.2x%.2x%.2x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], + buf[5], buf[6], buf[7], buf[8]); + + ret = data->hdev->hid_output_raw_report(data->hdev, buf, + BLINK1_CMD_SIZE, HID_FEATURE_REPORT); + + return ret < 0 ? ret : 0; +} + +static int blink1_update_color(struct blink1_data *data) +{ + u8 buf[BLINK1_CMD_SIZE] = { 1, 'n', 0, 0, 0, 0, 0, 0, 0 }; + + if (data->brightness) { + unsigned int coef = DIV_ROUND_CLOSEST(255, data->brightness); + + buf[2] = DIV_ROUND_CLOSEST(blink1_rgb_to_r(data->rgb), coef); + buf[3] = DIV_ROUND_CLOSEST(blink1_rgb_to_g(data->rgb), coef); + buf[4] = DIV_ROUND_CLOSEST(blink1_rgb_to_b(data->rgb), coef); + } + + if (data->fade) { + buf[1] = 'c'; + buf[5] = (data->fade & 0xFF00) >> 8; + buf[6] = (data->fade & 0x00FF); + } + + return blink1_send_command(data, buf); +} + +static void blink1_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent); + + data->brightness = brightness; + if (blink1_update_color(data)) + hid_err(data->hdev, "failed to update color\n"); +} + +static enum led_brightness blink1_led_get(struct led_classdev *led_cdev) +{ + struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent); + + return data->brightness; +} + +static ssize_t blink1_show_rgb(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct blink1_data *data = dev_get_drvdata(dev->parent); + + return sprintf(buf, "%.6X\n", data->rgb); +} + +static ssize_t blink1_store_rgb(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct blink1_data *data = dev_get_drvdata(dev->parent); + long unsigned int rgb; + int ret; + + ret = kstrtoul(buf, 16, &rgb); + if (ret) + return ret; + + /* RGB triplet notation is 24-bit hexadecimal */ + if (rgb > 0xFFFFFF) + return -EINVAL; + + data->rgb = rgb; + ret = blink1_update_color(data); + + return ret ? ret : count; +} + +static DEVICE_ATTR(rgb, S_IRUGO | S_IWUSR, blink1_show_rgb, blink1_store_rgb); + +static ssize_t blink1_show_fade(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct blink1_data *data = dev_get_drvdata(dev->parent); + + return sprintf(buf, "%d\n", data->fade * 10); +} + +static ssize_t blink1_store_fade(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct blink1_data *data = dev_get_drvdata(dev->parent); + long unsigned int fade; + int ret; + + ret = kstrtoul(buf, 10, &fade); + if (ret) + return ret; + + /* blink(1) accepts 16-bit fade time, number of 10ms ticks */ + fade = DIV_ROUND_CLOSEST(fade, 10); + if (fade > 65535) + return -EINVAL; + + data->fade = fade; + + return count; +} + +static DEVICE_ATTR(fade, S_IRUGO | S_IWUSR, + blink1_show_fade, blink1_store_fade); + +static ssize_t blink1_show_play(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct blink1_data *data = dev_get_drvdata(dev->parent); + + return sprintf(buf, "%d\n", data->play); +} + +static ssize_t blink1_store_play(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct blink1_data *data = dev_get_drvdata(dev->parent); + u8 cmd[BLINK1_CMD_SIZE] = { 1, 'p', 0, 0, 0, 0, 0, 0, 0 }; + long unsigned int play; + int ret; + + ret = kstrtoul(buf, 10, &play); + if (ret) + return ret; + + data->play = !!play; + cmd[2] = data->play; + ret = blink1_send_command(data, cmd); + + return ret ? ret : count; +} + +static DEVICE_ATTR(play, S_IRUGO | S_IWUSR, + blink1_show_play, blink1_store_play); + +static const struct attribute_group blink1_sysfs_group = { + .attrs = (struct attribute *[]) { + &dev_attr_rgb.attr, + &dev_attr_fade.attr, + &dev_attr_play.attr, + NULL + }, +}; + +static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct blink1_data *data; + struct led_classdev *led; + char led_name[13]; + int ret; + + data = devm_kzalloc(&hdev->dev, sizeof(struct blink1_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + hid_set_drvdata(hdev, data); + data->hdev = hdev; + data->rgb = 0xFFFFFF; /* set a default white color */ + + ret = hid_parse(hdev); + if (ret) + goto error; + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) + goto error; + + /* blink(1) serial numbers range is 0x1A001000 to 0x1A002FFF */ + led = &data->led_cdev; + snprintf(led_name, sizeof(led_name), "blink1::%s", hdev->uniq + 4); + led->name = led_name; + led->brightness_set = blink1_led_set; + led->brightness_get = blink1_led_get; + ret = led_classdev_register(&hdev->dev, led); + if (ret) + goto stop; + + ret = sysfs_create_group(&led->dev->kobj, &blink1_sysfs_group); + if (ret) + goto remove_led; + + return 0; + +remove_led: + led_classdev_unregister(led); +stop: + hid_hw_stop(hdev); +error: + return ret; +} + +static void thingm_remove(struct hid_device *hdev) +{ + struct blink1_data *data = hid_get_drvdata(hdev); + struct led_classdev *led = &data->led_cdev; + + sysfs_remove_group(&led->dev->kobj, &blink1_sysfs_group); + led_classdev_unregister(led); + hid_hw_stop(hdev); +} + +static const struct hid_device_id thingm_table[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) }, + { } +}; +MODULE_DEVICE_TABLE(hid, thingm_table); + +static struct hid_driver thingm_driver = { + .name = "thingm", + .probe = thingm_probe, + .remove = thingm_remove, + .id_table = thingm_table, +}; + +module_hid_driver(thingm_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vivien Didelot <vivien.didelot@savoirfairelinux.com>"); +MODULE_DESCRIPTION("ThingM blink(1) USB RGB LED driver"); diff --git a/drivers/hid/hid-tivo.c b/drivers/hid/hid-tivo.c index 9f85f827607f..d790d8d71f7f 100644 --- a/drivers/hid/hid-tivo.c +++ b/drivers/hid/hid-tivo.c @@ -73,18 +73,7 @@ static struct hid_driver tivo_driver = { .id_table = tivo_devices, .input_mapping = tivo_input_mapping, }; +module_hid_driver(tivo_driver); -static int __init tivo_init(void) -{ - return hid_register_driver(&tivo_driver); -} - -static void __exit tivo_exit(void) -{ - hid_unregister_driver(&tivo_driver); -} - -module_init(tivo_init); -module_exit(tivo_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c index 83a933b9c2e9..e4fcf3f702a5 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -261,17 +261,6 @@ static struct hid_driver tm_driver = { .id_table = tm_devices, .probe = tm_probe, }; +module_hid_driver(tm_driver); -static int __init tm_init(void) -{ - return hid_register_driver(&tm_driver); -} - -static void __exit tm_exit(void) -{ - hid_unregister_driver(&tm_driver); -} - -module_init(tm_init); -module_exit(tm_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-topseed.c b/drivers/hid/hid-topseed.c index 613ff7b1d746..8a5b843e9dd6 100644 --- a/drivers/hid/hid-topseed.c +++ b/drivers/hid/hid-topseed.c @@ -76,17 +76,6 @@ static struct hid_driver ts_driver = { .id_table = ts_devices, .input_mapping = ts_input_mapping, }; +module_hid_driver(ts_driver); -static int __init ts_init(void) -{ - return hid_register_driver(&ts_driver); -} - -static void __exit ts_exit(void) -{ - hid_unregister_driver(&ts_driver); -} - -module_init(ts_init); -module_exit(ts_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-twinhan.c b/drivers/hid/hid-twinhan.c index f23456b1fd4b..c08c36443f83 100644 --- a/drivers/hid/hid-twinhan.c +++ b/drivers/hid/hid-twinhan.c @@ -131,17 +131,6 @@ static struct hid_driver twinhan_driver = { .id_table = twinhan_devices, .input_mapping = twinhan_input_mapping, }; +module_hid_driver(twinhan_driver); -static int __init twinhan_init(void) -{ - return hid_register_driver(&twinhan_driver); -} - -static void __exit twinhan_exit(void) -{ - hid_unregister_driver(&twinhan_driver); -} - -module_init(twinhan_init); -module_exit(twinhan_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c index 2e56a1fd2375..fb8b516ff0ed 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic.c @@ -650,17 +650,6 @@ static struct hid_driver uclogic_driver = { .id_table = uclogic_devices, .report_fixup = uclogic_report_fixup, }; +module_hid_driver(uclogic_driver); -static int __init uclogic_init(void) -{ - return hid_register_driver(&uclogic_driver); -} - -static void __exit uclogic_exit(void) -{ - hid_unregister_driver(&uclogic_driver); -} - -module_init(uclogic_init); -module_exit(uclogic_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 2f60da9ed066..a4a8bb0da688 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -953,23 +953,7 @@ static struct hid_driver wacom_driver = { .raw_event = wacom_raw_event, .input_mapped = wacom_input_mapped, }; +module_hid_driver(wacom_driver); -static int __init wacom_init(void) -{ - int ret; - - ret = hid_register_driver(&wacom_driver); - if (ret) - pr_err("can't register wacom driver\n"); - return ret; -} - -static void __exit wacom_exit(void) -{ - hid_unregister_driver(&wacom_driver); -} - -module_init(wacom_init); -module_exit(wacom_exit); MODULE_DESCRIPTION("Driver for Wacom Graphire Bluetooth and Wacom Intuos4 WL"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-waltop.c b/drivers/hid/hid-waltop.c index bb536ab5941e..059931d7b392 100644 --- a/drivers/hid/hid-waltop.c +++ b/drivers/hid/hid-waltop.c @@ -779,17 +779,6 @@ static struct hid_driver waltop_driver = { .report_fixup = waltop_report_fixup, .raw_event = waltop_raw_event, }; +module_hid_driver(waltop_driver); -static int __init waltop_init(void) -{ - return hid_register_driver(&waltop_driver); -} - -static void __exit waltop_exit(void) -{ - hid_unregister_driver(&waltop_driver); -} - -module_init(waltop_init); -module_exit(waltop_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 84e2fbec5fbb..0fb8ab93db68 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -1294,25 +1294,8 @@ static struct hid_driver wiimote_hid_driver = { .remove = wiimote_hid_remove, .raw_event = wiimote_hid_event, }; +module_hid_driver(wiimote_hid_driver); -static int __init wiimote_init(void) -{ - int ret; - - ret = hid_register_driver(&wiimote_hid_driver); - if (ret) - pr_err("Can't register wiimote hid driver\n"); - - return ret; -} - -static void __exit wiimote_exit(void) -{ - hid_unregister_driver(&wiimote_hid_driver); -} - -module_init(wiimote_init); -module_exit(wiimote_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>"); MODULE_DESCRIPTION(WIIMOTE_NAME " Device Driver"); diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c index eec329197c16..90124ffaa2a5 100644 --- a/drivers/hid/hid-wiimote-debug.c +++ b/drivers/hid/hid-wiimote-debug.c @@ -31,7 +31,7 @@ static ssize_t wiidebug_eeprom_read(struct file *f, char __user *u, size_t s, unsigned long flags; ssize_t ret; char buf[16]; - __u16 size; + __u16 size = 0; if (s == 0) return -EINVAL; diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index 38ae87772e96..0472191d4a72 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -403,14 +403,14 @@ static void handler_nunchuck(struct wiimote_ext *ext, const __u8 *payload) if (ext->motionp) { input_report_key(ext->input, - wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x04)); + wiiext_keymap[WIIEXT_KEY_Z], !(payload[5] & 0x04)); input_report_key(ext->input, - wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x08)); + wiiext_keymap[WIIEXT_KEY_C], !(payload[5] & 0x08)); } else { input_report_key(ext->input, - wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x01)); + wiiext_keymap[WIIEXT_KEY_Z], !(payload[5] & 0x01)); input_report_key(ext->input, - wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x02)); + wiiext_keymap[WIIEXT_KEY_C], !(payload[5] & 0x02)); } input_sync(ext->input); diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c index f6ba81df71bd..af66452592e9 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -152,17 +152,6 @@ static struct hid_driver zp_driver = { .id_table = zp_devices, .probe = zp_probe, }; +module_hid_driver(zp_driver); -static int __init zp_init(void) -{ - return hid_register_driver(&zp_driver); -} - -static void __exit zp_exit(void) -{ - hid_unregister_driver(&zp_driver); -} - -module_init(zp_init); -module_exit(zp_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-zydacron.c b/drivers/hid/hid-zydacron.c index 1ad85f2257b4..e4cddeccd6b5 100644 --- a/drivers/hid/hid-zydacron.c +++ b/drivers/hid/hid-zydacron.c @@ -219,17 +219,6 @@ static struct hid_driver zc_driver = { .probe = zc_probe, .remove = zc_remove, }; +module_hid_driver(zc_driver); -static int __init zc_init(void) -{ - return hid_register_driver(&zc_driver); -} - -static void __exit zc_exit(void) -{ - hid_unregister_driver(&zc_driver); -} - -module_init(zc_init); -module_exit(zc_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 413a73187d33..f3bbbce8353b 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -581,6 +581,7 @@ int __init hidraw_init(void) if (result < 0) goto error_class; + printk(KERN_INFO "hidraw: raw HID events driver (C) Jiri Kosina\n"); out: return result; diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index e766b5614ef5..ec7930217a6d 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -34,6 +34,7 @@ #include <linux/kernel.h> #include <linux/hid.h> #include <linux/mutex.h> +#include <linux/acpi.h> #include <linux/i2c/i2c-hid.h> @@ -139,6 +140,8 @@ struct i2c_hid { unsigned long flags; /* device flags */ wait_queue_head_t wait; /* For waiting the interrupt */ + + struct i2c_hid_platform_data pdata; }; static int __i2c_hid_command(struct i2c_client *client, @@ -821,6 +824,70 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) return 0; } +#ifdef CONFIG_ACPI +static int i2c_hid_acpi_pdata(struct i2c_client *client, + struct i2c_hid_platform_data *pdata) +{ + static u8 i2c_hid_guid[] = { + 0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45, + 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE, + }; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4], *obj; + struct acpi_object_list input; + struct acpi_device *adev; + acpi_handle handle; + + handle = ACPI_HANDLE(&client->dev); + if (!handle || acpi_bus_get_device(handle, &adev)) + return -ENODEV; + + input.count = ARRAY_SIZE(params); + input.pointer = params; + + params[0].type = ACPI_TYPE_BUFFER; + params[0].buffer.length = sizeof(i2c_hid_guid); + params[0].buffer.pointer = i2c_hid_guid; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = 1; + params[2].type = ACPI_TYPE_INTEGER; + params[2].integer.value = 1; /* HID function */ + params[3].type = ACPI_TYPE_INTEGER; + params[3].integer.value = 0; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DSM", &input, &buf))) { + dev_err(&client->dev, "device _DSM execution failed\n"); + return -ENODEV; + } + + obj = (union acpi_object *)buf.pointer; + if (obj->type != ACPI_TYPE_INTEGER) { + dev_err(&client->dev, "device _DSM returned invalid type: %d\n", + obj->type); + kfree(buf.pointer); + return -EINVAL; + } + + pdata->hid_descriptor_address = obj->integer.value; + + kfree(buf.pointer); + return 0; +} + +static const struct acpi_device_id i2c_hid_acpi_match[] = { + {"ACPI0C50", 0 }, + {"PNP0C50", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match); +#else +static inline int i2c_hid_acpi_pdata(struct i2c_client *client, + struct i2c_hid_platform_data *pdata) +{ + return -ENODEV; +} +#endif + static int i2c_hid_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) { @@ -832,11 +899,6 @@ static int i2c_hid_probe(struct i2c_client *client, dbg_hid("HID probe called for i2c 0x%02x\n", client->addr); - if (!platform_data) { - dev_err(&client->dev, "HID register address not provided\n"); - return -EINVAL; - } - if (!client->irq) { dev_err(&client->dev, "HID over i2c has not been provided an Int IRQ\n"); @@ -847,11 +909,22 @@ static int i2c_hid_probe(struct i2c_client *client, if (!ihid) return -ENOMEM; + if (!platform_data) { + ret = i2c_hid_acpi_pdata(client, &ihid->pdata); + if (ret) { + dev_err(&client->dev, + "HID register address not provided\n"); + goto err; + } + } else { + ihid->pdata = *platform_data; + } + i2c_set_clientdata(client, ihid); ihid->client = client; - hidRegister = platform_data->hid_descriptor_address; + hidRegister = ihid->pdata.hid_descriptor_address; ihid->wHIDDescRegister = cpu_to_le16(hidRegister); init_waitqueue_head(&ihid->wait); @@ -884,6 +957,7 @@ static int i2c_hid_probe(struct i2c_client *client, hid->hid_get_raw_report = i2c_hid_get_raw_report; hid->hid_output_raw_report = i2c_hid_output_raw_report; hid->dev.parent = &client->dev; + ACPI_HANDLE_SET(&hid->dev, ACPI_HANDLE(&client->dev)); hid->bus = BUS_I2C; hid->version = le16_to_cpu(ihid->hdesc.bcdVersion); hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); @@ -975,6 +1049,7 @@ static struct i2c_driver i2c_hid_driver = { .name = "i2c_hid", .owner = THIS_MODULE, .pm = &i2c_hid_pm, + .acpi_match_table = ACPI_PTR(i2c_hid_acpi_match), }, .probe = i2c_hid_probe, diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 714cd8cc9579..fc307e0422af 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -11,6 +11,7 @@ */ #include <linux/atomic.h> +#include <linux/compat.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/hid.h> @@ -276,6 +277,94 @@ static struct hid_ll_driver uhid_hid_driver = { .parse = uhid_hid_parse, }; +#ifdef CONFIG_COMPAT + +/* Apparently we haven't stepped on these rakes enough times yet. */ +struct uhid_create_req_compat { + __u8 name[128]; + __u8 phys[64]; + __u8 uniq[64]; + + compat_uptr_t rd_data; + __u16 rd_size; + + __u16 bus; + __u32 vendor; + __u32 product; + __u32 version; + __u32 country; +} __attribute__((__packed__)); + +static int uhid_event_from_user(const char __user *buffer, size_t len, + struct uhid_event *event) +{ + if (is_compat_task()) { + u32 type; + + if (get_user(type, buffer)) + return -EFAULT; + + if (type == UHID_CREATE) { + /* + * This is our messed up request with compat pointer. + * It is largish (more than 256 bytes) so we better + * allocate it from the heap. + */ + struct uhid_create_req_compat *compat; + + compat = kmalloc(sizeof(*compat), GFP_KERNEL); + if (!compat) + return -ENOMEM; + + buffer += sizeof(type); + len -= sizeof(type); + if (copy_from_user(compat, buffer, + min(len, sizeof(*compat)))) { + kfree(compat); + return -EFAULT; + } + + /* Shuffle the data over to proper structure */ + event->type = type; + + memcpy(event->u.create.name, compat->name, + sizeof(compat->name)); + memcpy(event->u.create.phys, compat->phys, + sizeof(compat->phys)); + memcpy(event->u.create.uniq, compat->uniq, + sizeof(compat->uniq)); + + event->u.create.rd_data = compat_ptr(compat->rd_data); + event->u.create.rd_size = compat->rd_size; + + event->u.create.bus = compat->bus; + event->u.create.vendor = compat->vendor; + event->u.create.product = compat->product; + event->u.create.version = compat->version; + event->u.create.country = compat->country; + + kfree(compat); + return 0; + } + /* All others can be copied directly */ + } + + if (copy_from_user(event, buffer, min(len, sizeof(*event)))) + return -EFAULT; + + return 0; +} +#else +static int uhid_event_from_user(const char __user *buffer, size_t len, + struct uhid_event *event) +{ + if (copy_from_user(event, buffer, min(len, sizeof(*event)))) + return -EFAULT; + + return 0; +} +#endif + static int uhid_dev_create(struct uhid_device *uhid, const struct uhid_event *ev) { @@ -498,10 +587,10 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, memset(&uhid->input_buf, 0, sizeof(uhid->input_buf)); len = min(count, sizeof(uhid->input_buf)); - if (copy_from_user(&uhid->input_buf, buffer, len)) { - ret = -EFAULT; + + ret = uhid_event_from_user(buffer, len, &uhid->input_buf); + if (ret) goto unlock; - } switch (uhid->input_buf.type) { case UHID_CREATE: diff --git a/include/linux/hid.h b/include/linux/hid.h index 7330a0fef0c0..e14b465b1146 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -589,6 +589,7 @@ struct hid_usage_id { * @raw_event: if report in report_table, this hook is called (NULL means nop) * @usage_table: on which events to call event (NULL means all) * @event: if usage in usage_table, this hook is called (NULL means nop) + * @report: this hook is called after parsing a report (NULL means nop) * @report_fixup: called before report descriptor parsing (NULL means nop) * @input_mapping: invoked on input registering before mapping an usage * @input_mapped: invoked on input registering after mapping an usage @@ -627,6 +628,7 @@ struct hid_driver { const struct hid_usage_id *usage_table; int (*event)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value); + void (*report)(struct hid_device *hdev, struct hid_report *report); __u8 *(*report_fixup)(struct hid_device *hdev, __u8 *buf, unsigned int *size); @@ -700,6 +702,18 @@ extern int __must_check __hid_register_driver(struct hid_driver *, extern void hid_unregister_driver(struct hid_driver *); +/** + * module_hid_driver() - Helper macro for registering a HID driver + * @__hid_driver: hid_driver struct + * + * Helper macro for HID drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_hid_driver(__hid_driver) \ + module_driver(__hid_driver, hid_register_driver, \ + hid_unregister_driver) + extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report); extern int hidinput_connect(struct hid_device *hid, unsigned int force); @@ -872,9 +886,6 @@ static inline int hid_hw_power(struct hid_device *hdev, int level) int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int interrupt); -extern int hid_generic_init(void); -extern void hid_generic_exit(void); - /* HID quirks API */ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct); int usbhid_quirks_init(char **quirks_param); diff --git a/include/uapi/linux/uhid.h b/include/uapi/linux/uhid.h index 9c6974f16966..e9ed951e2b09 100644 --- a/include/uapi/linux/uhid.h +++ b/include/uapi/linux/uhid.h @@ -86,7 +86,7 @@ struct uhid_feature_answer_req { __u16 err; __u16 size; __u8 data[UHID_DATA_MAX]; -}; +} __attribute__((__packed__)); struct uhid_event { __u32 type; |