diff options
Diffstat (limited to 'drivers/hid')
110 files changed, 13638 insertions, 4471 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index e7d6a13ec6a..5e79c6ad914 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -92,14 +92,14 @@ menu "Special HID drivers" config HID_A4TECH tristate "A4 tech mice" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for A4 tech X5 and WOP-35 / Trust 450L mice. config HID_ACRUX tristate "ACRUX game controller support" - depends on USB_HID + depends on HID ---help--- Say Y here if you want to enable support for ACRUX game controllers. @@ -113,7 +113,7 @@ config HID_ACRUX_FF config HID_APPLE tristate "Apple {i,Power,Mac}Books" if EXPERT - depends on (USB_HID || BT_HIDP) + depends on HID default !EXPERT ---help--- Support for some Apple devices which less or more break @@ -122,36 +122,47 @@ config HID_APPLE Say Y here if you want support for keyboards of Apple iBooks, PowerBooks, MacBooks, MacBook Pros and Apple Aluminum. +config HID_APPLEIR + tristate "Apple infrared receiver" + depends on (USB_HID) + ---help--- + Support for Apple infrared remote control. All the Apple computers from + 2005 onwards include such a port, except the unibody Macbook (2009), + and Mac Pros. This receiver is also used in the Apple TV set-top box + prior to the 2010 model. + + Say Y here if you want support for Apple infrared remote control. + config HID_AUREAL tristate "Aureal" - depends on USB_HID + depends on HID ---help--- Support for Aureal Cy se W-01RN Remote Controller and other Aureal derived remotes. config HID_BELKIN tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Belkin Flip KVM and Wireless keyboard. config HID_CHERRY tristate "Cherry Cymotion keyboard" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Cherry Cymotion keyboard. config HID_CHICONY tristate "Chicony Tactical pad" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Chicony Tactical pad. config HID_PRODIKEYS tristate "Prodikeys PC-MIDI Keyboard support" - depends on USB_HID && SND + depends on HID && SND select SND_RAWMIDI ---help--- Support for Prodikeys PC-MIDI Keyboard device support. @@ -164,16 +175,25 @@ config HID_PRODIKEYS multimedia keyboard, but will lack support for the musical keyboard and some additional multimedia keys. +config HID_CP2112 + tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support" + depends on USB_HID && I2C && GPIOLIB + ---help--- + Support for Silicon Labs CP2112 HID USB to SMBus Master Bridge. + This is a HID device driver which registers as an i2c adapter + and gpiochip to expose these functions of the CP2112. The + customizable USB descriptor fields are exposed as sysfs attributes. + config HID_CYPRESS tristate "Cypress mouse and barcode readers" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for cypress mouse and barcode readers. config HID_DRAGONRISE tristate "DragonRise Inc. game controller" - depends on USB_HID + depends on HID ---help--- Say Y here if you have DragonRise Inc. game controllers. These might be branded as: @@ -192,7 +212,7 @@ config DRAGONRISE_FF config HID_EMS_FF tristate "EMS Production Inc. force feedback support" - depends on USB_HID + depends on HID select INPUT_FF_MEMLESS ---help--- Say Y here if you want to enable force feedback support for devices by @@ -202,13 +222,20 @@ config HID_EMS_FF config HID_ELECOM tristate "ELECOM BM084 bluetooth mouse" - depends on BT_HIDP + depends on HID ---help--- Support for the ELECOM BM084 (bluetooth mouse). +config HID_ELO + tristate "ELO USB 4000/4500 touchscreen" + depends on USB_HID + ---help--- + Support for the ELO USB 4000/4500 touchscreens. Note that this is for + different devices than those handled by CONFIG_TOUCHSCREEN_USB_ELO. + config HID_EZKEY tristate "Ezkey BTC 8193 keyboard" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Ezkey BTC 8193 keyboard. @@ -220,6 +247,11 @@ config HID_HOLTEK Support for Holtek based devices: - Holtek On Line Grip based game controller - Trust GXT 18 Gaming Keyboard + - Sharkoon Drakonia / Perixx MX-2000 gaming mice + - Tracer Sniper TRM-503 / NOVA Gaming Slider X200 / + Zalman ZM-GM1 + - SHARKOON DarkGlider Gaming mouse + - LEETGION Hellion Gaming Mouse config HOLTEK_FF bool "Holtek On Line Grip force feedback support" @@ -229,9 +261,15 @@ config HOLTEK_FF Say Y here if you have a Holtek On Line Grip based game controller and want to have force feedback support for it. +config HID_HUION + tristate "Huion tablets" + depends on USB_HID + ---help--- + Support for Huion 580 tablet. + config HID_KEYTOUCH tristate "Keytouch HID devices" - depends on USB_HID + depends on HID ---help--- Support for Keytouch HID devices not fully compliant with the specification. Currently supported: @@ -239,7 +277,7 @@ config HID_KEYTOUCH config HID_KYE tristate "KYE/Genius devices" - depends on USB_HID + depends on HID ---help--- Support for KYE/Genius devices not fully compliant with HID standard: - Ergo Mouse @@ -249,25 +287,25 @@ config HID_KYE config HID_UCLOGIC tristate "UC-Logic" - depends on USB_HID + depends on HID ---help--- Support for UC-Logic tablets. config HID_WALTOP tristate "Waltop" - depends on USB_HID + depends on HID ---help--- Support for Waltop tablets. config HID_GYRATION tristate "Gyration remote control" - depends on USB_HID + depends on HID ---help--- Support for Gyration remote control. config HID_ICADE tristate "ION iCade arcade controller" - depends on BT_HIDP + depends on HID ---help--- Support for the ION iCade arcade controller to work as a joystick. @@ -276,26 +314,26 @@ config HID_ICADE config HID_TWINHAN tristate "Twinhan IR remote control" - depends on USB_HID + depends on HID ---help--- Support for Twinhan IR remote control. config HID_KENSINGTON tristate "Kensington Slimblade Trackball" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Kensington Slimblade Trackball. config HID_LCPOWER tristate "LC-Power" - depends on USB_HID + depends on HID ---help--- Support for LC-Power RC1000MCE RF remote control. config HID_LENOVO_TPKBD tristate "Lenovo ThinkPad USB Keyboard with TrackPoint" - depends on USB_HID + depends on HID select NEW_LEDS select LEDS_CLASS ---help--- @@ -308,19 +346,20 @@ config HID_LENOVO_TPKBD config HID_LOGITECH tristate "Logitech devices" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Logitech devices that are not fully compliant with HID standard. config HID_LOGITECH_DJ tristate "Logitech Unifying receivers full support" + depends on HIDRAW depends on HID_LOGITECH ---help--- Say Y if you want support for Logitech Unifying receivers and devices. Unifying receivers are capable of pairing up to 6 Logitech compliant devices to the same receiver. Without this driver it will be handled by - generic USB_HID driver and all incomming events will be multiplexed + generic USB_HID driver and all incoming events will be multiplexed into a single mouse and a single keyboard device. config LOGITECH_FF @@ -334,19 +373,20 @@ config LOGITECH_FF - Logitech WingMan Force 3D - Logitech Formula Force EX - Logitech WingMan Formula Force GP - - Logitech MOMO Force wheel and if you want to enable force feedback for them. Note: if you say N here, this device will still be supported, but without force feedback. config LOGIRUMBLEPAD2_FF - bool "Logitech RumblePad/Rumblepad 2 force feedback support" + bool "Logitech force feedback support (variant 2)" depends on HID_LOGITECH select INPUT_FF_MEMLESS help - Say Y here if you want to enable force feedback support for Logitech - RumblePad and Rumblepad 2 devices. + Say Y here if you want to enable force feedback support for: + - Logitech RumblePad + - Logitech Rumblepad 2 + - Logitech Formula Vibration Feedback Wheel config LOGIG940_FF bool "Logitech Flight System G940 force feedback support" @@ -373,31 +413,31 @@ config LOGIWHEELS_FF - Logitech Formula Force EX config HID_MAGICMOUSE - tristate "Apple MagicMouse multi-touch support" - depends on BT_HIDP + tristate "Apple Magic Mouse/Trackpad multi-touch support" + depends on HID ---help--- - Support for the Apple Magic Mouse multi-touch. + Support for the Apple Magic Mouse/Trackpad multi-touch. Say Y here if you want support for the multi-touch features of the - Apple Wireless "Magic" Mouse. + Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad. config HID_MICROSOFT tristate "Microsoft non-fully HID-compliant devices" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Microsoft devices that are not fully compliant with HID standard. config HID_MONTEREY tristate "Monterey Genius KB29E keyboard" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Monterey Genius KB29E. config HID_MULTITOUCH tristate "HID Multitouch panels" - depends on USB_HID + depends on HID ---help--- Generic support for HID multitouch panels. @@ -409,6 +449,7 @@ config HID_MULTITOUCH - Chunghwa panels - CVTouch panels - Cypress TrueTouch panels + - Elan Microelectronics touch panels - Elo TouchSystems IntelliTouch Plus panels - GeneralTouch 'Sensing Win7-TwoFinger' panels - GoodTouch panels @@ -425,9 +466,11 @@ config HID_MULTITOUCH - Pixcir dual touch panels - Quanta panels - eGalax dual-touch panels, including the Joojoo and Wetab tablets + - SiS multitouch panels - Stantum multitouch panels - Touch International Panels - Unitec Panels + - Wistron optical touch panels - XAT optical touch panels - Xiroku optical touch panels - Zytronic touch panels @@ -445,7 +488,7 @@ config HID_NTRIG config HID_ORTEK tristate "Ortek PKB-1700/WKB-2000/Skycable wireless keyboard and mouse trackpad" - depends on USB_HID + depends on HID ---help--- There are certain devices which have LogicalMaximum wrong in the keyboard usage page of their report descriptor. The most prevailing ones so far @@ -458,7 +501,7 @@ config HID_ORTEK config HID_PANTHERLORD tristate "Pantherlord/GreenAsia game controller" - depends on USB_HID + depends on HID ---help--- Say Y here if you have a PantherLord/GreenAsia based game controller or adapter. @@ -473,13 +516,13 @@ config PANTHERLORD_FF config HID_PETALYNX tristate "Petalynx Maxter remote control" - depends on USB_HID + depends on HID ---help--- Support for Petalynx Maxter remote control. config HID_PICOLCD tristate "PicoLCD (graphic version)" - depends on USB_HID + depends on HID ---help--- This provides support for Minibox PicoLCD devices, currently only the graphical ones are supported. @@ -545,20 +588,11 @@ config HID_PICOLCD_CIR config HID_PRIMAX tristate "Primax non-fully HID-compliant devices" - depends on USB_HID + depends on HID ---help--- Support for Primax devices that are not fully compliant with the HID standard. -config HID_PS3REMOTE - tristate "Sony PS3 BD Remote Control" - depends on BT_HIDP - ---help--- - Support for the Sony PS3 Blue-ray Disk Remote Control and Logitech - Harmony Adapter for PS3, which connect over Bluetooth. - - Support for the 6-axis controllers is provided by HID_SONY. - config HID_ROCCAT tristate "Roccat device support" depends on USB_HID @@ -569,42 +603,74 @@ config HID_ROCCAT config HID_SAITEK tristate "Saitek non-fully HID-compliant devices" - depends on USB_HID + depends on HID ---help--- Support for Saitek devices that are not fully compliant with the HID standard. - Currently only supports the PS1000 controller. + Supported devices: + - PS1000 Dual Analog Pad + - R.A.T.7 Gaming Mouse + - M.M.O.7 Gaming Mouse config HID_SAMSUNG tristate "Samsung InfraRed remote control or keyboards" - depends on USB_HID + depends on HID ---help--- Support for Samsung InfraRed remote control or keyboards. config HID_SONY - tristate "Sony PS3 controller" + tristate "Sony PS2/3/4 accessories" depends on USB_HID + depends on NEW_LEDS + depends on LEDS_CLASS + select POWER_SUPPLY ---help--- - Support for Sony PS3 6-axis controllers. + Support for - Support for the Sony PS3 BD Remote is provided by HID_PS3REMOTE. + * Sony PS3 6-axis controllers + * Sony PS4 DualShock 4 controllers + * Buzz controllers + * Sony PS3 Blue-ray Disk Remote Control (Bluetooth) + * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth) + +config SONY_FF + bool "Sony PS2/3/4 accessories force feedback support" + depends on HID_SONY + select INPUT_FF_MEMLESS + ---help--- + Say Y here if you have a Sony PS2/3/4 accessory and want to enable + force feedback support for it. config HID_SPEEDLINK tristate "Speedlink VAD Cezanne mouse support" - depends on USB_HID + depends on HID ---help--- Support for Speedlink Vicious and Divine Cezanne mouse. +config HID_STEELSERIES + tristate "Steelseries SRW-S1 steering wheel support" + depends on HID + ---help--- + Support for Steelseries SRW-S1 steering wheel + config HID_SUNPLUS tristate "Sunplus wireless desktop" - depends on USB_HID + depends on HID ---help--- Support for Sunplus wireless desktop. +config HID_RMI + tristate "Synaptics RMI4 device support" + depends on HID + ---help--- + Support for Synaptics RMI4 touchpads. + Say Y here if you have a Synaptics RMI4 touchpads over i2c-hid or usbhid + and want support for its special functionalities. + config HID_GREENASIA tristate "GreenAsia (Product ID 0x12) game controller support" - depends on USB_HID + depends on HID ---help--- Say Y here if you have a GreenAsia (Product ID 0x12) based game controller or adapter. @@ -626,7 +692,7 @@ config HID_HYPERV_MOUSE config HID_SMARTJOYPLUS tristate "SmartJoy PLUS PS2/USB adapter support" - depends on USB_HID + depends on HID ---help--- Support for SmartJoy PLUS PS2/USB adapter, Super Dual Box, Super Joy Box 3 Pro, Super Dual Box Pro, and Super Joy Box 5 Pro. @@ -644,20 +710,30 @@ config SMARTJOYPLUS_FF config HID_TIVO tristate "TiVo Slide Bluetooth remote control support" - depends on (USB_HID || BT_HIDP) + depends on HID ---help--- Say Y if you have a TiVo Slide Bluetooth remote control. config HID_TOPSEED tristate "TopSeed Cyberlink, BTC Emprex, Conceptronic remote control support" - depends on USB_HID + depends on HID ---help--- 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 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 + depends on HID ---help--- Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or a THRUSTMASTER Ferrari GT Rumble Wheel. @@ -673,33 +749,48 @@ config THRUSTMASTER_FF config HID_WACOM tristate "Wacom Bluetooth devices support" - depends on BT_HIDP + depends on HID depends on LEDS_CLASS select POWER_SUPPLY ---help--- Support for Wacom Graphire Bluetooth and Intuos4 WL tablets. config HID_WIIMOTE - tristate "Nintendo Wii Remote support" - depends on BT_HIDP + tristate "Nintendo Wii / Wii U peripherals" + depends on HID depends on LEDS_CLASS select POWER_SUPPLY select INPUT_FF_MEMLESS ---help--- - Support for the Nintendo Wii Remote bluetooth device. + Support for Nintendo Wii and Wii U Bluetooth peripherals. Supported + devices are the Wii Remote and its extension devices, but also devices + based on the Wii Remote like the Wii U Pro Controller or the + Wii Balance Board. + + Support for all official Nintendo extensions is available, however, 3rd + party extensions might not be supported. Please report these devices to: + http://github.com/dvdhrm/xwiimote/issues + + Other Nintendo Wii U peripherals that are IEEE 802.11 based (including + the Wii U Gamepad) might be supported in the future. But currently + support is limited to Bluetooth based devices. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called hid-wiimote. -config HID_WIIMOTE_EXT - bool "Nintendo Wii Remote Extension support" - depends on HID_WIIMOTE - default HID_WIIMOTE +config HID_XINMO + tristate "Xin-Mo non-fully compliant devices" + depends on HID ---help--- - Support for extension controllers of the Nintendo Wii Remote. Say yes - here if you want to use the Nintendo Motion+, Nunchuck or Classic - extension controllers with your Wii Remote. + Support for Xin-Mo devices that are not fully compliant with the HID + standard. Currently only supports the Xin-Mo Dual Arcade. Say Y here + if you have a Xin-Mo Dual Arcade controller. config HID_ZEROPLUS tristate "Zeroplus based game controller support" - depends on USB_HID + depends on HID ---help--- Say Y here if you have a Zeroplus based game controller. @@ -713,16 +804,16 @@ config ZEROPLUS_FF config HID_ZYDACRON tristate "Zydacron remote control support" - depends on USB_HID + depends on HID ---help--- Support for Zydacron remote control. config HID_SENSOR_HUB tristate "HID Sensors framework support" - depends on USB_HID + depends on HID && HAS_IOMEM select MFD_CORE default n - -- help--- + ---help--- Support for HID Sensor framework. This creates a MFD instance for a sensor hub and identifies all the sensors connected to it. Each sensor is registered as a MFD cell, so that sensor specific diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index b62215716b2..a6fa6baf368 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -28,10 +28,7 @@ ifdef CONFIG_LOGIWHEELS_FF hid-logitech-y += hid-lg4ff.o endif -hid-wiimote-y := hid-wiimote-core.o -ifdef CONFIG_HID_WIIMOTE_EXT - hid-wiimote-y += hid-wiimote-ext.o -endif +hid-wiimote-y := hid-wiimote-core.o hid-wiimote-modules.o ifdef CONFIG_DEBUG_FS hid-wiimote-y += hid-wiimote-debug.o endif @@ -39,18 +36,23 @@ endif obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_ACRUX) += hid-axff.o obj-$(CONFIG_HID_APPLE) += hid-apple.o +obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o obj-$(CONFIG_HID_AUREAL) += hid-aureal.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o +obj-$(CONFIG_HID_CP2112) += hid-cp2112.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o +obj-$(CONFIG_HID_ELO) += hid-elo.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o +obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o +obj-$(CONFIG_HID_HUION) += hid-huion.o obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_HID_ICADE) += hid-icade.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o @@ -91,23 +93,26 @@ hid-picolcd-y += hid-picolcd_debugfs.o endif obj-$(CONFIG_HID_PRIMAX) += hid-primax.o -obj-$(CONFIG_HID_PS3REMOTE) += hid-ps3remote.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ - hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-lua.o \ - hid-roccat-pyra.o hid-roccat-savu.o + hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \ + hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-ryos.o hid-roccat-savu.o +obj-$(CONFIG_HID_RMI) += hid-rmi.o obj-$(CONFIG_HID_SAITEK) += hid-saitek.o 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 obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o +obj-$(CONFIG_HID_XINMO) += hid-xinmo.o obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o obj-$(CONFIG_HID_WACOM) += hid-wacom.o diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index 0a239885e67..9428ea7cdf8 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c @@ -90,11 +90,10 @@ static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id) struct a4tech_sc *a4; int ret; - a4 = kzalloc(sizeof(*a4), GFP_KERNEL); + a4 = devm_kzalloc(&hdev->dev, sizeof(*a4), GFP_KERNEL); if (a4 == NULL) { hid_err(hdev, "can't alloc device descriptor\n"); - ret = -ENOMEM; - goto err_free; + return -ENOMEM; } a4->quirks = id->driver_data; @@ -104,27 +103,16 @@ static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err_free; + return ret; } ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); - goto err_free; + return ret; } return 0; -err_free: - kfree(a4); - return ret; -} - -static void a4_remove(struct hid_device *hdev) -{ - struct a4tech_sc *a4 = hid_get_drvdata(hdev); - - hid_hw_stop(hdev); - kfree(a4); } static const struct hid_device_id a4_devices[] = { @@ -144,19 +132,7 @@ static struct hid_driver a4_driver = { .input_mapped = a4_input_mapped, .event = a4_event, .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 d0f7662aacc..f822fd2a1ad 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -21,7 +21,6 @@ #include <linux/hid.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/usb.h> #include "hid-ids.h" @@ -47,6 +46,12 @@ module_param(iso_layout, uint, 0644); MODULE_PARM_DESC(iso_layout, "Enable/Disable hardcoded ISO-layout of the keyboard. " "(0 = disabled, [1] = enabled)"); +static unsigned int swap_opt_cmd; +module_param(swap_opt_cmd, uint, 0644); +MODULE_PARM_DESC(swap_opt_cmd, "Swap the Option (\"Alt\") and Command (\"Flag\") keys. " + "(For people who want to keep Windows PC keyboard muscle memory. " + "[0] = as-is, Mac layout. 1 = swapped, Windows layout.)"); + struct apple_sc { unsigned long quirks; unsigned int fn_on; @@ -151,6 +156,14 @@ static const struct apple_key_translation apple_iso_keyboard[] = { { } }; +static const struct apple_key_translation swapped_option_cmd_keys[] = { + { KEY_LEFTALT, KEY_LEFTMETA }, + { KEY_LEFTMETA, KEY_LEFTALT }, + { KEY_RIGHTALT, KEY_RIGHTMETA }, + { KEY_RIGHTMETA,KEY_RIGHTALT }, + { } +}; + static const struct apple_key_translation *apple_find_translation( const struct apple_key_translation *table, u16 from) { @@ -243,6 +256,14 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, } } + if (swap_opt_cmd) { + trans = apple_find_translation(swapped_option_cmd_keys, usage->code); + if (trans) { + input_event(input, usage->type, trans->to, value); + return 1; + } + } + return 0; } @@ -350,7 +371,7 @@ static int apple_probe(struct hid_device *hdev, unsigned int connect_mask = HID_CONNECT_DEFAULT; int ret; - asc = kzalloc(sizeof(*asc), GFP_KERNEL); + asc = devm_kzalloc(&hdev->dev, sizeof(*asc), GFP_KERNEL); if (asc == NULL) { hid_err(hdev, "can't alloc apple descriptor\n"); return -ENOMEM; @@ -363,7 +384,7 @@ static int apple_probe(struct hid_device *hdev, ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err_free; + return ret; } if (quirks & APPLE_HIDDEV) @@ -374,26 +395,13 @@ static int apple_probe(struct hid_device *hdev, ret = hid_hw_start(hdev, connect_mask); if (ret) { hid_err(hdev, "hw start failed\n"); - goto err_free; + return ret; } return 0; -err_free: - kfree(asc); - return ret; -} - -static void apple_remove(struct hid_device *hdev) -{ - hid_hw_stop(hdev); - kfree(hid_get_drvdata(hdev)); } static const struct hid_device_id apple_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL), - .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4), - .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE), .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL }, @@ -461,6 +469,9 @@ static const struct hid_device_id apple_devices[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), @@ -529,6 +540,12 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS), + .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), @@ -550,28 +567,10 @@ static struct hid_driver apple_driver = { .id_table = apple_devices, .report_fixup = apple_report_fixup, .probe = apple_probe, - .remove = apple_remove, .event = apple_event, .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-appleir.c b/drivers/hid/hid-appleir.c new file mode 100644 index 00000000000..0e6a42d37eb --- /dev/null +++ b/drivers/hid/hid-appleir.c @@ -0,0 +1,355 @@ +/* + * HID driver for the apple ir device + * + * Original driver written by James McKenzie + * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de> + * Updated to support newer remotes by Bastien Nocera <hadess@hadess.net> + * Ported to HID subsystem by Benjamin Tissoires <benjamin.tissoires@gmail.com> + * + * Copyright (C) 2006 James McKenzie + * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com> + * Copyright (C) 2008 Novell Inc. + * Copyright (C) 2010, 2012 Bastien Nocera <hadess@hadess.net> + * Copyright (C) 2013 Benjamin Tissoires <benjamin.tissoires@gmail.com> + * Copyright (C) 2013 Red Hat Inc. All Rights Reserved + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> +#include "hid-ids.h" + +MODULE_AUTHOR("James McKenzie"); +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@redhat.com>"); +MODULE_DESCRIPTION("HID Apple IR remote controls"); +MODULE_LICENSE("GPL"); + +#define KEY_MASK 0x0F +#define TWO_PACKETS_MASK 0x40 + +/* + * James McKenzie has two devices both of which report the following + * 25 87 ee 83 0a + + * 25 87 ee 83 0c - + * 25 87 ee 83 09 << + * 25 87 ee 83 06 >> + * 25 87 ee 83 05 >" + * 25 87 ee 83 03 menu + * 26 00 00 00 00 for key repeat + */ + +/* + * Thomas Glanzmann reports the following responses + * 25 87 ee ca 0b + + * 25 87 ee ca 0d - + * 25 87 ee ca 08 << + * 25 87 ee ca 07 >> + * 25 87 ee ca 04 >" + * 25 87 ee ca 02 menu + * 26 00 00 00 00 for key repeat + * + * He also observes the following event sometimes + * sent after a key is release, which I interpret + * as a flat battery message + * 25 87 e0 ca 06 flat battery + */ + +/* + * Alexandre Karpenko reports the following responses for Device ID 0x8242 + * 25 87 ee 47 0b + + * 25 87 ee 47 0d - + * 25 87 ee 47 08 << + * 25 87 ee 47 07 >> + * 25 87 ee 47 04 >" + * 25 87 ee 47 02 menu + * 26 87 ee 47 ** for key repeat (** is the code of the key being held) + */ + +/* + * Bastien Nocera's remote + * 25 87 ee 91 5f followed by + * 25 87 ee 91 05 gives you >" + * + * 25 87 ee 91 5c followed by + * 25 87 ee 91 05 gives you the middle button + */ + +/* + * Fabien Andre's remote + * 25 87 ee a3 5e followed by + * 25 87 ee a3 04 gives you >" + * + * 25 87 ee a3 5d followed by + * 25 87 ee a3 04 gives you the middle button + */ + +static const unsigned short appleir_key_table[] = { + KEY_RESERVED, + KEY_MENU, + KEY_PLAYPAUSE, + KEY_FORWARD, + KEY_BACK, + KEY_VOLUMEUP, + KEY_VOLUMEDOWN, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_ENTER, + KEY_PLAYPAUSE, + KEY_RESERVED, +}; + +struct appleir { + struct input_dev *input_dev; + struct hid_device *hid; + unsigned short keymap[ARRAY_SIZE(appleir_key_table)]; + struct timer_list key_up_timer; /* timer for key up */ + spinlock_t lock; /* protects .current_key */ + int current_key; /* the currently pressed key */ + int prev_key_idx; /* key index in a 2 packets message */ +}; + +static int get_key(int data) +{ + /* + * The key is coded accross bits 2..9: + * + * 0x00 or 0x01 ( ) key: 0 -> KEY_RESERVED + * 0x02 or 0x03 ( menu ) key: 1 -> KEY_MENU + * 0x04 or 0x05 ( >" ) key: 2 -> KEY_PLAYPAUSE + * 0x06 or 0x07 ( >> ) key: 3 -> KEY_FORWARD + * 0x08 or 0x09 ( << ) key: 4 -> KEY_BACK + * 0x0a or 0x0b ( + ) key: 5 -> KEY_VOLUMEUP + * 0x0c or 0x0d ( - ) key: 6 -> KEY_VOLUMEDOWN + * 0x0e or 0x0f ( ) key: 7 -> KEY_RESERVED + * 0x50 or 0x51 ( ) key: 8 -> KEY_RESERVED + * 0x52 or 0x53 ( ) key: 9 -> KEY_RESERVED + * 0x54 or 0x55 ( ) key: 10 -> KEY_RESERVED + * 0x56 or 0x57 ( ) key: 11 -> KEY_RESERVED + * 0x58 or 0x59 ( ) key: 12 -> KEY_RESERVED + * 0x5a or 0x5b ( ) key: 13 -> KEY_RESERVED + * 0x5c or 0x5d ( middle ) key: 14 -> KEY_ENTER + * 0x5e or 0x5f ( >" ) key: 15 -> KEY_PLAYPAUSE + * + * Packets starting with 0x5 are part of a two-packets message, + * we notify the caller by sending a negative value. + */ + int key = (data >> 1) & KEY_MASK; + + if ((data & TWO_PACKETS_MASK)) + /* Part of a 2 packets-command */ + key = -key; + + return key; +} + +static void key_up(struct hid_device *hid, struct appleir *appleir, int key) +{ + input_report_key(appleir->input_dev, key, 0); + input_sync(appleir->input_dev); +} + +static void key_down(struct hid_device *hid, struct appleir *appleir, int key) +{ + input_report_key(appleir->input_dev, key, 1); + input_sync(appleir->input_dev); +} + +static void battery_flat(struct appleir *appleir) +{ + dev_err(&appleir->input_dev->dev, "possible flat battery?\n"); +} + +static void key_up_tick(unsigned long data) +{ + struct appleir *appleir = (struct appleir *)data; + struct hid_device *hid = appleir->hid; + unsigned long flags; + + spin_lock_irqsave(&appleir->lock, flags); + if (appleir->current_key) { + key_up(hid, appleir, appleir->current_key); + appleir->current_key = 0; + } + spin_unlock_irqrestore(&appleir->lock, flags); +} + +static int appleir_raw_event(struct hid_device *hid, struct hid_report *report, + u8 *data, int len) +{ + struct appleir *appleir = hid_get_drvdata(hid); + static const u8 keydown[] = { 0x25, 0x87, 0xee }; + static const u8 keyrepeat[] = { 0x26, }; + static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 }; + unsigned long flags; + + if (len != 5) + goto out; + + if (!memcmp(data, keydown, sizeof(keydown))) { + int index; + + spin_lock_irqsave(&appleir->lock, flags); + /* + * If we already have a key down, take it up before marking + * this one down + */ + if (appleir->current_key) + key_up(hid, appleir, appleir->current_key); + + /* Handle dual packet commands */ + if (appleir->prev_key_idx > 0) + index = appleir->prev_key_idx; + else + index = get_key(data[4]); + + if (index >= 0) { + appleir->current_key = appleir->keymap[index]; + + key_down(hid, appleir, appleir->current_key); + /* + * Remote doesn't do key up, either pull them up, in + * the test above, or here set a timer which pulls + * them up after 1/8 s + */ + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + appleir->prev_key_idx = 0; + } else + /* Remember key for next packet */ + appleir->prev_key_idx = -index; + spin_unlock_irqrestore(&appleir->lock, flags); + goto out; + } + + appleir->prev_key_idx = 0; + + if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) { + key_down(hid, appleir, appleir->current_key); + /* + * Remote doesn't do key up, either pull them up, in the test + * above, or here set a timer which pulls them up after 1/8 s + */ + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + goto out; + } + + if (!memcmp(data, flatbattery, sizeof(flatbattery))) { + battery_flat(appleir); + /* Fall through */ + } + +out: + /* let hidraw and hiddev handle the report */ + return 0; +} + +static void appleir_input_configured(struct hid_device *hid, + struct hid_input *hidinput) +{ + struct input_dev *input_dev = hidinput->input; + struct appleir *appleir = hid_get_drvdata(hid); + int i; + + appleir->input_dev = input_dev; + + input_dev->keycode = appleir->keymap; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = ARRAY_SIZE(appleir->keymap); + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + + memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap)); + for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++) + set_bit(appleir->keymap[i], input_dev->keybit); + clear_bit(KEY_RESERVED, input_dev->keybit); +} + +static int appleir_input_mapping(struct hid_device *hid, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + return -1; +} + +static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id) +{ + int ret; + struct appleir *appleir; + + appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL); + if (!appleir) { + ret = -ENOMEM; + goto allocfail; + } + + appleir->hid = hid; + + /* force input as some remotes bypass the input registration */ + hid->quirks |= HID_QUIRK_HIDINPUT_FORCE; + + spin_lock_init(&appleir->lock); + setup_timer(&appleir->key_up_timer, + key_up_tick, (unsigned long) appleir); + + hid_set_drvdata(hid, appleir); + + ret = hid_parse(hid); + if (ret) { + hid_err(hid, "parse failed\n"); + goto fail; + } + + ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE); + if (ret) { + hid_err(hid, "hw start failed\n"); + goto fail; + } + + return 0; +fail: + kfree(appleir); +allocfail: + return ret; +} + +static void appleir_remove(struct hid_device *hid) +{ + struct appleir *appleir = hid_get_drvdata(hid); + hid_hw_stop(hid); + del_timer_sync(&appleir->key_up_timer); + kfree(appleir); +} + +static const struct hid_device_id appleir_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, + { } +}; +MODULE_DEVICE_TABLE(hid, appleir_devices); + +static struct hid_driver appleir_driver = { + .name = "appleir", + .id_table = appleir_devices, + .raw_event = appleir_raw_event, + .input_configured = appleir_input_configured, + .probe = appleir_probe, + .remove = appleir_remove, + .input_mapping = appleir_input_mapping, +}; +module_hid_driver(appleir_driver); diff --git a/drivers/hid/hid-aureal.c b/drivers/hid/hid-aureal.c index 7968187ddf7..340ba9d394a 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 5be858dd9a1..a594e478a1e 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -29,14 +29,12 @@ #include <linux/input.h> #include <linux/slab.h> -#include <linux/usb.h> #include <linux/hid.h> #include <linux/module.h> #include "hid-ids.h" #ifdef CONFIG_HID_ACRUX_FF -#include "usbhid/usbhid.h" struct axff_device { struct hid_report *report; @@ -68,7 +66,7 @@ static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect } dbg_hid("running with 0x%02x 0x%02x", left, right); - usbhid_submit_report(hid, axff->report, USB_DIR_OUT); + hid_hw_request(hid, axff->report, HID_REQ_SET_REPORT); return 0; } @@ -97,7 +95,7 @@ static int axff_init(struct hid_device *hid) } } - if (field_count < 4) { + if (field_count < 4 && hid->product != 0xf705) { hid_err(hid, "not enough fields in the report: %d\n", field_count); return -ENODEV; @@ -114,7 +112,7 @@ static int axff_init(struct hid_device *hid) goto err_free_mem; axff->report = report; - usbhid_submit_report(hid, axff->report, USB_DIR_OUT); + hid_hw_request(hid, axff->report, HID_REQ_SET_REPORT); hid_info(hid, "Force Feedback for ACRUX game controllers by Sergei Kolzun <x0r@dv-life.ru>\n"); @@ -182,6 +180,7 @@ static void ax_remove(struct hid_device *hdev) static const struct hid_device_id ax_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), }, + { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705), }, { } }; MODULE_DEVICE_TABLE(hid, ax_devices); @@ -192,19 +191,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 a1a5a12c3a6..cc4cf138bef 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 af034d3d925..1bdcccc54a1 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 a2abb8e1572..b613d5a7968 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 eb2ee11b641..8ed66fd1ea8 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -63,6 +63,8 @@ struct hid_report *hid_register_report(struct hid_device *device, unsigned type, struct hid_report_enum *report_enum = device->report_enum + type; struct hid_report *report; + if (id >= HID_MAX_IDS) + return NULL; if (report_enum->report_id_hash[id]) return report_enum->report_id_hash[id]; @@ -92,7 +94,6 @@ EXPORT_SYMBOL_GPL(hid_register_report); static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values) { struct hid_field *field; - int i; if (report->maxfield == HID_MAX_FIELDS) { hid_err(report->device, "too many fields in report\n"); @@ -111,9 +112,6 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned field->value = (s32 *)(field->usage + usages); field->report = report; - for (i = 0; i < usages; i++) - field->usage[i].usage_index = i; - return field; } @@ -224,9 +222,9 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign { struct hid_report *report; struct hid_field *field; - int usages; + unsigned usages; unsigned offset; - int i; + unsigned i; report = hid_register_report(parser->device, report_type, parser->global.report_id); if (!report) { @@ -253,7 +251,8 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign if (!parser->local.usage_index) /* Ignore padding fields */ return 0; - usages = max_t(int, parser->local.usage_index, parser->global.report_count); + usages = max_t(unsigned, parser->local.usage_index, + parser->global.report_count); field = hid_register_field(report, usages, parser->global.report_count); if (!field) @@ -264,13 +263,14 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); for (i = 0; i < usages; i++) { - int j = i; + unsigned j = i; /* Duplicate the last usage we parsed if we have excess values */ if (i >= parser->local.usage_index) j = parser->local.usage_index - 1; field->usage[i].hid = parser->local.usage[j]; field->usage[i].collection_index = parser->local.collection_index[j]; + field->usage[i].usage_index = i; } field->maxusage = usages; @@ -319,7 +319,7 @@ static s32 item_sdata(struct hid_item *item) static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) { - __u32 raw_value; + __s32 raw_value; switch (item->tag) { case HID_GLOBAL_ITEM_TAG_PUSH: @@ -370,10 +370,11 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) return 0; case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: - /* Units exponent negative numbers are given through a - * two's complement. - * See "6.2.2.7 Global Items" for more information. */ - raw_value = item_udata(item); + /* Many devices provide unit exponent as a two's complement + * nibble due to the common misunderstanding of HID + * specification 1.11, 6.2.2.7 Global Items. Attempt to handle + * both this and the standard encoding. */ + raw_value = item_sdata(item); if (!(raw_value & 0xfffffff0)) parser->global.unit_exponent = hid_snto32(raw_value, 4); else @@ -404,8 +405,10 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_ID: parser->global.report_id = item_udata(item); - if (parser->global.report_id == 0) { - hid_err(parser->device, "report_id 0 is invalid\n"); + if (parser->global.report_id == 0 || + parser->global.report_id >= HID_MAX_IDS) { + hid_err(parser->device, "report_id %u is invalid\n", + parser->global.report_id); return -1; } return 0; @@ -450,7 +453,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) } parser->local.delimiter_depth--; } - return 1; + return 0; case HID_LOCAL_ITEM_TAG_USAGE: @@ -575,7 +578,7 @@ static void hid_close_report(struct hid_device *device) for (i = 0; i < HID_REPORT_TYPES; i++) { struct hid_report_enum *report_enum = device->report_enum + i; - for (j = 0; j < 256; j++) { + for (j = 0; j < HID_MAX_IDS; j++) { struct hid_report *report = report_enum->report_id_hash[j]; if (report) hid_free_report(report); @@ -677,12 +680,64 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) return NULL; } -static void hid_scan_usage(struct hid_device *hid, u32 usage) +static void hid_scan_input_usage(struct hid_parser *parser, u32 usage) { + struct hid_device *hid = parser->device; + if (usage == HID_DG_CONTACTID) hid->group = HID_GROUP_MULTITOUCH; } +static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage) +{ + if (usage == 0xff0000c5 && parser->global.report_count == 256 && + parser->global.report_size == 8) + parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8; +} + +static void hid_scan_collection(struct hid_parser *parser, unsigned type) +{ + struct hid_device *hid = parser->device; + + if (((parser->global.usage_page << 16) == HID_UP_SENSOR) && + type == HID_COLLECTION_PHYSICAL) + hid->group = HID_GROUP_SENSOR_HUB; +} + +static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) +{ + __u32 data; + int i; + + data = item_udata(item); + + switch (item->tag) { + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: + hid_scan_collection(parser, data & 0xff); + break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: + break; + case HID_MAIN_ITEM_TAG_INPUT: + /* ignore constant inputs, they will be ignored by hid-input */ + if (data & HID_MAIN_ITEM_CONSTANT) + break; + for (i = 0; i < parser->local.usage_index; i++) + hid_scan_input_usage(parser, parser->local.usage[i]); + break; + case HID_MAIN_ITEM_TAG_OUTPUT: + break; + case HID_MAIN_ITEM_TAG_FEATURE: + for (i = 0; i < parser->local.usage_index; i++) + hid_scan_feature_usage(parser, parser->local.usage[i]); + break; + } + + /* Reset the local parser environment */ + memset(&parser->local, 0, sizeof(parser->local)); + + return 0; +} + /* * Scan a report descriptor before the device is added to the bus. * Sets device groups and other properties that determine what driver @@ -690,49 +745,49 @@ static void hid_scan_usage(struct hid_device *hid, u32 usage) */ static int hid_scan_report(struct hid_device *hid) { - unsigned int page = 0, delim = 0; + struct hid_parser *parser; + struct hid_item item; __u8 *start = hid->dev_rdesc; __u8 *end = start + hid->dev_rsize; - unsigned int u, u_min = 0, u_max = 0; - struct hid_item item; + static int (*dispatch_type[])(struct hid_parser *parser, + struct hid_item *item) = { + hid_scan_main, + hid_parser_global, + hid_parser_local, + hid_parser_reserved + }; + + parser = vzalloc(sizeof(struct hid_parser)); + if (!parser) + return -ENOMEM; + parser->device = hid; hid->group = HID_GROUP_GENERIC; - while ((start = fetch_item(start, end, &item)) != NULL) { - if (item.format != HID_ITEM_FORMAT_SHORT) - return -EINVAL; - if (item.type == HID_ITEM_TYPE_GLOBAL) { - if (item.tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE) - page = item_udata(&item) << 16; - } else if (item.type == HID_ITEM_TYPE_LOCAL) { - if (delim > 1) - break; - u = item_udata(&item); - if (item.size <= 2) - u += page; - switch (item.tag) { - case HID_LOCAL_ITEM_TAG_DELIMITER: - delim += !!u; - break; - case HID_LOCAL_ITEM_TAG_USAGE: - hid_scan_usage(hid, u); - break; - case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: - u_min = u; - break; - case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: - u_max = u; - for (u = u_min; u <= u_max; u++) - hid_scan_usage(hid, u); - break; - } - } else if (page == HID_UP_SENSOR && - 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->group = HID_GROUP_SENSOR_HUB; - } + /* + * The parsing is simpler than the one in hid_open_report() as we should + * be robust against hid errors. Those errors will be raised by + * hid_open_report() anyway. + */ + while ((start = fetch_item(start, end, &item)) != NULL) + dispatch_type[item.type](parser, &item); + + /* + * Handle special flags set during scanning. + */ + if ((parser->scan_flags & HID_SCAN_FLAG_MT_WIN_8) && + (hid->group == HID_GROUP_MULTITOUCH)) + hid->group = HID_GROUP_MULTITOUCH_WIN_8; + + /* + * Vendor specific handlings + */ + if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) && + (hid->group == HID_GROUP_GENERIC)) + /* hid-rmi should take care of them, not hid-generic */ + hid->group = HID_GROUP_RMI; + + vfree(parser); return 0; } @@ -756,6 +811,74 @@ int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size) } EXPORT_SYMBOL_GPL(hid_parse_report); +static const char * const hid_report_names[] = { + "HID_INPUT_REPORT", + "HID_OUTPUT_REPORT", + "HID_FEATURE_REPORT", +}; +/** + * hid_validate_values - validate existing device report's value indexes + * + * @device: hid device + * @type: which report type to examine + * @id: which report ID to examine (0 for first) + * @field_index: which report field to examine + * @report_counts: expected number of values + * + * Validate the number of values in a given field of a given report, after + * parsing. + */ +struct hid_report *hid_validate_values(struct hid_device *hid, + unsigned int type, unsigned int id, + unsigned int field_index, + unsigned int report_counts) +{ + struct hid_report *report; + + if (type > HID_FEATURE_REPORT) { + hid_err(hid, "invalid HID report type %u\n", type); + return NULL; + } + + if (id >= HID_MAX_IDS) { + hid_err(hid, "invalid HID report id %u\n", id); + return NULL; + } + + /* + * Explicitly not using hid_get_report() here since it depends on + * ->numbered being checked, which may not always be the case when + * drivers go to access report values. + */ + if (id == 0) { + /* + * Validating on id 0 means we should examine the first + * report in the list. + */ + report = list_entry( + hid->report_enum[type].report_list.next, + struct hid_report, list); + } else { + report = hid->report_enum[type].report_id_hash[id]; + } + if (!report) { + hid_err(hid, "missing %s %u\n", hid_report_names[type], id); + return NULL; + } + if (report->maxfield <= field_index) { + hid_err(hid, "not enough fields in %s %u\n", + hid_report_names[type], id); + return NULL; + } + if (report->field[field_index]->report_count < report_counts) { + hid_err(hid, "not enough values in %s %u field %u\n", + hid_report_names[type], id, field_index); + return NULL; + } + return report; +} +EXPORT_SYMBOL_GPL(hid_validate_values); + /** * hid_open_report - open a driver-specific device report * @@ -1129,7 +1252,8 @@ static void hid_output_field(const struct hid_device *hid, } /* - * Create a report. + * Create a report. 'data' has to be allocated using + * hid_alloc_report_buf() so that it has proper size. */ void hid_output_report(struct hid_report *report, __u8 *data) @@ -1145,6 +1269,28 @@ void hid_output_report(struct hid_report *report, __u8 *data) } EXPORT_SYMBOL_GPL(hid_output_report); +static int hid_report_len(struct hid_report *report) +{ + /* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */ + return ((report->size - 1) >> 3) + 1 + (report->id > 0); +} + +/* + * Allocator for buffer that is going to be passed to hid_output_report() + */ +u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) +{ + /* + * 7 extra bytes are necessary to achieve proper functionality + * of implement() working on 8 byte chunks + */ + + int len = hid_report_len(report) + 7; + + return kmalloc(len, flags); +} +EXPORT_SYMBOL_GPL(hid_alloc_report_buf); + /* * Set a field value. The report this field belongs to has to be * created and transferred to the device, to set this value in the @@ -1153,7 +1299,12 @@ EXPORT_SYMBOL_GPL(hid_output_report); int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) { - unsigned size = field->report_size; + unsigned size; + + if (!field) + return -1; + + size = field->report_size; hid_dump_input(field->report->device, field->usage + offset, value); @@ -1190,11 +1341,47 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, return report; } +/* + * Implement a generic .request() callback, using .raw_request() + * DO NOT USE in hid drivers directly, but through hid_hw_request instead. + */ +void __hid_request(struct hid_device *hid, struct hid_report *report, + int reqtype) +{ + char *buf; + int ret; + int len; + + buf = hid_alloc_report_buf(report, GFP_KERNEL); + if (!buf) + return; + + len = hid_report_len(report); + + if (reqtype == HID_REQ_SET_REPORT) + hid_output_report(report, buf); + + ret = hid->ll_driver->raw_request(hid, report->id, buf, len, + report->type, reqtype); + if (ret < 0) { + dbg_hid("unable to complete request: %d\n", ret); + goto out; + } + + if (reqtype == HID_REQ_GET_REPORT) + hid_input_report(hid, report->type, buf, ret, 0); + +out: + kfree(buf); +} +EXPORT_SYMBOL_GPL(__hid_request); + int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int interrupt) { 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; @@ -1228,9 +1415,12 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, goto out; } - if (hid->claimed != HID_CLAIMED_HIDRAW) { + if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) { 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) @@ -1256,14 +1446,12 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i struct hid_report_enum *report_enum; struct hid_driver *hdrv; struct hid_report *report; - char *buf; - unsigned int i; int ret = 0; if (!hid) return -ENODEV; - if (down_trylock(&hid->driver_lock)) + if (down_trylock(&hid->driver_input_lock)) return -EBUSY; if (!hid->driver) { @@ -1280,28 +1468,9 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i } /* Avoid unnecessary overhead if debugfs is disabled */ - if (list_empty(&hid->debug_list)) - goto nomem; - - buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); - - if (!buf) - goto nomem; - - /* dump the report */ - snprintf(buf, HID_DEBUG_BUFSIZE - 1, - "\nreport (size %u) (%snumbered) = ", size, report_enum->numbered ? "" : "un"); - hid_debug_event(hid, buf); - - for (i = 0; i < size; i++) { - snprintf(buf, HID_DEBUG_BUFSIZE - 1, - " %02x", data[i]); - hid_debug_event(hid, buf); - } - hid_debug_event(hid, "\n"); - kfree(buf); + if (!list_empty(&hid->debug_list)) + hid_dump_report(hid, type, data, size); -nomem: report = hid_get_report(report_enum, data); if (!report) { @@ -1311,16 +1480,14 @@ nomem: if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) { ret = hdrv->raw_event(hid, report, data, size); - if (ret != 0) { - ret = ret < 0 ? ret : 0; + if (ret < 0) goto unlock; - } } ret = hid_report_raw_event(hid, type, data, size, interrupt); unlock: - up(&hid->driver_lock); + up(&hid->driver_input_lock); return ret; } EXPORT_SYMBOL_GPL(hid_input_report); @@ -1498,8 +1665,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) }, @@ -1523,6 +1689,11 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS) }, @@ -1562,11 +1733,15 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) }, @@ -1580,6 +1755,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) }, { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) }, @@ -1588,6 +1764,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030) }, { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, @@ -1599,9 +1777,19 @@ 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_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, + { 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) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_MANTICORE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GX_IMPERATOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, @@ -1632,6 +1820,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, @@ -1649,10 +1838,12 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) }, @@ -1678,26 +1869,44 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, #if IS_ENABLED(CONFIG_HID_ROCCAT) - { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKUFX) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEXTD) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, #endif +#if IS_ENABLED(CONFIG_HID_SAITEK) { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) }, +#endif { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { 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_PS4_CONTROLLER) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_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) }, @@ -1734,12 +1943,14 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) }, { } }; @@ -1838,6 +2049,11 @@ static int hid_device_probe(struct device *dev) if (down_interruptible(&hdev->driver_lock)) return -EINTR; + if (down_interruptible(&hdev->driver_input_lock)) { + ret = -EINTR; + goto unlock_driver_lock; + } + hdev->io_started = false; if (!hdev->driver) { id = hid_match_device(hdev, hdrv); @@ -1860,6 +2076,9 @@ static int hid_device_probe(struct device *dev) } } unlock: + if (!hdev->io_started) + up(&hdev->driver_input_lock); +unlock_driver_lock: up(&hdev->driver_lock); return ret; } @@ -1868,9 +2087,15 @@ static int hid_device_remove(struct device *dev) { struct hid_device *hdev = container_of(dev, struct hid_device, dev); struct hid_driver *hdrv; + int ret = 0; if (down_interruptible(&hdev->driver_lock)) return -EINTR; + if (down_interruptible(&hdev->driver_input_lock)) { + ret = -EINTR; + goto unlock_driver_lock; + } + hdev->io_started = false; hdrv = hdev->driver; if (hdrv) { @@ -1882,8 +2107,11 @@ static int hid_device_remove(struct device *dev) hdev->driver = NULL; } + if (!hdev->io_started) + up(&hdev->driver_input_lock); +unlock_driver_lock: up(&hdev->driver_lock); - return 0; + return ret; } static ssize_t modalias_show(struct device *dev, struct device_attribute *a, @@ -1897,11 +2125,13 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; } +static DEVICE_ATTR_RO(modalias); -static struct device_attribute hid_dev_attrs[] = { - __ATTR_RO(modalias), - __ATTR_NULL, +static struct attribute *hid_dev_attrs[] = { + &dev_attr_modalias.attr, + NULL, }; +ATTRIBUTE_GROUPS(hid_dev); static int hid_uevent(struct device *dev, struct kobj_uevent_env *env) { @@ -1929,7 +2159,7 @@ static int hid_uevent(struct device *dev, struct kobj_uevent_env *env) static struct bus_type hid_bus_type = { .name = "hid", - .dev_attrs = hid_dev_attrs, + .dev_groups = hid_dev_groups, .match = hid_bus_match, .probe = hid_device_probe, .remove = hid_device_remove, @@ -1957,6 +2187,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) }, { HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI4713) }, { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) }, @@ -2033,6 +2264,8 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) }, { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) }, { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_410) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_510) }, { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, @@ -2111,6 +2344,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) }, { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) }, { } }; @@ -2170,6 +2404,9 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { } @@ -2219,13 +2456,24 @@ bool hid_ignore(struct hid_device *hdev) hdev->type == HID_TYPE_USBNONE) return true; break; - case USB_VENDOR_ID_DWAV: - /* These are handled by usbtouchscreen. hdev->type is probably - * HID_TYPE_USBNONE, but we say !HID_TYPE_USBMOUSE to match - * usbtouchscreen. */ - if ((hdev->product == USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER || - hdev->product == USB_DEVICE_ID_DWAV_TOUCHCONTROLLER) && - hdev->type != HID_TYPE_USBMOUSE) + 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; + case USB_VENDOR_ID_ATMEL_V_USB: + /* Masterkit MA901 usb radio based on Atmel tiny85 chip and + * it has the same USB ID as many Atmel V-USB devices. This + * usb radio is handled by radio-ma901.c driver so we want + * ignore the hid. Check the name, bus, product and ignore + * if we have MA901 usb radio. + */ + if (hdev->product == USB_DEVICE_ID_ATMEL_V_USB && + hdev->bus == BUS_USB && + strncmp(hdev->name, "www.masterkit.ru MA901", 22) == 0) return true; break; } @@ -2252,6 +2500,14 @@ int hid_add_device(struct hid_device *hdev) return -ENODEV; /* + * Check for the mandatory transport channel. + */ + if (!hdev->ll_driver->raw_request) { + hid_err(hdev, "transport driver missing .raw_request()\n"); + return -EINVAL; + } + + /* * Read the device report descriptor once and use as template * for the driver-specific modifications. */ @@ -2313,7 +2569,9 @@ struct hid_device *hid_allocate_device(void) init_waitqueue_head(&hdev->debug_wait); INIT_LIST_HEAD(&hdev->debug_list); + spin_lock_init(&hdev->debug_list_lock); sema_init(&hdev->driver_lock, 1); + sema_init(&hdev->driver_input_lock, 1); return hdev; } diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c new file mode 100644 index 00000000000..56be85a9a77 --- /dev/null +++ b/drivers/hid/hid-cp2112.c @@ -0,0 +1,1073 @@ +/* + * hid-cp2112.c - Silicon Labs HID USB to SMBus master bridge + * Copyright (c) 2013,2014 Uplogix, Inc. + * David Barksdale <dbarksdale@uplogix.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +/* + * The Silicon Labs CP2112 chip is a USB HID device which provides an + * SMBus controller for talking to slave devices and 8 GPIO pins. The + * host communicates with the CP2112 via raw HID reports. + * + * Data Sheet: + * http://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf + * Programming Interface Specification: + * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN495.pdf + */ + +#include <linux/gpio.h> +#include <linux/hid.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/nls.h> +#include <linux/usb/ch9.h> +#include "hid-ids.h" + +enum { + CP2112_GPIO_CONFIG = 0x02, + CP2112_GPIO_GET = 0x03, + CP2112_GPIO_SET = 0x04, + CP2112_GET_VERSION_INFO = 0x05, + CP2112_SMBUS_CONFIG = 0x06, + CP2112_DATA_READ_REQUEST = 0x10, + CP2112_DATA_WRITE_READ_REQUEST = 0x11, + CP2112_DATA_READ_FORCE_SEND = 0x12, + CP2112_DATA_READ_RESPONSE = 0x13, + CP2112_DATA_WRITE_REQUEST = 0x14, + CP2112_TRANSFER_STATUS_REQUEST = 0x15, + CP2112_TRANSFER_STATUS_RESPONSE = 0x16, + CP2112_CANCEL_TRANSFER = 0x17, + CP2112_LOCK_BYTE = 0x20, + CP2112_USB_CONFIG = 0x21, + CP2112_MANUFACTURER_STRING = 0x22, + CP2112_PRODUCT_STRING = 0x23, + CP2112_SERIAL_STRING = 0x24, +}; + +enum { + STATUS0_IDLE = 0x00, + STATUS0_BUSY = 0x01, + STATUS0_COMPLETE = 0x02, + STATUS0_ERROR = 0x03, +}; + +enum { + STATUS1_TIMEOUT_NACK = 0x00, + STATUS1_TIMEOUT_BUS = 0x01, + STATUS1_ARBITRATION_LOST = 0x02, + STATUS1_READ_INCOMPLETE = 0x03, + STATUS1_WRITE_INCOMPLETE = 0x04, + STATUS1_SUCCESS = 0x05, +}; + +struct cp2112_smbus_config_report { + u8 report; /* CP2112_SMBUS_CONFIG */ + __be32 clock_speed; /* Hz */ + u8 device_address; /* Stored in the upper 7 bits */ + u8 auto_send_read; /* 1 = enabled, 0 = disabled */ + __be16 write_timeout; /* ms, 0 = no timeout */ + __be16 read_timeout; /* ms, 0 = no timeout */ + u8 scl_low_timeout; /* 1 = enabled, 0 = disabled */ + __be16 retry_time; /* # of retries, 0 = no limit */ +} __packed; + +struct cp2112_usb_config_report { + u8 report; /* CP2112_USB_CONFIG */ + __le16 vid; /* Vendor ID */ + __le16 pid; /* Product ID */ + u8 max_power; /* Power requested in 2mA units */ + u8 power_mode; /* 0x00 = bus powered + 0x01 = self powered & regulator off + 0x02 = self powered & regulator on */ + u8 release_major; + u8 release_minor; + u8 mask; /* What fields to program */ +} __packed; + +struct cp2112_read_req_report { + u8 report; /* CP2112_DATA_READ_REQUEST */ + u8 slave_address; + __be16 length; +} __packed; + +struct cp2112_write_read_req_report { + u8 report; /* CP2112_DATA_WRITE_READ_REQUEST */ + u8 slave_address; + __be16 length; + u8 target_address_length; + u8 target_address[16]; +} __packed; + +struct cp2112_write_req_report { + u8 report; /* CP2112_DATA_WRITE_REQUEST */ + u8 slave_address; + u8 length; + u8 data[61]; +} __packed; + +struct cp2112_force_read_report { + u8 report; /* CP2112_DATA_READ_FORCE_SEND */ + __be16 length; +} __packed; + +struct cp2112_xfer_status_report { + u8 report; /* CP2112_TRANSFER_STATUS_RESPONSE */ + u8 status0; /* STATUS0_* */ + u8 status1; /* STATUS1_* */ + __be16 retries; + __be16 length; +} __packed; + +struct cp2112_string_report { + u8 dummy; /* force .string to be aligned */ + u8 report; /* CP2112_*_STRING */ + u8 length; /* length in bytes of everyting after .report */ + u8 type; /* USB_DT_STRING */ + wchar_t string[30]; /* UTF16_LITTLE_ENDIAN string */ +} __packed; + +/* Number of times to request transfer status before giving up waiting for a + transfer to complete. This may need to be changed if SMBUS clock, retries, + or read/write/scl_low timeout settings are changed. */ +static const int XFER_STATUS_RETRIES = 10; + +/* Time in ms to wait for a CP2112_DATA_READ_RESPONSE or + CP2112_TRANSFER_STATUS_RESPONSE. */ +static const int RESPONSE_TIMEOUT = 50; + +static const struct hid_device_id cp2112_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) }, + { } +}; +MODULE_DEVICE_TABLE(hid, cp2112_devices); + +struct cp2112_device { + struct i2c_adapter adap; + struct hid_device *hdev; + wait_queue_head_t wait; + u8 read_data[61]; + u8 read_length; + int xfer_status; + atomic_t read_avail; + atomic_t xfer_avail; + struct gpio_chip gc; +}; + +static int gpio_push_pull = 0xFF; +module_param(gpio_push_pull, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(gpio_push_pull, "GPIO push-pull configuration bitmask"); + +static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct cp2112_device *dev = container_of(chip, struct cp2112_device, + gc); + struct hid_device *hdev = dev->hdev; + u8 buf[5]; + int ret; + + ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, + sizeof(buf), HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); + if (ret != sizeof(buf)) { + hid_err(hdev, "error requesting GPIO config: %d\n", ret); + return ret; + } + + buf[1] &= ~(1 << offset); + buf[2] = gpio_push_pull; + + ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret < 0) { + hid_err(hdev, "error setting GPIO config: %d\n", ret); + return ret; + } + + return 0; +} + +static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct cp2112_device *dev = container_of(chip, struct cp2112_device, + gc); + struct hid_device *hdev = dev->hdev; + u8 buf[3]; + int ret; + + buf[0] = CP2112_GPIO_SET; + buf[1] = value ? 0xff : 0; + buf[2] = 1 << offset; + + ret = hid_hw_raw_request(hdev, CP2112_GPIO_SET, buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret < 0) + hid_err(hdev, "error setting GPIO values: %d\n", ret); +} + +static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct cp2112_device *dev = container_of(chip, struct cp2112_device, + gc); + struct hid_device *hdev = dev->hdev; + u8 buf[2]; + int ret; + + ret = hid_hw_raw_request(hdev, CP2112_GPIO_GET, buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret != sizeof(buf)) { + hid_err(hdev, "error requesting GPIO values: %d\n", ret); + return ret; + } + + return (buf[1] >> offset) & 1; +} + +static int cp2112_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct cp2112_device *dev = container_of(chip, struct cp2112_device, + gc); + struct hid_device *hdev = dev->hdev; + u8 buf[5]; + int ret; + + cp2112_gpio_set(chip, offset, value); + + ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, + sizeof(buf), HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); + if (ret != sizeof(buf)) { + hid_err(hdev, "error requesting GPIO config: %d\n", ret); + return ret; + } + + buf[1] |= 1 << offset; + buf[2] = gpio_push_pull; + + ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret < 0) { + hid_err(hdev, "error setting GPIO config: %d\n", ret); + return ret; + } + + return 0; +} + +static int cp2112_hid_get(struct hid_device *hdev, unsigned char report_number, + u8 *data, size_t count, unsigned char report_type) +{ + u8 *buf; + int ret; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hid_hw_raw_request(hdev, report_number, buf, count, + report_type, HID_REQ_GET_REPORT); + memcpy(data, buf, count); + kfree(buf); + return ret; +} + +static int cp2112_hid_output(struct hid_device *hdev, u8 *data, size_t count, + unsigned char report_type) +{ + u8 *buf; + int ret; + + buf = kmemdup(data, count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (report_type == HID_OUTPUT_REPORT) + ret = hid_hw_output_report(hdev, buf, count); + else + ret = hid_hw_raw_request(hdev, buf[0], buf, count, report_type, + HID_REQ_SET_REPORT); + + kfree(buf); + return ret; +} + +static int cp2112_wait(struct cp2112_device *dev, atomic_t *avail) +{ + int ret = 0; + + /* We have sent either a CP2112_TRANSFER_STATUS_REQUEST or a + * CP2112_DATA_READ_FORCE_SEND and we are waiting for the response to + * come in cp2112_raw_event or timeout. There will only be one of these + * in flight at any one time. The timeout is extremely large and is a + * last resort if the CP2112 has died. If we do timeout we don't expect + * to receive the response which would cause data races, it's not like + * we can do anything about it anyway. + */ + ret = wait_event_interruptible_timeout(dev->wait, + atomic_read(avail), msecs_to_jiffies(RESPONSE_TIMEOUT)); + if (-ERESTARTSYS == ret) + return ret; + if (!ret) + return -ETIMEDOUT; + + atomic_set(avail, 0); + return 0; +} + +static int cp2112_xfer_status(struct cp2112_device *dev) +{ + struct hid_device *hdev = dev->hdev; + u8 buf[2]; + int ret; + + buf[0] = CP2112_TRANSFER_STATUS_REQUEST; + buf[1] = 0x01; + atomic_set(&dev->xfer_avail, 0); + + ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT); + if (ret < 0) { + hid_warn(hdev, "Error requesting status: %d\n", ret); + return ret; + } + + ret = cp2112_wait(dev, &dev->xfer_avail); + if (ret) + return ret; + + return dev->xfer_status; +} + +static int cp2112_read(struct cp2112_device *dev, u8 *data, size_t size) +{ + struct hid_device *hdev = dev->hdev; + struct cp2112_force_read_report report; + int ret; + + report.report = CP2112_DATA_READ_FORCE_SEND; + report.length = cpu_to_be16(size); + + atomic_set(&dev->read_avail, 0); + + ret = cp2112_hid_output(hdev, &report.report, sizeof(report), + HID_OUTPUT_REPORT); + if (ret < 0) { + hid_warn(hdev, "Error requesting data: %d\n", ret); + return ret; + } + + ret = cp2112_wait(dev, &dev->read_avail); + if (ret) + return ret; + + hid_dbg(hdev, "read %d of %zd bytes requested\n", + dev->read_length, size); + + if (size > dev->read_length) + size = dev->read_length; + + memcpy(data, dev->read_data, size); + return dev->read_length; +} + +static int cp2112_read_req(void *buf, u8 slave_address, u16 length) +{ + struct cp2112_read_req_report *report = buf; + + if (length < 1 || length > 512) + return -EINVAL; + + report->report = CP2112_DATA_READ_REQUEST; + report->slave_address = slave_address << 1; + report->length = cpu_to_be16(length); + return sizeof(*report); +} + +static int cp2112_write_read_req(void *buf, u8 slave_address, u16 length, + u8 command, u8 *data, u8 data_length) +{ + struct cp2112_write_read_req_report *report = buf; + + if (length < 1 || length > 512 + || data_length > sizeof(report->target_address) - 1) + return -EINVAL; + + report->report = CP2112_DATA_WRITE_READ_REQUEST; + report->slave_address = slave_address << 1; + report->length = cpu_to_be16(length); + report->target_address_length = data_length + 1; + report->target_address[0] = command; + memcpy(&report->target_address[1], data, data_length); + return data_length + 6; +} + +static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data, + u8 data_length) +{ + struct cp2112_write_req_report *report = buf; + + if (data_length > sizeof(report->data) - 1) + return -EINVAL; + + report->report = CP2112_DATA_WRITE_REQUEST; + report->slave_address = slave_address << 1; + report->length = data_length + 1; + report->data[0] = command; + memcpy(&report->data[1], data, data_length); + return data_length + 4; +} + +static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data; + struct hid_device *hdev = dev->hdev; + u8 buf[64]; + __be16 word; + ssize_t count; + size_t read_length = 0; + unsigned int retries; + int ret; + + hid_dbg(hdev, "%s addr 0x%x flags 0x%x cmd 0x%x size %d\n", + read_write == I2C_SMBUS_WRITE ? "write" : "read", + addr, flags, command, size); + + switch (size) { + case I2C_SMBUS_BYTE: + read_length = 1; + + if (I2C_SMBUS_READ == read_write) + count = cp2112_read_req(buf, addr, read_length); + else + count = cp2112_write_req(buf, addr, data->byte, NULL, + 0); + break; + case I2C_SMBUS_BYTE_DATA: + read_length = 1; + + if (I2C_SMBUS_READ == read_write) + count = cp2112_write_read_req(buf, addr, read_length, + command, NULL, 0); + else + count = cp2112_write_req(buf, addr, command, + &data->byte, 1); + break; + case I2C_SMBUS_WORD_DATA: + read_length = 2; + word = cpu_to_be16(data->word); + + if (I2C_SMBUS_READ == read_write) + count = cp2112_write_read_req(buf, addr, read_length, + command, NULL, 0); + else + count = cp2112_write_req(buf, addr, command, + (u8 *)&word, 2); + break; + case I2C_SMBUS_PROC_CALL: + size = I2C_SMBUS_WORD_DATA; + read_write = I2C_SMBUS_READ; + read_length = 2; + word = cpu_to_be16(data->word); + + count = cp2112_write_read_req(buf, addr, read_length, command, + (u8 *)&word, 2); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + size = I2C_SMBUS_BLOCK_DATA; + /* fallthrough */ + case I2C_SMBUS_BLOCK_DATA: + if (I2C_SMBUS_READ == read_write) { + count = cp2112_write_read_req(buf, addr, + I2C_SMBUS_BLOCK_MAX, + command, NULL, 0); + } else { + count = cp2112_write_req(buf, addr, command, + data->block, + data->block[0] + 1); + } + break; + case I2C_SMBUS_BLOCK_PROC_CALL: + size = I2C_SMBUS_BLOCK_DATA; + read_write = I2C_SMBUS_READ; + + count = cp2112_write_read_req(buf, addr, I2C_SMBUS_BLOCK_MAX, + command, data->block, + data->block[0] + 1); + break; + default: + hid_warn(hdev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + if (count < 0) + return count; + + ret = hid_hw_power(hdev, PM_HINT_FULLON); + if (ret < 0) { + hid_err(hdev, "power management error: %d\n", ret); + return ret; + } + + ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT); + if (ret < 0) { + hid_warn(hdev, "Error starting transaction: %d\n", ret); + goto power_normal; + } + + for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) { + ret = cp2112_xfer_status(dev); + if (-EBUSY == ret) + continue; + if (ret < 0) + goto power_normal; + break; + } + + if (XFER_STATUS_RETRIES <= retries) { + hid_warn(hdev, "Transfer timed out, cancelling.\n"); + buf[0] = CP2112_CANCEL_TRANSFER; + buf[1] = 0x01; + + ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT); + if (ret < 0) + hid_warn(hdev, "Error cancelling transaction: %d\n", + ret); + + ret = -ETIMEDOUT; + goto power_normal; + } + + if (I2C_SMBUS_WRITE == read_write) { + ret = 0; + goto power_normal; + } + + if (I2C_SMBUS_BLOCK_DATA == size) + read_length = ret; + + ret = cp2112_read(dev, buf, read_length); + if (ret < 0) + goto power_normal; + if (ret != read_length) { + hid_warn(hdev, "short read: %d < %zd\n", ret, read_length); + ret = -EIO; + goto power_normal; + } + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + data->byte = buf[0]; + break; + case I2C_SMBUS_WORD_DATA: + data->word = be16_to_cpup((__be16 *)buf); + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_length > I2C_SMBUS_BLOCK_MAX) { + ret = -EPROTO; + goto power_normal; + } + + memcpy(data->block, buf, read_length); + break; + } + + ret = 0; +power_normal: + hid_hw_power(hdev, PM_HINT_NORMAL); + hid_dbg(hdev, "transfer finished: %d\n", ret); + return ret; +} + +static u32 cp2112_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL; +} + +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = cp2112_xfer, + .functionality = cp2112_functionality, +}; + +static int cp2112_get_usb_config(struct hid_device *hdev, + struct cp2112_usb_config_report *cfg) +{ + int ret; + + ret = cp2112_hid_get(hdev, CP2112_USB_CONFIG, (u8 *)cfg, sizeof(*cfg), + HID_FEATURE_REPORT); + if (ret != sizeof(*cfg)) { + hid_err(hdev, "error reading usb config: %d\n", ret); + if (ret < 0) + return ret; + return -EIO; + } + + return 0; +} + +static int cp2112_set_usb_config(struct hid_device *hdev, + struct cp2112_usb_config_report *cfg) +{ + int ret; + + BUG_ON(cfg->report != CP2112_USB_CONFIG); + + ret = cp2112_hid_output(hdev, (u8 *)cfg, sizeof(*cfg), + HID_FEATURE_REPORT); + if (ret != sizeof(*cfg)) { + hid_err(hdev, "error writing usb config: %d\n", ret); + if (ret < 0) + return ret; + return -EIO; + } + + return 0; +} + +static void chmod_sysfs_attrs(struct hid_device *hdev); + +#define CP2112_CONFIG_ATTR(name, store, format, ...) \ +static ssize_t name##_store(struct device *kdev, \ + struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct hid_device *hdev = container_of(kdev, struct hid_device, dev); \ + struct cp2112_usb_config_report cfg; \ + int ret = cp2112_get_usb_config(hdev, &cfg); \ + if (ret) \ + return ret; \ + store; \ + ret = cp2112_set_usb_config(hdev, &cfg); \ + if (ret) \ + return ret; \ + chmod_sysfs_attrs(hdev); \ + return count; \ +} \ +static ssize_t name##_show(struct device *kdev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct hid_device *hdev = container_of(kdev, struct hid_device, dev); \ + struct cp2112_usb_config_report cfg; \ + int ret = cp2112_get_usb_config(hdev, &cfg); \ + if (ret) \ + return ret; \ + return scnprintf(buf, PAGE_SIZE, format, ##__VA_ARGS__); \ +} \ +static DEVICE_ATTR_RW(name); + +CP2112_CONFIG_ATTR(vendor_id, ({ + u16 vid; + + if (sscanf(buf, "%hi", &vid) != 1) + return -EINVAL; + + cfg.vid = cpu_to_le16(vid); + cfg.mask = 0x01; +}), "0x%04x\n", le16_to_cpu(cfg.vid)); + +CP2112_CONFIG_ATTR(product_id, ({ + u16 pid; + + if (sscanf(buf, "%hi", &pid) != 1) + return -EINVAL; + + cfg.pid = cpu_to_le16(pid); + cfg.mask = 0x02; +}), "0x%04x\n", le16_to_cpu(cfg.pid)); + +CP2112_CONFIG_ATTR(max_power, ({ + int mA; + + if (sscanf(buf, "%i", &mA) != 1) + return -EINVAL; + + cfg.max_power = (mA + 1) / 2; + cfg.mask = 0x04; +}), "%u mA\n", cfg.max_power * 2); + +CP2112_CONFIG_ATTR(power_mode, ({ + if (sscanf(buf, "%hhi", &cfg.power_mode) != 1) + return -EINVAL; + + cfg.mask = 0x08; +}), "%u\n", cfg.power_mode); + +CP2112_CONFIG_ATTR(release_version, ({ + if (sscanf(buf, "%hhi.%hhi", &cfg.release_major, &cfg.release_minor) + != 2) + return -EINVAL; + + cfg.mask = 0x10; +}), "%u.%u\n", cfg.release_major, cfg.release_minor); + +#undef CP2112_CONFIG_ATTR + +struct cp2112_pstring_attribute { + struct device_attribute attr; + unsigned char report; +}; + +static ssize_t pstr_store(struct device *kdev, + struct device_attribute *kattr, const char *buf, + size_t count) +{ + struct hid_device *hdev = container_of(kdev, struct hid_device, dev); + struct cp2112_pstring_attribute *attr = + container_of(kattr, struct cp2112_pstring_attribute, attr); + struct cp2112_string_report report; + int ret; + + memset(&report, 0, sizeof(report)); + + ret = utf8s_to_utf16s(buf, count, UTF16_LITTLE_ENDIAN, + report.string, ARRAY_SIZE(report.string)); + report.report = attr->report; + report.length = ret * sizeof(report.string[0]) + 2; + report.type = USB_DT_STRING; + + ret = cp2112_hid_output(hdev, &report.report, report.length + 1, + HID_FEATURE_REPORT); + if (ret != report.length + 1) { + hid_err(hdev, "error writing %s string: %d\n", kattr->attr.name, + ret); + if (ret < 0) + return ret; + return -EIO; + } + + chmod_sysfs_attrs(hdev); + return count; +} + +static ssize_t pstr_show(struct device *kdev, + struct device_attribute *kattr, char *buf) +{ + struct hid_device *hdev = container_of(kdev, struct hid_device, dev); + struct cp2112_pstring_attribute *attr = + container_of(kattr, struct cp2112_pstring_attribute, attr); + struct cp2112_string_report report; + u8 length; + int ret; + + ret = cp2112_hid_get(hdev, attr->report, &report.report, + sizeof(report) - 1, HID_FEATURE_REPORT); + if (ret < 3) { + hid_err(hdev, "error reading %s string: %d\n", kattr->attr.name, + ret); + if (ret < 0) + return ret; + return -EIO; + } + + if (report.length < 2) { + hid_err(hdev, "invalid %s string length: %d\n", + kattr->attr.name, report.length); + return -EIO; + } + + length = report.length > ret - 1 ? ret - 1 : report.length; + length = (length - 2) / sizeof(report.string[0]); + ret = utf16s_to_utf8s(report.string, length, UTF16_LITTLE_ENDIAN, buf, + PAGE_SIZE - 1); + buf[ret++] = '\n'; + return ret; +} + +#define CP2112_PSTR_ATTR(name, _report) \ +static struct cp2112_pstring_attribute dev_attr_##name = { \ + .attr = __ATTR(name, (S_IWUSR | S_IRUGO), pstr_show, pstr_store), \ + .report = _report, \ +}; + +CP2112_PSTR_ATTR(manufacturer, CP2112_MANUFACTURER_STRING); +CP2112_PSTR_ATTR(product, CP2112_PRODUCT_STRING); +CP2112_PSTR_ATTR(serial, CP2112_SERIAL_STRING); + +#undef CP2112_PSTR_ATTR + +static const struct attribute_group cp2112_attr_group = { + .attrs = (struct attribute *[]){ + &dev_attr_vendor_id.attr, + &dev_attr_product_id.attr, + &dev_attr_max_power.attr, + &dev_attr_power_mode.attr, + &dev_attr_release_version.attr, + &dev_attr_manufacturer.attr.attr, + &dev_attr_product.attr.attr, + &dev_attr_serial.attr.attr, + NULL + } +}; + +/* Chmoding our sysfs attributes is simply a way to expose which fields in the + * PROM have already been programmed. We do not depend on this preventing + * writing to these attributes since the CP2112 will simply ignore writes to + * already-programmed fields. This is why there is no sense in fixing this + * racy behaviour. + */ +static void chmod_sysfs_attrs(struct hid_device *hdev) +{ + struct attribute **attr; + u8 buf[2]; + int ret; + + ret = cp2112_hid_get(hdev, CP2112_LOCK_BYTE, buf, sizeof(buf), + HID_FEATURE_REPORT); + if (ret != sizeof(buf)) { + hid_err(hdev, "error reading lock byte: %d\n", ret); + return; + } + + for (attr = cp2112_attr_group.attrs; *attr; ++attr) { + umode_t mode = (buf[1] & 1) ? S_IWUSR | S_IRUGO : S_IRUGO; + ret = sysfs_chmod_file(&hdev->dev.kobj, *attr, mode); + if (ret < 0) + hid_err(hdev, "error chmoding sysfs file %s\n", + (*attr)->name); + buf[1] >>= 1; + } +} + +static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct cp2112_device *dev; + u8 buf[3]; + struct cp2112_smbus_config_report config; + int ret; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "hw open failed\n"); + goto err_hid_stop; + } + + ret = hid_hw_power(hdev, PM_HINT_FULLON); + if (ret < 0) { + hid_err(hdev, "power management error: %d\n", ret); + goto err_hid_close; + } + + ret = cp2112_hid_get(hdev, CP2112_GET_VERSION_INFO, buf, sizeof(buf), + HID_FEATURE_REPORT); + if (ret != sizeof(buf)) { + hid_err(hdev, "error requesting version\n"); + if (ret >= 0) + ret = -EIO; + goto err_power_normal; + } + + hid_info(hdev, "Part Number: 0x%02X Device Version: 0x%02X\n", + buf[1], buf[2]); + + ret = cp2112_hid_get(hdev, CP2112_SMBUS_CONFIG, (u8 *)&config, + sizeof(config), HID_FEATURE_REPORT); + if (ret != sizeof(config)) { + hid_err(hdev, "error requesting SMBus config\n"); + if (ret >= 0) + ret = -EIO; + goto err_power_normal; + } + + config.retry_time = cpu_to_be16(1); + + ret = cp2112_hid_output(hdev, (u8 *)&config, sizeof(config), + HID_FEATURE_REPORT); + if (ret != sizeof(config)) { + hid_err(hdev, "error setting SMBus config\n"); + if (ret >= 0) + ret = -EIO; + goto err_power_normal; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err_power_normal; + } + + hid_set_drvdata(hdev, (void *)dev); + dev->hdev = hdev; + dev->adap.owner = THIS_MODULE; + dev->adap.class = I2C_CLASS_HWMON; + dev->adap.algo = &smbus_algorithm; + dev->adap.algo_data = dev; + dev->adap.dev.parent = &hdev->dev; + snprintf(dev->adap.name, sizeof(dev->adap.name), + "CP2112 SMBus Bridge on hiddev%d", hdev->minor); + init_waitqueue_head(&dev->wait); + + hid_device_io_start(hdev); + ret = i2c_add_adapter(&dev->adap); + hid_device_io_stop(hdev); + + if (ret) { + hid_err(hdev, "error registering i2c adapter\n"); + goto err_free_dev; + } + + hid_dbg(hdev, "adapter registered\n"); + + dev->gc.label = "cp2112_gpio"; + dev->gc.direction_input = cp2112_gpio_direction_input; + dev->gc.direction_output = cp2112_gpio_direction_output; + dev->gc.set = cp2112_gpio_set; + dev->gc.get = cp2112_gpio_get; + dev->gc.base = -1; + dev->gc.ngpio = 8; + dev->gc.can_sleep = 1; + dev->gc.dev = &hdev->dev; + + ret = gpiochip_add(&dev->gc); + if (ret < 0) { + hid_err(hdev, "error registering gpio chip\n"); + goto err_free_i2c; + } + + ret = sysfs_create_group(&hdev->dev.kobj, &cp2112_attr_group); + if (ret < 0) { + hid_err(hdev, "error creating sysfs attrs\n"); + goto err_gpiochip_remove; + } + + chmod_sysfs_attrs(hdev); + hid_hw_power(hdev, PM_HINT_NORMAL); + + return ret; + +err_gpiochip_remove: + if (gpiochip_remove(&dev->gc) < 0) + hid_err(hdev, "error removing gpio chip\n"); +err_free_i2c: + i2c_del_adapter(&dev->adap); +err_free_dev: + kfree(dev); +err_power_normal: + hid_hw_power(hdev, PM_HINT_NORMAL); +err_hid_close: + hid_hw_close(hdev); +err_hid_stop: + hid_hw_stop(hdev); + return ret; +} + +static void cp2112_remove(struct hid_device *hdev) +{ + struct cp2112_device *dev = hid_get_drvdata(hdev); + + sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group); + if (gpiochip_remove(&dev->gc)) + hid_err(hdev, "unable to remove gpio chip\n"); + i2c_del_adapter(&dev->adap); + /* i2c_del_adapter has finished removing all i2c devices from our + * adapter. Well behaved devices should no longer call our cp2112_xfer + * and should have waited for any pending calls to finish. It has also + * waited for device_unregister(&adap->dev) to complete. Therefore we + * can safely free our struct cp2112_device. + */ + hid_hw_close(hdev); + hid_hw_stop(hdev); + kfree(dev); +} + +static int cp2112_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct cp2112_device *dev = hid_get_drvdata(hdev); + struct cp2112_xfer_status_report *xfer = (void *)data; + + switch (data[0]) { + case CP2112_TRANSFER_STATUS_RESPONSE: + hid_dbg(hdev, "xfer status: %02x %02x %04x %04x\n", + xfer->status0, xfer->status1, + be16_to_cpu(xfer->retries), be16_to_cpu(xfer->length)); + + switch (xfer->status0) { + case STATUS0_IDLE: + dev->xfer_status = -EAGAIN; + break; + case STATUS0_BUSY: + dev->xfer_status = -EBUSY; + break; + case STATUS0_COMPLETE: + dev->xfer_status = be16_to_cpu(xfer->length); + break; + case STATUS0_ERROR: + switch (xfer->status1) { + case STATUS1_TIMEOUT_NACK: + case STATUS1_TIMEOUT_BUS: + dev->xfer_status = -ETIMEDOUT; + break; + default: + dev->xfer_status = -EIO; + break; + } + break; + default: + dev->xfer_status = -EINVAL; + break; + } + + atomic_set(&dev->xfer_avail, 1); + break; + case CP2112_DATA_READ_RESPONSE: + hid_dbg(hdev, "read response: %02x %02x\n", data[1], data[2]); + + dev->read_length = data[2]; + if (dev->read_length > sizeof(dev->read_data)) + dev->read_length = sizeof(dev->read_data); + + memcpy(dev->read_data, &data[3], dev->read_length); + atomic_set(&dev->read_avail, 1); + break; + default: + hid_err(hdev, "unknown report\n"); + + return 0; + } + + wake_up_interruptible(&dev->wait); + return 1; +} + +static struct hid_driver cp2112_driver = { + .name = "cp2112", + .id_table = cp2112_devices, + .probe = cp2112_probe, + .remove = cp2112_remove, + .raw_event = cp2112_raw_event, +}; + +module_hid_driver(cp2112_driver); +MODULE_DESCRIPTION("Silicon Labs HID USB to SMBus master bridge"); +MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c index 3e159a50dac..c4ef3bc726e 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-debug.c b/drivers/hid/hid-debug.c index 933fff0fff1..84c3cb15ccd 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -165,6 +165,8 @@ static const struct hid_usage_entry hid_usage_table[] = { {0, 0x53, "DeviceIndex"}, {0, 0x54, "ContactCount"}, {0, 0x55, "ContactMaximumNumber"}, + {0, 0x5A, "SecondaryBarrelSwitch"}, + {0, 0x5B, "TransducerSerialNumber"}, { 15, 0, "PhysicalInterfaceDevice" }, {0, 0x00, "Undefined"}, {0, 0x01, "Physical_Interface_Device"}, @@ -272,6 +274,85 @@ static const struct hid_usage_entry hid_usage_table[] = { {0, 0xAA, "Shared_Parameter_Blocks"}, {0, 0xAB, "Create_New_Effect_Report"}, {0, 0xAC, "RAM_Pool_Available"}, + { 0x20, 0, "Sensor" }, + { 0x20, 0x01, "Sensor" }, + { 0x20, 0x10, "Biometric" }, + { 0x20, 0x11, "BiometricHumanPresence" }, + { 0x20, 0x12, "BiometricHumanProximity" }, + { 0x20, 0x13, "BiometricHumanTouch" }, + { 0x20, 0x20, "Electrical" }, + { 0x20, 0x21, "ElectricalCapacitance" }, + { 0x20, 0x22, "ElectricalCurrent" }, + { 0x20, 0x23, "ElectricalPower" }, + { 0x20, 0x24, "ElectricalInductance" }, + { 0x20, 0x25, "ElectricalResistance" }, + { 0x20, 0x26, "ElectricalVoltage" }, + { 0x20, 0x27, "ElectricalPoteniometer" }, + { 0x20, 0x28, "ElectricalFrequency" }, + { 0x20, 0x29, "ElectricalPeriod" }, + { 0x20, 0x30, "Environmental" }, + { 0x20, 0x31, "EnvironmentalAtmosphericPressure" }, + { 0x20, 0x32, "EnvironmentalHumidity" }, + { 0x20, 0x33, "EnvironmentalTemperature" }, + { 0x20, 0x34, "EnvironmentalWindDirection" }, + { 0x20, 0x35, "EnvironmentalWindSpeed" }, + { 0x20, 0x40, "Light" }, + { 0x20, 0x41, "LightAmbientLight" }, + { 0x20, 0x42, "LightConsumerInfrared" }, + { 0x20, 0x50, "Location" }, + { 0x20, 0x51, "LocationBroadcast" }, + { 0x20, 0x52, "LocationDeadReckoning" }, + { 0x20, 0x53, "LocationGPS" }, + { 0x20, 0x54, "LocationLookup" }, + { 0x20, 0x55, "LocationOther" }, + { 0x20, 0x56, "LocationStatic" }, + { 0x20, 0x57, "LocationTriangulation" }, + { 0x20, 0x60, "Mechanical" }, + { 0x20, 0x61, "MechanicalBooleanSwitch" }, + { 0x20, 0x62, "MechanicalBooleanSwitchArray" }, + { 0x20, 0x63, "MechanicalMultivalueSwitch" }, + { 0x20, 0x64, "MechanicalForce" }, + { 0x20, 0x65, "MechanicalPressure" }, + { 0x20, 0x66, "MechanicalStrain" }, + { 0x20, 0x67, "MechanicalWeight" }, + { 0x20, 0x68, "MechanicalHapticVibrator" }, + { 0x20, 0x69, "MechanicalHallEffectSwitch" }, + { 0x20, 0x70, "Motion" }, + { 0x20, 0x71, "MotionAccelerometer1D" }, + { 0x20, 0x72, "MotionAccelerometer2D" }, + { 0x20, 0x73, "MotionAccelerometer3D" }, + { 0x20, 0x74, "MotionGyrometer1D" }, + { 0x20, 0x75, "MotionGyrometer2D" }, + { 0x20, 0x76, "MotionGyrometer3D" }, + { 0x20, 0x77, "MotionMotionDetector" }, + { 0x20, 0x78, "MotionSpeedometer" }, + { 0x20, 0x79, "MotionAccelerometer" }, + { 0x20, 0x7A, "MotionGyrometer" }, + { 0x20, 0x80, "Orientation" }, + { 0x20, 0x81, "OrientationCompass1D" }, + { 0x20, 0x82, "OrientationCompass2D" }, + { 0x20, 0x83, "OrientationCompass3D" }, + { 0x20, 0x84, "OrientationInclinometer1D" }, + { 0x20, 0x85, "OrientationInclinometer2D" }, + { 0x20, 0x86, "OrientationInclinometer3D" }, + { 0x20, 0x87, "OrientationDistance1D" }, + { 0x20, 0x88, "OrientationDistance2D" }, + { 0x20, 0x89, "OrientationDistance3D" }, + { 0x20, 0x8A, "OrientationDeviceOrientation" }, + { 0x20, 0x8B, "OrientationCompass" }, + { 0x20, 0x8C, "OrientationInclinometer" }, + { 0x20, 0x8D, "OrientationDistance" }, + { 0x20, 0x90, "Scanner" }, + { 0x20, 0x91, "ScannerBarcode" }, + { 0x20, 0x91, "ScannerRFID" }, + { 0x20, 0x91, "ScannerNFC" }, + { 0x20, 0xA0, "Time" }, + { 0x20, 0xA1, "TimeAlarmTimer" }, + { 0x20, 0xA2, "TimeRealTimeClock" }, + { 0x20, 0xE0, "Other" }, + { 0x20, 0xE1, "OtherCustom" }, + { 0x20, 0xE2, "OtherGeneric" }, + { 0x20, 0xE3, "OtherGenericEnumerator" }, { 0x84, 0, "Power Device" }, { 0x84, 0x02, "PresentStatus" }, { 0x84, 0x03, "ChangeStatus" }, @@ -579,18 +660,51 @@ void hid_debug_event(struct hid_device *hdev, char *buf) { int i; struct hid_debug_list *list; + unsigned long flags; + spin_lock_irqsave(&hdev->debug_list_lock, flags); list_for_each_entry(list, &hdev->debug_list, node) { for (i = 0; i < strlen(buf); i++) list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] = buf[i]; list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE; } + spin_unlock_irqrestore(&hdev->debug_list_lock, flags); wake_up_interruptible(&hdev->debug_wait); } EXPORT_SYMBOL_GPL(hid_debug_event); +void hid_dump_report(struct hid_device *hid, int type, u8 *data, + int size) +{ + struct hid_report_enum *report_enum; + char *buf; + unsigned int i; + + buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); + + if (!buf) + return; + + report_enum = hid->report_enum + type; + + /* dump the report */ + snprintf(buf, HID_DEBUG_BUFSIZE - 1, + "\nreport (size %u) (%snumbered) = ", size, + report_enum->numbered ? "" : "un"); + hid_debug_event(hid, buf); + + for (i = 0; i < size; i++) { + snprintf(buf, HID_DEBUG_BUFSIZE - 1, + " %02x", data[i]); + hid_debug_event(hid, buf); + } + hid_debug_event(hid, "\n"); + kfree(buf); +} +EXPORT_SYMBOL_GPL(hid_dump_report); + void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 value) { char *buf; @@ -735,6 +849,8 @@ static const char *keys[KEY_MAX + 1] = { [KEY_ALTERASE] = "AlternateErase", [KEY_CANCEL] = "Cancel", [KEY_BRIGHTNESSDOWN] = "BrightnessDown", [KEY_BRIGHTNESSUP] = "BrightnessUp", [KEY_MEDIA] = "Media", [KEY_UNKNOWN] = "Unknown", + [BTN_DPAD_UP] = "BtnDPadUp", [BTN_DPAD_DOWN] = "BtnDPadDown", + [BTN_DPAD_LEFT] = "BtnDPadLeft", [BTN_DPAD_RIGHT] = "BtnDPadRight", [BTN_0] = "Btn0", [BTN_1] = "Btn1", [BTN_2] = "Btn2", [BTN_3] = "Btn3", [BTN_4] = "Btn4", [BTN_5] = "Btn5", @@ -764,7 +880,8 @@ static const char *keys[KEY_MAX + 1] = { [BTN_TOOL_MOUSE] = "ToolMouse", [BTN_TOOL_LENS] = "ToolLens", [BTN_TOUCH] = "Touch", [BTN_STYLUS] = "Stylus", [BTN_STYLUS2] = "Stylus2", [BTN_TOOL_DOUBLETAP] = "ToolDoubleTap", - [BTN_TOOL_TRIPLETAP] = "ToolTripleTap", [BTN_GEAR_DOWN] = "WheelBtn", + [BTN_TOOL_TRIPLETAP] = "ToolTripleTap", [BTN_TOOL_QUADTAP] = "ToolQuadrupleTap", + [BTN_GEAR_DOWN] = "WheelBtn", [BTN_GEAR_UP] = "Gear up", [KEY_OK] = "Ok", [KEY_SELECT] = "Select", [KEY_GOTO] = "Goto", [KEY_CLEAR] = "Clear", [KEY_POWER2] = "Power2", @@ -819,6 +936,16 @@ static const char *keys[KEY_MAX + 1] = { [KEY_KBDILLUMDOWN] = "KbdIlluminationDown", [KEY_KBDILLUMUP] = "KbdIlluminationUp", [KEY_SWITCHVIDEOMODE] = "SwitchVideoMode", + [KEY_BUTTONCONFIG] = "ButtonConfig", + [KEY_TASKMANAGER] = "TaskManager", + [KEY_JOURNAL] = "Journal", + [KEY_CONTROLPANEL] = "ControlPanel", + [KEY_APPSELECT] = "AppSelect", + [KEY_SCREENSAVER] = "ScreenSaver", + [KEY_VOICECOMMAND] = "VoiceCommand", + [KEY_BRIGHTNESS_MIN] = "BrightnessMin", + [KEY_BRIGHTNESS_MAX] = "BrightnessMax", + [KEY_BRIGHTNESS_AUTO] = "BrightnessAuto", }; static const char *relatives[REL_MAX + 1] = { @@ -945,6 +1072,7 @@ static int hid_debug_events_open(struct inode *inode, struct file *file) { int err = 0; struct hid_debug_list *list; + unsigned long flags; if (!(list = kzalloc(sizeof(struct hid_debug_list), GFP_KERNEL))) { err = -ENOMEM; @@ -960,7 +1088,9 @@ static int hid_debug_events_open(struct inode *inode, struct file *file) file->private_data = list; mutex_init(&list->read_mutex); + spin_lock_irqsave(&list->hdev->debug_list_lock, flags); list_add_tail(&list->node, &list->hdev->debug_list); + spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags); out: return err; @@ -1054,8 +1184,11 @@ static unsigned int hid_debug_events_poll(struct file *file, poll_table *wait) static int hid_debug_events_release(struct inode *inode, struct file *file) { struct hid_debug_list *list = file->private_data; + unsigned long flags; + spin_lock_irqsave(&list->hdev->debug_list_lock, flags); list_del(&list->node); + spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags); kfree(list->hid_debug_buf); kfree(list); diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c index e832f44ae38..ce0644424f5 100644 --- a/drivers/hid/hid-dr.c +++ b/drivers/hid/hid-dr.c @@ -29,14 +29,12 @@ #include <linux/input.h> #include <linux/slab.h> -#include <linux/usb.h> #include <linux/hid.h> #include <linux/module.h> #include "hid-ids.h" #ifdef CONFIG_DRAGONRISE_FF -#include "usbhid/usbhid.h" struct drff_device { struct hid_report *report; @@ -68,7 +66,7 @@ static int drff_play(struct input_dev *dev, void *data, drff->report->field[0]->value[1] = 0x00; drff->report->field[0]->value[2] = weak; drff->report->field[0]->value[4] = strong; - usbhid_submit_report(hid, drff->report, USB_DIR_OUT); + hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT); drff->report->field[0]->value[0] = 0xfa; drff->report->field[0]->value[1] = 0xfe; @@ -80,7 +78,7 @@ static int drff_play(struct input_dev *dev, void *data, drff->report->field[0]->value[2] = 0x00; drff->report->field[0]->value[4] = 0x00; dbg_hid("running with 0x%02x 0x%02x", strong, weak); - usbhid_submit_report(hid, drff->report, USB_DIR_OUT); + hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT); return 0; } @@ -132,7 +130,7 @@ static int drff_init(struct hid_device *hid) drff->report->field[0]->value[4] = 0x00; drff->report->field[0]->value[5] = 0x00; drff->report->field[0]->value[6] = 0x00; - usbhid_submit_report(hid, drff->report, USB_DIR_OUT); + hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT); hid_info(hid, "Force Feedback for DragonRise Inc. " "game controllers by Richard Walmsley <richwalm@gmail.com>\n"); @@ -297,17 +295,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 79d0c61e721..d0bd13b62dc 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-elo.c b/drivers/hid/hid-elo.c new file mode 100644 index 00000000000..4e49462870a --- /dev/null +++ b/drivers/hid/hid-elo.c @@ -0,0 +1,306 @@ +/* + * HID driver for ELO usb touchscreen 4000/4500 + * + * Copyright (c) 2013 Jiri Slaby + * + * Data parsing taken from elousb driver by Vojtech Pavlik. + * + * This driver is licensed under the terms of GPLv2. + */ + +#include <linux/hid.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/workqueue.h> + +#include "hid-ids.h" + +#define ELO_PERIODIC_READ_INTERVAL HZ +#define ELO_SMARTSET_CMD_TIMEOUT 2000 /* msec */ + +/* Elo SmartSet commands */ +#define ELO_FLUSH_SMARTSET_RESPONSES 0x02 /* Flush all pending smartset responses */ +#define ELO_SEND_SMARTSET_COMMAND 0x05 /* Send a smartset command */ +#define ELO_GET_SMARTSET_RESPONSE 0x06 /* Get a smartset response */ +#define ELO_DIAG 0x64 /* Diagnostics command */ +#define ELO_SMARTSET_PACKET_SIZE 8 + +struct elo_priv { + struct usb_device *usbdev; + struct delayed_work work; + unsigned char buffer[ELO_SMARTSET_PACKET_SIZE]; +}; + +static struct workqueue_struct *wq; +static bool use_fw_quirk = true; +module_param(use_fw_quirk, bool, S_IRUGO); +MODULE_PARM_DESC(use_fw_quirk, "Do periodic pokes for broken M firmwares (default = true)"); + +static void elo_input_configured(struct hid_device *hdev, + struct hid_input *hidinput) +{ + struct input_dev *input = hidinput->input; + + set_bit(BTN_TOUCH, input->keybit); + set_bit(ABS_PRESSURE, input->absbit); + input_set_abs_params(input, ABS_PRESSURE, 0, 256, 0, 0); +} + +static void elo_process_data(struct input_dev *input, const u8 *data, int size) +{ + int press; + + input_report_abs(input, ABS_X, (data[3] << 8) | data[2]); + input_report_abs(input, ABS_Y, (data[5] << 8) | data[4]); + + press = 0; + if (data[1] & 0x80) + press = (data[7] << 8) | data[6]; + input_report_abs(input, ABS_PRESSURE, press); + + if (data[1] & 0x03) { + input_report_key(input, BTN_TOUCH, 1); + input_sync(input); + } + + if (data[1] & 0x04) + input_report_key(input, BTN_TOUCH, 0); + + input_sync(input); +} + +static int elo_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct hid_input *hidinput; + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || list_empty(&hdev->inputs)) + return 0; + + hidinput = list_first_entry(&hdev->inputs, struct hid_input, list); + + switch (report->id) { + case 0: + if (data[0] == 'T') { /* Mandatory ELO packet marker */ + elo_process_data(hidinput->input, data, size); + return 1; + } + break; + default: /* unknown report */ + /* Unknown report type; pass upstream */ + hid_info(hdev, "unknown report type %d\n", report->id); + break; + } + + return 0; +} + +static int elo_smartset_send_get(struct usb_device *dev, u8 command, + void *data) +{ + unsigned int pipe; + u8 dir; + + if (command == ELO_SEND_SMARTSET_COMMAND) { + pipe = usb_sndctrlpipe(dev, 0); + dir = USB_DIR_OUT; + } else if (command == ELO_GET_SMARTSET_RESPONSE) { + pipe = usb_rcvctrlpipe(dev, 0); + dir = USB_DIR_IN; + } else + return -EINVAL; + + return usb_control_msg(dev, pipe, command, + dir | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, data, ELO_SMARTSET_PACKET_SIZE, + ELO_SMARTSET_CMD_TIMEOUT); +} + +static int elo_flush_smartset_responses(struct usb_device *dev) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + ELO_FLUSH_SMARTSET_RESPONSES, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); +} + +static void elo_work(struct work_struct *work) +{ + struct elo_priv *priv = container_of(work, struct elo_priv, work.work); + struct usb_device *dev = priv->usbdev; + unsigned char *buffer = priv->buffer; + int ret; + + ret = elo_flush_smartset_responses(dev); + if (ret < 0) { + dev_err(&dev->dev, "initial FLUSH_SMARTSET_RESPONSES failed, error %d\n", + ret); + goto fail; + } + + /* send Diagnostics command */ + *buffer = ELO_DIAG; + ret = elo_smartset_send_get(dev, ELO_SEND_SMARTSET_COMMAND, buffer); + if (ret < 0) { + dev_err(&dev->dev, "send Diagnostics Command failed, error %d\n", + ret); + goto fail; + } + + /* get the result */ + ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE, buffer); + if (ret < 0) { + dev_err(&dev->dev, "get Diagnostics Command response failed, error %d\n", + ret); + goto fail; + } + + /* read the ack */ + if (*buffer != 'A') { + ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE, + buffer); + if (ret < 0) { + dev_err(&dev->dev, "get acknowledge response failed, error %d\n", + ret); + goto fail; + } + } + +fail: + ret = elo_flush_smartset_responses(dev); + if (ret < 0) + dev_err(&dev->dev, "final FLUSH_SMARTSET_RESPONSES failed, error %d\n", + ret); + queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL); +} + +/* + * Not all Elo devices need the periodic HID descriptor reads. + * Only firmware version M needs this. + */ +static bool elo_broken_firmware(struct usb_device *dev) +{ + struct usb_device *hub = dev->parent; + struct usb_device *child = NULL; + u16 fw_lvl = le16_to_cpu(dev->descriptor.bcdDevice); + u16 child_vid, child_pid; + int i; + + if (!use_fw_quirk) + return false; + if (fw_lvl != 0x10d) + return false; + + /* iterate sibling devices of the touch controller */ + usb_hub_for_each_child(hub, i, child) { + child_vid = le16_to_cpu(child->descriptor.idVendor); + child_pid = le16_to_cpu(child->descriptor.idProduct); + + /* + * If one of the devices below is present attached as a sibling of + * the touch controller then this is a newer IBM 4820 monitor that + * does not need the IBM-requested workaround if fw level is + * 0x010d - aka 'M'. + * No other HW can have this combination. + */ + if (child_vid==0x04b3) { + switch (child_pid) { + case 0x4676: /* 4820 21x Video */ + case 0x4677: /* 4820 51x Video */ + case 0x4678: /* 4820 2Lx Video */ + case 0x4679: /* 4820 5Lx Video */ + return false; + } + } + } + return true; +} + +static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct elo_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + INIT_DELAYED_WORK(&priv->work, elo_work); + priv->usbdev = interface_to_usbdev(to_usb_interface(hdev->dev.parent)); + + hid_set_drvdata(hdev, priv); + + 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; + } + + if (elo_broken_firmware(priv->usbdev)) { + hid_info(hdev, "broken firmware found, installing workaround\n"); + queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL); + } + + return 0; +err_free: + kfree(priv); + return ret; +} + +static void elo_remove(struct hid_device *hdev) +{ + struct elo_priv *priv = hid_get_drvdata(hdev); + + hid_hw_stop(hdev); + flush_workqueue(wq); + kfree(priv); +} + +static const struct hid_device_id elo_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009), }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030), }, + { } +}; +MODULE_DEVICE_TABLE(hid, elo_devices); + +static struct hid_driver elo_driver = { + .name = "elo", + .id_table = elo_devices, + .probe = elo_probe, + .remove = elo_remove, + .raw_event = elo_raw_event, + .input_configured = elo_input_configured, +}; + +static int __init elo_driver_init(void) +{ + int ret; + + wq = create_singlethread_workqueue("elousb"); + if (!wq) + return -ENOMEM; + + ret = hid_register_driver(&elo_driver); + if (ret) + destroy_workqueue(wq); + + return ret; +} +module_init(elo_driver_init); + +static void __exit elo_driver_exit(void) +{ + hid_unregister_driver(&elo_driver); + destroy_workqueue(wq); +} +module_exit(elo_driver_exit); + +MODULE_AUTHOR("Jiri Slaby <jslaby@suse.cz>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-emsff.c b/drivers/hid/hid-emsff.c index 2630d483d26..d82d75bb11f 100644 --- a/drivers/hid/hid-emsff.c +++ b/drivers/hid/hid-emsff.c @@ -23,11 +23,9 @@ #include <linux/hid.h> #include <linux/input.h> -#include <linux/usb.h> #include <linux/module.h> #include "hid-ids.h" -#include "usbhid/usbhid.h" struct emsff_device { struct hid_report *report; @@ -52,7 +50,7 @@ static int emsff_play(struct input_dev *dev, void *data, emsff->report->field[0]->value[2] = strong; dbg_hid("running with 0x%02x 0x%02x\n", strong, weak); - usbhid_submit_report(hid, emsff->report, USB_DIR_OUT); + hid_hw_request(hid, emsff->report, HID_REQ_SET_REPORT); return 0; } @@ -104,7 +102,7 @@ static int emsff_init(struct hid_device *hid) emsff->report->field[0]->value[4] = 0x00; emsff->report->field[0]->value[5] = 0x00; emsff->report->field[0]->value[6] = 0x00; - usbhid_submit_report(hid, emsff->report, USB_DIR_OUT); + hid_hw_request(hid, emsff->report, HID_REQ_SET_REPORT); hid_info(hid, "force feedback for EMS based devices by Ignaz Forster <ignaz.forster@gmx.de>\n"); @@ -150,18 +148,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 6540af2871a..212ac6be245 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 f1e1bcf6742..2d8cead3adc 100644 --- a/drivers/hid/hid-gaff.c +++ b/drivers/hid/hid-gaff.c @@ -29,13 +29,11 @@ #include <linux/input.h> #include <linux/slab.h> -#include <linux/usb.h> #include <linux/hid.h> #include <linux/module.h> #include "hid-ids.h" #ifdef CONFIG_GREENASIA_FF -#include "usbhid/usbhid.h" struct gaff_device { struct hid_report *report; @@ -63,14 +61,14 @@ static int hid_gaff_play(struct input_dev *dev, void *data, gaff->report->field[0]->value[4] = left; gaff->report->field[0]->value[5] = 0; dbg_hid("running with 0x%02x 0x%02x", left, right); - usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + hid_hw_request(hid, gaff->report, HID_REQ_SET_REPORT); gaff->report->field[0]->value[0] = 0xfa; gaff->report->field[0]->value[1] = 0xfe; gaff->report->field[0]->value[2] = 0x0; gaff->report->field[0]->value[4] = 0x0; - usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + hid_hw_request(hid, gaff->report, HID_REQ_SET_REPORT); return 0; } @@ -122,12 +120,12 @@ static int gaff_init(struct hid_device *hid) gaff->report->field[0]->value[1] = 0x00; gaff->report->field[0]->value[2] = 0x00; gaff->report->field[0]->value[3] = 0x00; - usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + hid_hw_request(hid, gaff->report, HID_REQ_SET_REPORT); gaff->report->field[0]->value[0] = 0xfa; gaff->report->field[0]->value[1] = 0xfe; - usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + hid_hw_request(hid, gaff->report, HID_REQ_SET_REPORT); hid_info(hid, "Force Feedback for GreenAsia 0x12 devices by Lukasz Lubojanski <lukasz@lubojanski.info>\n"); @@ -176,17 +174,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 a8b3148e03a..e288a4a06fe 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 4442c30ef53..288d61c9748 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 e0a5d1739fc..6e1a4a4fc0c 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-holtek-mouse.c b/drivers/hid/hid-holtek-mouse.c new file mode 100644 index 00000000000..d60fbd0adc0 --- /dev/null +++ b/drivers/hid/hid-holtek-mouse.c @@ -0,0 +1,88 @@ +/* + * HID driver for Holtek gaming mice + * Copyright (c) 2013 Christian Ohm + * Heavily inspired by various other HID drivers that adjust the report + * descriptor. +*/ + +/* + * 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/hid.h> +#include <linux/module.h> +#include <linux/usb.h> + +#include "hid-ids.h" + +/* + * The report descriptor of some Holtek based gaming mice specifies an + * excessively large number of consumer usages (2^15), which is more than + * HID_MAX_USAGES. This prevents proper parsing of the report descriptor. + * + * This driver fixes the report descriptor for: + * - USB ID 04d9:a067, sold as Sharkoon Drakonia and Perixx MX-2000 + * - USB ID 04d9:a04a, sold as Tracer Sniper TRM-503, NOVA Gaming Slider X200 + * and Zalman ZM-GM1 + * - USB ID 04d9:a081, sold as SHARKOON DarkGlider Gaming mouse + * - USB ID 04d9:a072, sold as LEETGION Hellion Gaming Mouse + */ + +static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { + /* Change usage maximum and logical maximum from 0x7fff to + * 0x2fff, so they don't exceed HID_MAX_USAGES */ + switch (hdev->product) { + case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067: + case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072: + if (*rsize >= 122 && rdesc[115] == 0xff && rdesc[116] == 0x7f + && rdesc[120] == 0xff && rdesc[121] == 0x7f) { + hid_info(hdev, "Fixing up report descriptor\n"); + rdesc[116] = rdesc[121] = 0x2f; + } + break; + case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A: + case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070: + case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081: + if (*rsize >= 113 && rdesc[106] == 0xff && rdesc[107] == 0x7f + && rdesc[111] == 0xff && rdesc[112] == 0x7f) { + hid_info(hdev, "Fixing up report descriptor\n"); + rdesc[107] = rdesc[112] = 0x2f; + } + break; + } + + } + return rdesc; +} + +static const struct hid_device_id holtek_mouse_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, + USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, + USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, + USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, + USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, + USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, + { } +}; +MODULE_DEVICE_TABLE(hid, holtek_mouse_devices); + +static struct hid_driver holtek_mouse_driver = { + .name = "holtek_mouse", + .id_table = holtek_mouse_devices, + .report_fixup = holtek_mouse_report_fixup, +}; + +module_hid_driver(holtek_mouse_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c index ff295e60059..9325545fc3a 100644 --- a/drivers/hid/hid-holtekff.c +++ b/drivers/hid/hid-holtekff.c @@ -27,12 +27,10 @@ #include <linux/input.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/usb.h> #include "hid-ids.h" #ifdef CONFIG_HOLTEK_FF -#include "usbhid/usbhid.h" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>"); @@ -100,9 +98,9 @@ static void holtekff_send(struct holtekff_device *holtekff, holtekff->field->value[i] = data[i]; } - dbg_hid("sending %*ph\n", 7, data); + dbg_hid("sending %7ph\n", data); - usbhid_submit_report(hid, holtekff->field->report, USB_DIR_OUT); + hid_hw_request(hid, holtekff->field->report, HID_REQ_SET_REPORT); } static int holtekff_play(struct input_dev *dev, void *data, @@ -224,17 +222,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-huion.c b/drivers/hid/hid-huion.c new file mode 100644 index 00000000000..cbf4da4689b --- /dev/null +++ b/drivers/hid/hid-huion.c @@ -0,0 +1,177 @@ +/* + * HID driver for Huion devices not fully compliant with HID standard + * + * Copyright (c) 2013 Martin Rusko + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/usb.h> +#include "usbhid/usbhid.h" + +#include "hid-ids.h" + +/* Original Huion 580 report descriptor size */ +#define HUION_580_RDESC_ORIG_SIZE 177 + +/* Fixed Huion 580 report descriptor */ +static __u8 huion_580_rdesc_fixed[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x07, /* Report ID (7), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x34, /* Physical Minimum (0), */ + 0x09, 0x30, /* Usage (X), */ + 0x46, 0x40, 0x1F, /* Physical Maximum (8000), */ + 0x26, 0x00, 0x7D, /* Logical Maximum (32000), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0x88, 0x13, /* Physical Maximum (5000), */ + 0x26, 0x20, 0x4E, /* Logical Maximum (20000), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x26, 0xFF, 0x07, /* Logical Maximum (2047), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + switch (hdev->product) { + case USB_DEVICE_ID_HUION_580: + if (*rsize == HUION_580_RDESC_ORIG_SIZE) { + rdesc = huion_580_rdesc_fixed; + *rsize = sizeof(huion_580_rdesc_fixed); + } + break; + } + return rdesc; +} + +/** + * Enable fully-functional tablet mode by reading special string + * descriptor. + * + * @hdev: HID device + * + * The specific string descriptor and data were discovered by sniffing + * the Windows driver traffic. + */ +static int huion_tablet_enable(struct hid_device *hdev) +{ + int rc; + char buf[22]; + + rc = usb_string(hid_to_usb_dev(hdev), 0x64, buf, sizeof(buf)); + if (rc < 0) + return rc; + + return 0; +} + +static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + /* Ignore interfaces 1 (mouse) and 2 (keyboard) for Huion 580 tablet, + * as they are not used + */ + switch (id->product) { + case USB_DEVICE_ID_HUION_580: + if (intf->cur_altsetting->desc.bInterfaceNumber != 0x00) + return -ENODEV; + break; + } + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + goto err; + } + + switch (id->product) { + case USB_DEVICE_ID_HUION_580: + ret = huion_tablet_enable(hdev); + if (ret) { + hid_err(hdev, "tablet enabling failed\n"); + goto enabling_err; + } + break; + } + + return 0; +enabling_err: + hid_hw_stop(hdev); +err: + return ret; +} + +static int huion_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + /* If this is a pen input report then invert the in-range bit */ + if (report->type == HID_INPUT_REPORT && report->id == 0x07 && size >= 2) + data[1] ^= 0x40; + + return 0; +} + +static const struct hid_device_id huion_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, + { } +}; +MODULE_DEVICE_TABLE(hid, huion_devices); + +static struct hid_driver huion_driver = { + .name = "huion", + .id_table = huion_devices, + .probe = huion_probe, + .report_fixup = huion_report_fixup, + .raw_event = huion_raw_event, +}; +module_hid_driver(huion_driver); + +MODULE_AUTHOR("Martin Rusko"); +MODULE_DESCRIPTION("Huion HID driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index 3d62781b899..f52dbcb7133 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -157,6 +157,7 @@ struct mousevsc_dev { u32 report_desc_size; struct hv_input_dev_info hid_dev_info; struct hid_device *hid_device; + u8 input_buf[HID_MAX_BUFFER_SIZE]; }; @@ -199,13 +200,11 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, if (desc->bLength == 0) goto cleanup; - input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC); + input_device->hid_desc = kmemdup(desc, desc->bLength, GFP_ATOMIC); if (!input_device->hid_desc) goto cleanup; - memcpy(input_device->hid_desc, desc, desc->bLength); - input_device->report_desc_size = desc->desc[0].wDescriptorLength; if (input_device->report_desc_size == 0) { input_device->dev_info_status = -EINVAL; @@ -258,6 +257,7 @@ static void mousevsc_on_receive(struct hv_device *device, struct synthhid_msg *hid_msg; struct mousevsc_dev *input_dev = hv_get_drvdata(device); struct synthhid_input_report *input_report; + size_t len; pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet + (packet->offset8 << 3)); @@ -302,9 +302,12 @@ static void mousevsc_on_receive(struct hv_device *device, (struct synthhid_input_report *)pipe_msg->data; if (!input_dev->init_complete) break; - hid_input_report(input_dev->hid_device, - HID_INPUT_REPORT, input_report->buffer, - input_report->header.size, 1); + + len = min(input_report->header.size, + (u32)sizeof(input_dev->input_buf)); + memcpy(input_dev->input_buf, input_report->buffer, len); + hid_input_report(input_dev->hid_device, HID_INPUT_REPORT, + input_dev->input_buf, len, 1); break; default: pr_err("unsupported hid msg type - type %d len %d", @@ -457,12 +460,22 @@ static void mousevsc_hid_stop(struct hid_device *hid) { } +static int mousevsc_hid_raw_request(struct hid_device *hid, + unsigned char report_num, + __u8 *buf, size_t len, + unsigned char rtype, + int reqtype) +{ + return 0; +} + static struct hid_ll_driver mousevsc_ll_driver = { .parse = mousevsc_hid_parse, .open = mousevsc_hid_open, .close = mousevsc_hid_close, .start = mousevsc_hid_start, .stop = mousevsc_hid_stop, + .raw_request = mousevsc_hid_raw_request, }; static struct hid_driver mousevsc_hid_driver; @@ -568,8 +581,7 @@ static int mousevsc_remove(struct hv_device *dev) static const struct hv_vmbus_device_id id_table[] = { /* Mouse guid */ - { VMBUS_DEVICE(0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, - 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A) }, + { HV_MOUSE_GUID, }, { }, }; @@ -593,6 +605,5 @@ static void __exit mousevsc_exit(void) } MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); module_init(mousevsc_init); module_exit(mousevsc_exit); diff --git a/drivers/hid/hid-icade.c b/drivers/hid/hid-icade.c index 1d6565e37ba..76b5a757078 100644 --- a/drivers/hid/hid-icade.c +++ b/drivers/hid/hid-icade.c @@ -159,7 +159,7 @@ static const struct icade_key icade_usage_table[30] = { static const struct icade_key *icade_find_translation(u16 from) { - if (from < 0 || from > ICADE_MAX_USAGE) + if (from > ICADE_MAX_USAGE) return NULL; return &icade_usage_table[from]; } @@ -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 4dfa605e2d1..48b66bbffc9 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -67,6 +67,9 @@ #define USB_VENDOR_ID_ALPS 0x0433 #define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 +#define USB_VENDOR_ID_ANTON 0x1130 +#define USB_DEVICE_ID_ANTON_TOUCH_PAD 0x3101 + #define USB_VENDOR_ID_APPLE 0x05ac #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 #define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d @@ -135,10 +138,17 @@ #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS 0x0257 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b -#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241 +#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 +#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440 +#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241 #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 +#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243 #define USB_VENDOR_ID_ASUS 0x0486 #define USB_DEVICE_ID_ASUS_T91MT 0x0185 @@ -158,6 +168,8 @@ #define USB_VENDOR_ID_ATMEL 0x03eb #define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c #define USB_DEVICE_ID_ATMEL_MXT_DIGITIZER 0x2118 +#define USB_VENDOR_ID_ATMEL_V_USB 0x16c0 +#define USB_DEVICE_ID_ATMEL_V_USB 0x05df #define USB_VENDOR_ID_AUREAL 0x0755 #define USB_DEVICE_ID_AUREAL_W01RN 0x2626 @@ -232,6 +244,10 @@ #define USB_VENDOR_ID_CYGNAL 0x10c4 #define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a +#define USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH 0x81b9 +#define USB_DEVICE_ID_CYGNAL_CP2112 0xea90 + +#define USB_DEVICE_ID_CYGNAL_RADIO_SI4713 0x8244 #define USB_VENDOR_ID_CYPRESS 0x04b4 #define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 @@ -243,6 +259,9 @@ #define USB_DEVICE_ID_CYPRESS_BARCODE_4 0xed81 #define USB_DEVICE_ID_CYPRESS_TRUETOUCH 0xc001 +#define USB_VENDOR_ID_DATA_MODUL 0x7374 +#define USB_VENDOR_ID_DATA_MODUL_EASYMAXTOUCH 0x1201 + #define USB_VENDOR_ID_DEALEXTREAME 0x10c5 #define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a @@ -267,22 +286,24 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E 0x725e #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262 0x7262 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA 0x72aa #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA 0x72aa +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4 #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 #define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34 +#define USB_VENDOR_ID_ELITEGROUP 0x03fc +#define USB_DEVICE_ID_ELITEGROUP_05D8 0x05d8 + #define USB_VENDOR_ID_ELO 0x04E7 #define USB_DEVICE_ID_ELO_TS2515 0x0022 #define USB_DEVICE_ID_ELO_TS2700 0x0020 @@ -302,10 +323,14 @@ #define USB_VENDOR_ID_ETURBOTOUCH 0x22b9 #define USB_DEVICE_ID_ETURBOTOUCH 0x0006 +#define USB_DEVICE_ID_ETURBOTOUCH_2968 0x2968 #define USB_VENDOR_ID_EZKEY 0x0518 #define USB_DEVICE_ID_BTC_8193 0x0002 +#define USB_VENDOR_ID_FORMOSA 0x147a +#define USB_DEVICE_ID_FORMOSA_IR_RECEIVER 0xe03e + #define USB_VENDOR_ID_FREESCALE 0x15A2 #define USB_DEVICE_ID_FREESCALE_MX28 0x004F @@ -319,6 +344,11 @@ #define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc #define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003 #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0101 0x0101 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0102 0x0102 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0106 0x0106 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100 #define USB_VENDOR_ID_GLAB 0x06c2 #define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 @@ -417,6 +447,9 @@ #define USB_DEVICE_ID_UGCI_FLYING 0x0020 #define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 +#define USB_VENDOR_ID_HUION 0x256c +#define USB_DEVICE_ID_HUION_580 0x006e + #define USB_VENDOR_ID_IDEACOM 0x1cb6 #define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650 #define USB_DEVICE_ID_IDEACOM_IDC6651 0x6651 @@ -424,6 +457,15 @@ #define USB_VENDOR_ID_ILITEK 0x222a #define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001 +#define USB_VENDOR_ID_INTEL_0 0x8086 +#define USB_VENDOR_ID_INTEL_1 0x8087 +#define USB_DEVICE_ID_INTEL_HID_SENSOR_0 0x09fa +#define USB_DEVICE_ID_INTEL_HID_SENSOR_1 0x0a04 + +#define USB_VENDOR_ID_STM_0 0x0483 +#define USB_DEVICE_ID_STM_HID_SENSOR 0x91d1 +#define USB_DEVICE_ID_STM_HID_SENSOR_1 0x9100 + #define USB_VENDOR_ID_ION 0x15e4 #define USB_DEVICE_ID_ICADE 0x0132 @@ -432,6 +474,11 @@ #define USB_VENDOR_ID_HOLTEK_ALT 0x04d9 #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD 0xa055 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A 0xa04a +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067 0xa067 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070 0xa070 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072 0xa072 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081 0xa081 #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 @@ -439,9 +486,16 @@ #define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615 #define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070 +#define USB_VENDOR_ID_JABRA 0x0b0e +#define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 +#define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420 + #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 @@ -456,6 +510,9 @@ #define USB_VENDOR_ID_KYE 0x0458 #define USB_DEVICE_ID_KYE_ERGO_525V 0x0087 +#define USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE 0x0138 +#define USB_DEVICE_ID_GENIUS_MANTICORE 0x0153 +#define USB_DEVICE_ID_GENIUS_GX_IMPERATOR 0x4018 #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 #define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010 #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 @@ -517,13 +574,14 @@ #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211 #define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 +#define USB_DEVICE_ID_LOGITECH_DUAL_ACTION 0xc216 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 #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 @@ -543,6 +601,7 @@ #define USB_DEVICE_ID_DINOVO_EDGE 0xc714 #define USB_DEVICE_ID_DINOVO_MINI 0xc71f #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2 0xca03 +#define USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL 0xca04 #define USB_VENDOR_ID_LUMIO 0x202e #define USB_DEVICE_ID_CRYSTALTOUCH 0x0006 @@ -567,13 +626,18 @@ #define USB_VENDOR_ID_MICROSOFT 0x045e #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b +#define USB_DEVICE_ID_MS_OFFICE_KB 0x0048 #define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d #define USB_DEVICE_ID_MS_NE4K 0x00db +#define USB_DEVICE_ID_MS_NE4K_JP 0x00dc #define USB_DEVICE_ID_MS_LK6K 0x00f9 #define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701 #define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730 #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c +#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 +#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 +#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 #define USB_VENDOR_ID_MOJO 0x8282 #define USB_DEVICE_ID_RETRO_ADAPTER 0x3201 @@ -581,6 +645,9 @@ #define USB_VENDOR_ID_MONTEREY 0x0566 #define USB_DEVICE_ID_GENIUS_KB29E 0x3004 +#define USB_VENDOR_ID_MSI 0x1770 +#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00 + #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400 #define USB_DEVICE_ID_N_S_HARMONY 0xc359 @@ -594,11 +661,16 @@ #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_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750 0x0110 + #define USB_VENDOR_ID_NEXTWINDOW 0x1926 #define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN 0x0003 #define USB_VENDOR_ID_NINTENDO 0x057e #define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306 +#define USB_DEVICE_ID_NINTENDO_WIIMOTE2 0x0330 #define USB_VENDOR_ID_NOVATEK 0x0603 #define USB_DEVICE_ID_NOVATEK_PCT 0x0600 @@ -624,6 +696,7 @@ #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16 0x0012 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17 0x0013 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18 0x0014 +#define USB_DEVICE_ID_NTRIG_DUOSENSE 0x1500 #define USB_VENDOR_ID_ONTRAK 0x0a07 #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 @@ -643,6 +716,8 @@ #define USB_VENDOR_ID_PENMOUNT 0x14e1 #define USB_DEVICE_ID_PENMOUNT_PCI 0x3500 +#define USB_DEVICE_ID_PENMOUNT_1610 0x1610 +#define USB_DEVICE_ID_PENMOUNT_1640 0x1640 #define USB_VENDOR_ID_PETALYNX 0x18b1 #define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 @@ -672,26 +747,40 @@ #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008 +#define USB_VENDOR_ID_REALTEK 0x0bda +#define USB_DEVICE_ID_REALTEK_READER 0x0152 + #define USB_VENDOR_ID_ROCCAT 0x1e7d #define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 #define USB_DEVICE_ID_ROCCAT_ISKU 0x319c +#define USB_DEVICE_ID_ROCCAT_ISKUFX 0x3264 #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 +#define USB_DEVICE_ID_ROCCAT_KONEPURE 0x2dbe +#define USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL 0x2db4 #define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22 #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 #define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 +#define USB_DEVICE_ID_ROCCAT_RYOS_MK 0x3138 +#define USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW 0x31ce +#define USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO 0x3232 #define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a #define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 #define USB_DEVICE_ID_SAITEK_PS1000 0x0621 +#define USB_DEVICE_ID_SAITEK_RAT7 0x0cd7 +#define USB_DEVICE_ID_SAITEK_MMO7 0x0cd0 #define USB_VENDOR_ID_SAMSUNG 0x0419 #define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 #define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE 0x0600 +#define USB_VENDOR_ID_SEMICO 0x1a2c +#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD 0x0023 + #define USB_VENDOR_ID_SENNHEISER 0x1395 #define USB_DEVICE_ID_SENNHEISER_BTD500USB 0x002c @@ -701,14 +790,24 @@ #define USB_VENDOR_ID_SIGMATEL 0x066F #define USB_DEVICE_ID_SIGMATEL_STMP3780 0x3780 +#define USB_VENDOR_ID_SIS_TOUCH 0x0457 +#define USB_DEVICE_ID_SIS9200_TOUCH 0x9200 +#define USB_DEVICE_ID_SIS817_TOUCH 0x0817 +#define USB_DEVICE_ID_SIS_TS 0x1013 +#define USB_DEVICE_ID_SIS1030_TOUCH 0x1030 + #define USB_VENDOR_ID_SKYCABLE 0x1223 #define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07 #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_PS4_CONTROLLER 0x05c4 #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f +#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002 +#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000 #define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034 @@ -723,6 +822,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 @@ -743,6 +845,17 @@ #define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009 #define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 #define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 +#define USB_DEVICE_ID_SYNAPTICS_LTS1 0x0af8 +#define USB_DEVICE_ID_SYNAPTICS_LTS2 0x1d10 +#define USB_DEVICE_ID_SYNAPTICS_HD 0x0ac3 +#define USB_DEVICE_ID_SYNAPTICS_QUAD_HD 0x1ac3 +#define USB_DEVICE_ID_SYNAPTICS_TP_V103 0x5710 + +#define USB_VENDOR_ID_TEXAS_INSTRUMENTS 0x2047 +#define USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA 0x0855 + +#define USB_VENDOR_ID_THINGM 0x27b8 +#define USB_DEVICE_ID_BLINK1 0x01ed #define USB_VENDOR_ID_THRUSTMASTER 0x044f @@ -791,6 +904,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 @@ -827,12 +946,18 @@ #define USB_DEVICE_ID_SUPER_DUAL_BOX_PRO 0x8802 #define USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO 0x8804 +#define USB_VENDOR_ID_WISTRON 0x0fb8 +#define USB_DEVICE_ID_WISTRON_OPTICAL_TOUCH 0x1109 + #define USB_VENDOR_ID_X_TENSIONS 0x1ae7 #define USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE 0x9001 #define USB_VENDOR_ID_XAT 0x2505 #define USB_DEVICE_ID_XAT_CSR 0x0220 +#define USB_VENDOR_ID_XIN_MO 0x16c0 +#define USB_DEVICE_ID_XIN_MO_DUAL_ARCADE 0x05e1 + #define USB_VENDOR_ID_XIROKU 0x1477 #define USB_DEVICE_ID_XIROKU_SPX 0x1006 #define USB_DEVICE_ID_XIROKU_MPX 0x1007 @@ -858,4 +983,8 @@ #define USB_VENDOR_ID_PRIMAX 0x0461 #define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05 + +#define USB_VENDOR_ID_RISO_KAGAKU 0x1294 /* Riso Kagaku Corp. */ +#define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320 /* Webmail Notifier */ + #endif diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 21b196c394b..2619f7f4517 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -192,6 +192,7 @@ static int hidinput_setkeycode(struct input_dev *dev, return -EINVAL; } + /** * hidinput_calc_abs_res - calculate an absolute axis resolution * @field: the HID report field to calculate resolution for @@ -234,23 +235,17 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) case ABS_MT_TOOL_Y: case ABS_MT_TOUCH_MAJOR: case ABS_MT_TOUCH_MINOR: - if (field->unit & 0xffffff00) /* Not a length */ - return 0; - unit_exponent += hid_snto32(field->unit >> 4, 4) - 1; - switch (field->unit & 0xf) { - case 0x1: /* If centimeters */ + if (field->unit == 0x11) { /* If centimeters */ /* Convert to millimeters */ unit_exponent += 1; - break; - case 0x3: /* If inches */ + } else if (field->unit == 0x13) { /* If inches */ /* Convert to millimeters */ prev = physical_extents; physical_extents *= 254; if (physical_extents < prev) return 0; unit_exponent -= 1; - break; - default: + } else { return 0; } break; @@ -340,7 +335,7 @@ static int hidinput_get_battery_property(struct power_supply *psy, { struct hid_device *dev = container_of(psy, struct hid_device, battery); int ret = 0; - __u8 buf[2] = {}; + __u8 *buf; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: @@ -349,21 +344,29 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - ret = dev->hid_get_raw_report(dev, dev->battery_report_id, - buf, sizeof(buf), - dev->battery_report_type); + + buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + break; + } + ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2, + dev->battery_report_type, + HID_REQ_GET_REPORT); if (ret != 2) { - if (ret >= 0) - ret = -EINVAL; + ret = -ENODATA; + kfree(buf); break; } + ret = 0; if (dev->battery_min < dev->battery_max && buf[1] >= dev->battery_min && buf[1] <= dev->battery_max) val->intval = (100 * (buf[1] - dev->battery_min)) / (dev->battery_max - dev->battery_min); + kfree(buf); break; case POWER_SUPPLY_PROP_MODEL_NAME: @@ -477,6 +480,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel if (field->flags & HID_MAIN_ITEM_CONSTANT) goto ignore; + /* Ignore if report count is out of bounds. */ + if (field->report_count < 1) + goto ignore; + /* only LED usages are supported in output fields */ if (field->report_type == HID_OUTPUT_REPORT && (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { @@ -677,9 +684,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; case 0x46: /* TabletPick */ + case 0x5a: /* SecondaryBarrelSwitch */ map_key_clear(BTN_STYLUS2); break; + case 0x5b: /* TransducerSerialNumber */ + set_bit(MSC_SERIAL, input->mscbit); + break; + default: goto unknown; } break; @@ -714,6 +726,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x06c: map_key_clear(KEY_YELLOW); break; case 0x06d: map_key_clear(KEY_ZOOM); break; + case 0x06f: map_key_clear(KEY_BRIGHTNESSUP); break; + case 0x070: map_key_clear(KEY_BRIGHTNESSDOWN); break; + case 0x072: map_key_clear(KEY_BRIGHTNESS_TOGGLE); break; + case 0x073: map_key_clear(KEY_BRIGHTNESS_MIN); break; + case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX); break; + case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO); break; + case 0x082: map_key_clear(KEY_VIDEO_NEXT); break; case 0x083: map_key_clear(KEY_LAST); break; case 0x084: map_key_clear(KEY_ENTER); break; @@ -754,6 +773,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x0bf: map_key_clear(KEY_SLOW); break; case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break; + case 0x0cf: map_key_clear(KEY_VOICECOMMAND); break; case 0x0e0: map_abs_clear(ABS_VOLUME); break; case 0x0e2: map_key_clear(KEY_MUTE); break; case 0x0e5: map_key_clear(KEY_BASSBOOST); break; @@ -761,6 +781,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break; case 0x0f5: map_key_clear(KEY_SLOW); break; + case 0x181: map_key_clear(KEY_BUTTONCONFIG); break; case 0x182: map_key_clear(KEY_BOOKMARKS); break; case 0x183: map_key_clear(KEY_CONFIG); break; case 0x184: map_key_clear(KEY_WORDPROCESSOR); break; @@ -774,6 +795,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x18c: map_key_clear(KEY_VOICEMAIL); break; case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break; case 0x18e: map_key_clear(KEY_CALENDAR); break; + case 0x18f: map_key_clear(KEY_TASKMANAGER); break; + case 0x190: map_key_clear(KEY_JOURNAL); break; case 0x191: map_key_clear(KEY_FINANCE); break; case 0x192: map_key_clear(KEY_CALC); break; case 0x193: map_key_clear(KEY_PLAYER); break; @@ -782,10 +805,16 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x199: map_key_clear(KEY_CHAT); break; case 0x19c: map_key_clear(KEY_LOGOFF); break; case 0x19e: map_key_clear(KEY_COFFEE); break; + case 0x19f: map_key_clear(KEY_CONTROLPANEL); break; + case 0x1a2: map_key_clear(KEY_APPSELECT); break; + case 0x1a3: map_key_clear(KEY_NEXT); break; + case 0x1a4: map_key_clear(KEY_PREVIOUS); break; case 0x1a6: map_key_clear(KEY_HELP); break; case 0x1a7: map_key_clear(KEY_DOCUMENTS); break; case 0x1ab: map_key_clear(KEY_SPELLCHECK); break; case 0x1ae: map_key_clear(KEY_KEYBOARD); break; + case 0x1b1: map_key_clear(KEY_SCREENSAVER); break; + case 0x1b4: map_key_clear(KEY_FILE); break; case 0x1b6: map_key_clear(KEY_IMAGES); break; case 0x1b7: map_key_clear(KEY_AUDIO); break; case 0x1b8: map_key_clear(KEY_VIDEO); break; @@ -1042,9 +1071,14 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct /* * Ignore out-of-range values as per HID specification, - * section 5.10 and 6.2.25 + * section 5.10 and 6.2.25. + * + * The logical_minimum < logical_maximum check is done so that we + * don't unintentionally discard values sent by devices which + * don't specify logical min and max. */ if ((field->flags & HID_MAIN_ITEM_VARIABLE) && + (field->logical_minimum < field->logical_maximum) && (value < field->logical_minimum || value > field->logical_maximum)) { dbg_hid("Ignoring out-of-range value %x\n", value); @@ -1132,6 +1166,77 @@ unsigned int hidinput_count_leds(struct hid_device *hid) } EXPORT_SYMBOL_GPL(hidinput_count_leds); +static void hidinput_led_worker(struct work_struct *work) +{ + struct hid_device *hid = container_of(work, struct hid_device, + led_work); + struct hid_field *field; + struct hid_report *report; + int len, ret; + __u8 *buf; + + field = hidinput_get_led_field(hid); + if (!field) + return; + + /* + * field->report is accessed unlocked regarding HID core. So there might + * be another incoming SET-LED request from user-space, which changes + * the LED state while we assemble our outgoing buffer. However, this + * doesn't matter as hid_output_report() correctly converts it into a + * boolean value no matter what information is currently set on the LED + * field (even garbage). So the remote device will always get a valid + * request. + * And in case we send a wrong value, a next led worker is spawned + * for every SET-LED request so the following worker will send the + * correct value, guaranteed! + */ + + report = field->report; + + /* use custom SET_REPORT request if possible (asynchronous) */ + if (hid->ll_driver->request) + return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT); + + /* fall back to generic raw-output-report */ + len = ((report->size - 1) >> 3) + 1 + (report->id > 0); + buf = hid_alloc_report_buf(report, GFP_KERNEL); + if (!buf) + return; + + hid_output_report(report, buf); + /* synchronous output report */ + ret = hid_hw_output_report(hid, buf, len); + if (ret == -ENOSYS) + hid_hw_raw_request(hid, report->id, buf, len, HID_OUTPUT_REPORT, + HID_REQ_SET_REPORT); + kfree(buf); +} + +static int hidinput_input_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct hid_field *field; + int offset; + + if (type == EV_FF) + return input_ff_event(dev, type, code, value); + + if (type != EV_LED) + return -1; + + if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) { + hid_warn(dev, "event field not found\n"); + return -1; + } + + hid_set_field(field, offset, value); + + schedule_work(&hid->led_work); + return 0; +} + static int hidinput_open(struct input_dev *dev) { struct hid_device *hid = input_get_drvdata(dev); @@ -1155,7 +1260,11 @@ static void report_features(struct hid_device *hid) rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; list_for_each_entry(rep, &rep_enum->report_list, list) - for (i = 0; i < rep->maxfield; i++) + for (i = 0; i < rep->maxfield; i++) { + /* Ignore if report count is out of bounds. */ + if (rep->field[i]->report_count < 1) + continue; + for (j = 0; j < rep->field[i]->maxusage; j++) { /* Verify if Battery Strength feature is available */ hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); @@ -1164,6 +1273,7 @@ static void report_features(struct hid_device *hid) drv->feature_mapping(hid, rep->field[i], rep->field[i]->usage + j); } + } } static struct hid_input *hidinput_allocate(struct hid_device *hid) @@ -1178,7 +1288,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid) } input_set_drvdata(input_dev, hid); - input_dev->event = hid->ll_driver->hidinput_input_event; + input_dev->event = hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; input_dev->setkeycode = hidinput_setkeycode; @@ -1191,13 +1301,74 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid) input_dev->id.vendor = hid->vendor; input_dev->id.product = hid->product; input_dev->id.version = hid->version; - input_dev->dev.parent = hid->dev.parent; + input_dev->dev.parent = &hid->dev; hidinput->input = input_dev; list_add_tail(&hidinput->list, &hid->inputs); return hidinput; } +static bool hidinput_has_been_populated(struct hid_input *hidinput) +{ + int i; + unsigned long r = 0; + + for (i = 0; i < BITS_TO_LONGS(EV_CNT); i++) + r |= hidinput->input->evbit[i]; + + for (i = 0; i < BITS_TO_LONGS(KEY_CNT); i++) + r |= hidinput->input->keybit[i]; + + for (i = 0; i < BITS_TO_LONGS(REL_CNT); i++) + r |= hidinput->input->relbit[i]; + + for (i = 0; i < BITS_TO_LONGS(ABS_CNT); i++) + r |= hidinput->input->absbit[i]; + + for (i = 0; i < BITS_TO_LONGS(MSC_CNT); i++) + r |= hidinput->input->mscbit[i]; + + for (i = 0; i < BITS_TO_LONGS(LED_CNT); i++) + r |= hidinput->input->ledbit[i]; + + for (i = 0; i < BITS_TO_LONGS(SND_CNT); i++) + r |= hidinput->input->sndbit[i]; + + for (i = 0; i < BITS_TO_LONGS(FF_CNT); i++) + r |= hidinput->input->ffbit[i]; + + for (i = 0; i < BITS_TO_LONGS(SW_CNT); i++) + r |= hidinput->input->swbit[i]; + + return !!r; +} + +static void hidinput_cleanup_hidinput(struct hid_device *hid, + struct hid_input *hidinput) +{ + struct hid_report *report; + int i, k; + + list_del(&hidinput->list); + input_free_device(hidinput->input); + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { + if (k == HID_OUTPUT_REPORT && + hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) + continue; + + list_for_each_entry(report, &hid->report_enum[k].report_list, + list) { + + for (i = 0; i < report->maxfield; i++) + if (report->field[i]->hidinput == hidinput) + report->field[i]->hidinput = NULL; + } + } + + kfree(hidinput); +} + /* * Register the input device; print a message. * Configure the input layer interface @@ -1212,6 +1383,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) int i, j, k; INIT_LIST_HEAD(&hid->inputs); + INIT_WORK(&hid->led_work, hidinput_led_worker); if (!force) { for (i = 0; i < hid->maxcollection; i++) { @@ -1249,6 +1421,10 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) hidinput_configure_usage(hidinput, report->field[i], report->field[i]->usage + j); + if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) && + !hidinput_has_been_populated(hidinput)) + continue; + if (hid->quirks & HID_QUIRK_MULTI_INPUT) { /* This will leave hidinput NULL, so that it * allocates another one if we have more inputs on @@ -1265,6 +1441,18 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) } } + if (hidinput && (hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) && + !hidinput_has_been_populated(hidinput)) { + /* no need to register an input device not populated */ + hidinput_cleanup_hidinput(hid, hidinput); + hidinput = NULL; + } + + if (list_empty(&hid->inputs)) { + hid_err(hid, "No inputs registered, leaving\n"); + goto out_unwind; + } + if (hidinput) { if (drv->input_configured) drv->input_configured(hid, hidinput); @@ -1297,6 +1485,12 @@ void hidinput_disconnect(struct hid_device *hid) input_unregister_device(hidinput->input); kfree(hidinput); } + + /* led_work is spawned by input_dev callbacks, but doesn't access the + * parent input_dev at all. Once all input devices are removed, we + * know that led_work will never get restarted, so we can cancel it + * synchronously and are safe. */ + cancel_work_sync(&hid->led_work); } EXPORT_SYMBOL_GPL(hidinput_disconnect); diff --git a/drivers/hid/hid-kensington.c b/drivers/hid/hid-kensington.c index a5b4016e9bd..fe9a99dd8d0 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 07cd825f6f0..3074671b7d6 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 b4f0d8216fd..e7769636759 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -16,8 +16,6 @@ #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h> -#include <linux/usb.h> -#include "usbhid/usbhid.h" #include "hid-ids.h" @@ -270,6 +268,26 @@ static __u8 easypen_m610x_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +static __u8 *kye_consumer_control_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize, int offset, const char *device_name) { + /* + * the fixup that need to be done: + * - change Usage Maximum in the Comsumer Control + * (report ID 3) to a reasonable value + */ + if (*rsize >= offset + 31 && + /* Usage Page (Consumer Devices) */ + rdesc[offset] == 0x05 && rdesc[offset + 1] == 0x0c && + /* Usage (Consumer Control) */ + rdesc[offset + 2] == 0x09 && rdesc[offset + 3] == 0x01 && + /* Usage Maximum > 12287 */ + rdesc[offset + 10] == 0x2a && rdesc[offset + 12] > 0x2f) { + hid_info(hdev, "fixing up %s report descriptor\n", device_name); + rdesc[offset + 12] = 0x2f; + } + return rdesc; +} + static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { @@ -316,6 +334,18 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(easypen_m610x_rdesc_fixed); } break; + case USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE: + rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 104, + "Genius Gila Gaming Mouse"); + break; + case USB_DEVICE_ID_GENIUS_GX_IMPERATOR: + rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 83, + "Genius Gx Imperator Keyboard"); + break; + case USB_DEVICE_ID_GENIUS_MANTICORE: + rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 104, + "Genius Manticore Keyboard"); + break; } return rdesc; } @@ -361,7 +391,7 @@ static int kye_tablet_enable(struct hid_device *hdev) value[4] = 0x00; value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hdev, report, USB_DIR_OUT); + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); return 0; } @@ -392,6 +422,14 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id) goto enabling_err; } break; + case USB_DEVICE_ID_GENIUS_MANTICORE: + /* + * The manticore keyboard needs to have all the interfaces + * opened at least once to be fully functional. + */ + if (hid_hw_open(hdev)) + hid_hw_close(hdev); + break; } return 0; @@ -409,6 +447,12 @@ static const struct hid_device_id kye_devices[] = { USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, + USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, + USB_DEVICE_ID_GENIUS_GX_IMPERATOR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, + USB_DEVICE_ID_GENIUS_MANTICORE) }, { } }; MODULE_DEVICE_TABLE(hid, kye_devices); @@ -419,17 +463,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 22bc14abdfa..6424cfdb773 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 cea016e94f4..2d25b6cbbc0 100644 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ b/drivers/hid/hid-lenovo-tpkbd.c @@ -14,11 +14,9 @@ #include <linux/module.h> #include <linux/sysfs.h> #include <linux/device.h> -#include <linux/usb.h> #include <linux/hid.h> #include <linux/input.h> #include <linux/leds.h> -#include "usbhid/usbhid.h" #include "hid-ids.h" @@ -41,10 +39,9 @@ static int tpkbd_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { - struct usbhid_device *uhdev; - - uhdev = (struct usbhid_device *) hdev->driver_data; - if (uhdev->ifnum == 1 && usage->hid == (HID_UP_BUTTON | 0x0010)) { + if (usage->hid == (HID_UP_BUTTON | 0x0010)) { + /* mark the device as pointer */ + hid_set_drvdata(hdev, (void *)1); map_key_clear(KEY_MICMUTE); return 1; } @@ -68,7 +65,7 @@ static int tpkbd_features_set(struct hid_device *hdev) report->field[2]->value[0] = data_pointer->sensitivity; report->field[3]->value[0] = data_pointer->press_speed; - usbhid_submit_report(hdev, report, USB_DIR_OUT); + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); return 0; } @@ -228,8 +225,6 @@ static ssize_t pointer_press_speed_show(struct device *dev, struct hid_device *hdev = container_of(dev, struct hid_device, dev); struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - data_pointer = hid_get_drvdata(hdev); - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_speed); } @@ -332,7 +327,7 @@ static void tpkbd_led_brightness_set(struct led_classdev *led_cdev, report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3]; report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1; report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1; - usbhid_submit_report(hdev, report, USB_DIR_OUT); + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); } static int tpkbd_probe_tp(struct hid_device *hdev) @@ -341,14 +336,24 @@ static int tpkbd_probe_tp(struct hid_device *hdev) struct tpkbd_data_pointer *data_pointer; size_t name_sz = strlen(dev_name(dev)) + 16; char *name_mute, *name_micmute; - int ret; + int i; + + /* Validate required reports. */ + for (i = 0; i < 4; i++) { + if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1)) + return -ENODEV; + } + if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2)) + return -ENODEV; if (sysfs_create_group(&hdev->dev.kobj, &tpkbd_attr_group_pointer)) { hid_warn(hdev, "Could not create sysfs group\n"); } - data_pointer = kzalloc(sizeof(struct tpkbd_data_pointer), GFP_KERNEL); + data_pointer = devm_kzalloc(&hdev->dev, + sizeof(struct tpkbd_data_pointer), + GFP_KERNEL); if (data_pointer == NULL) { hid_err(hdev, "Could not allocate memory for driver data\n"); return -ENOMEM; @@ -358,20 +363,13 @@ static int tpkbd_probe_tp(struct hid_device *hdev) data_pointer->sensitivity = 0xa0; data_pointer->press_speed = 0x38; - name_mute = kzalloc(name_sz, GFP_KERNEL); - if (name_mute == NULL) { + name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); + name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); + if (name_mute == NULL || name_micmute == NULL) { hid_err(hdev, "Could not allocate memory for led data\n"); - ret = -ENOMEM; - goto err; + return -ENOMEM; } snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev)); - - name_micmute = kzalloc(name_sz, GFP_KERNEL); - if (name_micmute == NULL) { - hid_err(hdev, "Could not allocate memory for led data\n"); - ret = -ENOMEM; - goto err2; - } snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev)); hid_set_drvdata(hdev, data_pointer); @@ -391,39 +389,36 @@ static int tpkbd_probe_tp(struct hid_device *hdev) tpkbd_features_set(hdev); return 0; - -err2: - kfree(name_mute); -err: - kfree(data_pointer); - return ret; } static int tpkbd_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; - struct usbhid_device *uhdev; ret = hid_parse(hdev); if (ret) { hid_err(hdev, "hid_parse failed\n"); - goto err_free; + goto err; } ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hid_hw_start failed\n"); - goto err_free; + goto err; } - uhdev = (struct usbhid_device *) hdev->driver_data; - - if (uhdev->ifnum == 1) - return tpkbd_probe_tp(hdev); + if (hid_get_drvdata(hdev)) { + hid_set_drvdata(hdev, NULL); + ret = tpkbd_probe_tp(hdev); + if (ret) + goto err_hid; + } return 0; -err_free: +err_hid: + hid_hw_stop(hdev); +err: return ret; } @@ -438,17 +433,11 @@ static void tpkbd_remove_tp(struct hid_device *hdev) led_classdev_unregister(&data_pointer->led_mute); hid_set_drvdata(hdev, NULL); - kfree(data_pointer->led_micmute.name); - kfree(data_pointer->led_mute.name); - kfree(data_pointer); } static void tpkbd_remove(struct hid_device *hdev) { - struct usbhid_device *uhdev; - - uhdev = (struct usbhid_device *) hdev->driver_data; - if (uhdev->ifnum == 1) + if (hid_get_drvdata(hdev)) tpkbd_remove_tp(hdev); hid_hw_stop(hdev); @@ -468,18 +457,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 a2f8e88b9fa..a976f48263f 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,88 @@ #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 FV_RDESC_ORIG_SIZE 130 +#define MOMO_RDESC_ORIG_SIZE 87 +#define MOMO2_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 +172,166 @@ static __u8 dfp_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +static __u8 fv_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, 0x0C, /* Report Count (12), */ +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, 0x0C, /* Usage Maximum (0Ch), */ +0x81, 0x02, /* Input (Variable), */ +0x95, 0x02, /* Report Count (2), */ +0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ +0x09, 0x01, /* Usage (01h), */ +0x81, 0x02, /* Input (Variable), */ +0x09, 0x02, /* Usage (02h), */ +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), */ +0x05, 0x01, /* Usage Page (Desktop), */ +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, */ +0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ +0x09, 0x01, /* Usage (01h), */ +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, 0x32, /* Usage (Z), */ +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 (03h), */ +0x91, 0x02, /* Output (Variable), */ +0xC0, /* End Collection, */ +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 */ +}; + +static __u8 momo2_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, 0x0A, /* Report Count (10), */ +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, 0x0A, /* Usage Maximum (0Ah), */ +0x81, 0x02, /* Input (Variable), */ +0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ +0x09, 0x00, /* Usage (00h), */ +0x95, 0x04, /* Report Count (4), */ +0x81, 0x02, /* Input (Variable), */ +0x95, 0x01, /* Report Count (1), */ +0x75, 0x08, /* Report Size (8), */ +0x26, 0xFF, 0x00, /* Logical Maximum (255), */ +0x46, 0xFF, 0x00, /* Physical Maximum (255), */ +0x09, 0x01, /* Usage (01h), */ +0x81, 0x02, /* Input (Variable), */ +0x05, 0x01, /* Usage Page (Desktop), */ +0x09, 0x31, /* Usage (Y), */ +0x81, 0x02, /* Input (Variable), */ +0x09, 0x32, /* Usage (Z), */ +0x81, 0x02, /* Input (Variable), */ +0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ +0x09, 0x00, /* Usage (00h), */ +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 +342,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 +359,57 @@ 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_MOMO_WHEEL2: + if (*rsize == MOMO2_RDESC_ORIG_SIZE) { + hid_info(hdev, + "fixing up Logitech Momo Racing Force (Black) report descriptor\n"); + rdesc = momo2_rdesc_fixed; + *rsize = sizeof(momo2_rdesc_fixed); + } + break; + + case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL: + if (*rsize == FV_RDESC_ORIG_SIZE) { + hid_info(hdev, + "fixing up Logitech Formula Vibration report descriptor\n"); + rdesc = fv_rdesc_fixed; + *rsize = sizeof(fv_rdesc_fixed); + } + break; + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: if (*rsize == DFP_RDESC_ORIG_SIZE) { hid_info(hdev, @@ -143,6 +418,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 +614,27 @@ 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: + case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL: + field->application = HID_GD_MULTIAXIS; + break; + default: + break; + } + } + return 0; } @@ -385,7 +692,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) { unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); if (ret >= 0) { /* insert a little delay of 10 jiffies ~ 40ms */ @@ -397,7 +705,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) buf[1] = 0xB2; get_random_bytes(&buf[2], 2); - ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); } } @@ -451,6 +760,8 @@ static const struct hid_device_id lg_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D), .driver_data = LG_NOGET }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION), + .driver_data = LG_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), .driver_data = LG_NOGET | LG_FF4 }, @@ -465,9 +776,11 @@ 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_VIBRATION_WHEEL), + .driver_data = LG_FF2 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), .driver_data = LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL), @@ -503,17 +816,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-lg2ff.c b/drivers/hid/hid-lg2ff.c index 3c31bc650e5..0e3fb1a7e42 100644 --- a/drivers/hid/hid-lg2ff.c +++ b/drivers/hid/hid-lg2ff.c @@ -23,10 +23,8 @@ #include <linux/input.h> #include <linux/slab.h> -#include <linux/usb.h> #include <linux/hid.h> -#include "usbhid/usbhid.h" #include "hid-lg.h" struct lg2ff_device { @@ -56,7 +54,7 @@ static int play_effect(struct input_dev *dev, void *data, lg2ff->report->field[0]->value[4] = 0x00; } - usbhid_submit_report(hid, lg2ff->report, USB_DIR_OUT); + hid_hw_request(hid, lg2ff->report, HID_REQ_SET_REPORT); return 0; } @@ -66,26 +64,13 @@ int lg2ff_init(struct hid_device *hid) struct hid_report *report; struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = - &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; int error; - if (list_empty(report_list)) { - hid_err(hid, "no output report found\n"); + /* Check that the report looks ok */ + report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7); + if (!report) return -ENODEV; - } - - report = list_entry(report_list->next, struct hid_report, list); - - if (report->maxfield < 1) { - hid_err(hid, "output report is empty\n"); - return -ENODEV; - } - if (report->field[0]->report_count < 7) { - hid_err(hid, "not enough values in the field\n"); - return -ENODEV; - } lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL); if (!lg2ff) @@ -108,9 +93,9 @@ int lg2ff_init(struct hid_device *hid) report->field[0]->value[5] = 0x00; report->field[0]->value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); - hid_info(hid, "Force feedback for Logitech RumblePad/Rumblepad 2 by Anssi Hannula <anssi.hannula@gmail.com>\n"); + hid_info(hid, "Force feedback for Logitech variant 2 rumble devices by Anssi Hannula <anssi.hannula@gmail.com>\n"); return 0; } diff --git a/drivers/hid/hid-lg3ff.c b/drivers/hid/hid-lg3ff.c index f98644c26c1..8c2da183d3b 100644 --- a/drivers/hid/hid-lg3ff.c +++ b/drivers/hid/hid-lg3ff.c @@ -22,10 +22,8 @@ #include <linux/input.h> -#include <linux/usb.h> #include <linux/hid.h> -#include "usbhid/usbhid.h" #include "hid-lg.h" /* @@ -68,10 +66,11 @@ static int hid_lg3ff_play(struct input_dev *dev, void *data, int x, y; /* - * Maxusage should always be 63 (maximum fields) - * likely a better way to ensure this data is clean + * Available values in the field should always be 63, but we only use up to + * 35. Instead, clear the entire area, however big it is. */ - memset(report->field[0]->value, 0, sizeof(__s32)*report->field[0]->maxusage); + memset(report->field[0]->value, 0, + sizeof(__s32) * report->field[0]->report_count); switch (effect->type) { case FF_CONSTANT: @@ -92,7 +91,7 @@ static int hid_lg3ff_play(struct input_dev *dev, void *data, report->field[0]->value[1] = (unsigned char)(-x); report->field[0]->value[31] = (unsigned char)(-y); - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); break; } return 0; @@ -118,7 +117,7 @@ static void hid_lg3ff_set_autocenter(struct input_dev *dev, u16 magnitude) report->field[0]->value[33] = 0x7F; report->field[0]->value[34] = 0x7F; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } @@ -131,32 +130,14 @@ static const signed short ff3_joystick_ac[] = { int lg3ff_init(struct hid_device *hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; - struct hid_report *report; - struct hid_field *field; const signed short *ff_bits = ff3_joystick_ac; int error; int i; - /* Find the report to use */ - if (list_empty(report_list)) { - hid_err(hid, "No output report found\n"); - return -1; - } - /* Check that the report looks ok */ - report = list_entry(report_list->next, struct hid_report, list); - if (!report) { - hid_err(hid, "NULL output report\n"); - return -1; - } - - field = report->field[0]; - if (!field) { - hid_err(hid, "NULL field\n"); - return -1; - } + if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35)) + return -ENODEV; /* Assume single fixed device G940 */ for (i = 0; ff_bits[i] >= 0; i++) diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index d7947c701f3..cc2bd202219 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -34,6 +34,7 @@ #define DFGT_REV_MAJ 0x13 #define DFGT_REV_MIN 0x22 +#define DFGT2_REV_MIN 0x26 #define DFP_REV_MAJ 0x11 #define DFP_REV_MIN 0x06 #define FFEX_REV_MAJ 0x21 @@ -42,11 +43,7 @@ #define G25_REV_MIN 0x22 #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 G27_2_REV_MIN 0x39 #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) @@ -55,7 +52,7 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range); static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); -static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store); +static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IROTH, lg4ff_range_show, lg4ff_range_store); struct lg4ff_device_entry { __u32 product_id; @@ -130,9 +127,11 @@ static const struct lg4ff_native_cmd native_g27 = { static const struct lg4ff_usb_revision lg4ff_revs[] = { {DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt}, /* Driving Force GT */ + {DFGT_REV_MAJ, DFGT2_REV_MIN, &native_dfgt}, /* Driving Force GT v2 */ {DFP_REV_MAJ, DFP_REV_MIN, &native_dfp}, /* Driving Force Pro */ {G25_REV_MAJ, G25_REV_MIN, &native_g25}, /* G25 */ {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */ + {G27_REV_MAJ, G27_2_REV_MIN, &native_g27}, /* G27 v2 */ }; /* Recalculates X axis value accordingly to currently selected range */ @@ -199,6 +198,21 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e case FF_CONSTANT: x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */ CLAMP(x); + + if (x == 0x80) { + /* De-activate force in slot-1*/ + value[0] = 0x13; + value[1] = 0x00; + value[2] = 0x00; + value[3] = 0x00; + value[4] = 0x00; + value[5] = 0x00; + value[6] = 0x00; + + hid_hw_request(hid, report, HID_REQ_SET_REPORT); + return 0; + } + value[0] = 0x11; /* Slot 1 */ value[1] = 0x08; value[2] = x; @@ -207,7 +221,7 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); break; } return 0; @@ -221,16 +235,74 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud struct list_head *report_list = &hid->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; + __u32 expand_a, expand_b; + struct lg4ff_device_entry *entry; + struct lg_drv_data *drv_data; + + drv_data = hid_get_drvdata(hid); + if (!drv_data) { + hid_err(hid, "Private driver data not found!\n"); + return; + } + + entry = drv_data->device_props; + if (!entry) { + hid_err(hid, "Device properties not found!\n"); + return; + } + + /* De-activate Auto-Center */ + if (magnitude == 0) { + value[0] = 0xf5; + value[1] = 0x00; + value[2] = 0x00; + value[3] = 0x00; + value[4] = 0x00; + value[5] = 0x00; + value[6] = 0x00; + + hid_hw_request(hid, report, HID_REQ_SET_REPORT); + return; + } + + if (magnitude <= 0xaaaa) { + expand_a = 0x0c * magnitude; + expand_b = 0x80 * magnitude; + } else { + expand_a = (0x0c * 0xaaaa) + 0x06 * (magnitude - 0xaaaa); + expand_b = (0x80 * 0xaaaa) + 0xff * (magnitude - 0xaaaa); + } + + /* Adjust for non-MOMO wheels */ + switch (entry->product_id) { + case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: + case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2: + break; + default: + expand_a = expand_a >> 1; + break; + } value[0] = 0xfe; value[1] = 0x0d; - value[2] = magnitude >> 13; - value[3] = magnitude >> 13; - value[4] = magnitude >> 8; + value[2] = expand_a / 0xaaaa; + value[3] = expand_a / 0xaaaa; + value[4] = expand_b / 0xaaaa; + value[5] = 0x00; + value[6] = 0x00; + + hid_hw_request(hid, report, HID_REQ_SET_REPORT); + + /* Activate Auto-Center */ + value[0] = 0x14; + value[1] = 0x00; + value[2] = 0x00; + value[3] = 0x00; + value[4] = 0x00; value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } /* Sends autocentering command compatible with Formula Force EX */ @@ -250,7 +322,7 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude) value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } /* Sends command to set range compatible with G25/G27/Driving Force GT */ @@ -270,7 +342,7 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range) value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } /* Sends commands to set range compatible with Driving Force Pro wheel */ @@ -299,7 +371,7 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) report->field[0]->value[1] = 0x02; full_range = 200; } - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); /* Prepare "fine" limit command */ value[0] = 0x81; @@ -311,7 +383,7 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) value[6] = 0x00; if (range == 200 || range == 900) { /* Do not apply any fine limit */ - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); return; } @@ -325,7 +397,7 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) value[5] = (start_right & 0xe) << 4 | (start_left & 0xe); value[6] = 0xff; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd) @@ -339,7 +411,7 @@ static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_n for (i = 0; i < 7; i++) report->field[0]->value[i] = cmd->cmd[j++]; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } } @@ -415,7 +487,7 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds) value[4] = 0x00; value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } static void lg4ff_led_set_brightness(struct led_classdev *led_cdev, @@ -487,34 +559,16 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde int lg4ff_init(struct hid_device *hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; - struct hid_report *report; - struct hid_field *field; struct lg4ff_device_entry *entry; struct lg_drv_data *drv_data; struct usb_device_descriptor *udesc; int error, i, j; __u16 bcdDevice, rev_maj, rev_min; - /* Find the report to use */ - if (list_empty(report_list)) { - hid_err(hid, "No output report found\n"); - return -1; - } - /* Check that the report looks ok */ - report = list_entry(report_list->next, struct hid_report, list); - if (!report) { - hid_err(hid, "NULL output report\n"); - return -1; - } - - field = report->field[0]; - if (!field) { - hid_err(hid, "NULL field\n"); + if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7)) return -1; - } /* Check what wheel has been connected */ for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) { @@ -561,17 +615,6 @@ int lg4ff_init(struct hid_device *hid) if (error) return error; - /* Check if autocentering is available and - * set the centering force to zero by default */ - if (test_bit(FF_AUTOCENTER, dev->ffbit)) { - if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */ - dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; - else - dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; - - dev->ff->set_autocenter(dev, 0); - } - /* Get private driver data */ drv_data = hid_get_drvdata(hid); if (!drv_data) { @@ -592,24 +635,23 @@ int lg4ff_init(struct hid_device *hid) entry->max_range = lg4ff_devices[i].max_range; entry->set_range = lg4ff_devices[i].set_range; + /* Check if autocentering is available and + * set the centering force to zero by default */ + if (test_bit(FF_AUTOCENTER, dev->ffbit)) { + if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */ + dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; + else + dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; + + dev->ff->set_autocenter(dev, 0); + } + /* Create sysfs interface */ error = device_create_file(&hid->dev, &dev_attr_range); if (error) 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-lgff.c b/drivers/hid/hid-lgff.c index 27bc54f92f4..e1394af0ae7 100644 --- a/drivers/hid/hid-lgff.c +++ b/drivers/hid/hid-lgff.c @@ -30,10 +30,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/input.h> -#include <linux/usb.h> #include <linux/hid.h> -#include "usbhid/usbhid.h" #include "hid-lg.h" struct dev_type { @@ -89,7 +87,7 @@ static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *ef report->field[0]->value[2] = x; report->field[0]->value[3] = y; dbg_hid("(x, y)=(%04x, %04x)\n", x, y); - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); break; case FF_RUMBLE: @@ -104,7 +102,7 @@ static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *ef report->field[0]->value[2] = left; report->field[0]->value[3] = right; dbg_hid("(left, right)=(%04x, %04x)\n", left, right); - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); break; } return 0; @@ -124,33 +122,20 @@ static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude) *value++ = 0x80; *value++ = 0x00; *value = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } int lgff_init(struct hid_device* hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; - struct hid_report *report; - struct hid_field *field; const signed short *ff_bits = ff_joystick; int error; int i; - /* Find the report to use */ - if (list_empty(report_list)) { - hid_err(hid, "No output report found\n"); - return -1; - } - /* Check that the report looks ok */ - report = list_entry(report_list->next, struct hid_report, list); - field = report->field[0]; - if (!field) { - hid_err(hid, "NULL field\n"); - return -1; - } + if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7)) + return -ENODEV; for (i = 0; i < ARRAY_SIZE(devices); i++) { if (dev->id.vendor == devices[i].idVendor && diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 9500f2f3f8f..486dbde2ba2 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -27,7 +27,6 @@ #include <linux/module.h> #include <linux/usb.h> #include <asm/unaligned.h> -#include "usbhid/usbhid.h" #include "hid-ids.h" #include "hid-logitech-dj.h" @@ -45,14 +44,6 @@ static const char kbd_descriptor[] = { 0x19, 0xE0, /* USAGE_MINIMUM (Left Control) */ 0x29, 0xE7, /* USAGE_MAXIMUM (Right GUI) */ 0x81, 0x02, /* INPUT (Data,Var,Abs) */ - 0x95, 0x05, /* REPORT COUNT (5) */ - 0x05, 0x08, /* USAGE PAGE (LED page) */ - 0x19, 0x01, /* USAGE MINIMUM (1) */ - 0x29, 0x05, /* USAGE MAXIMUM (5) */ - 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */ - 0x95, 0x01, /* REPORT COUNT (1) */ - 0x75, 0x03, /* REPORT SIZE (3) */ - 0x91, 0x01, /* OUTPUT (Constant) */ 0x95, 0x06, /* REPORT_COUNT (6) */ 0x75, 0x08, /* REPORT_SIZE (8) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ @@ -61,6 +52,18 @@ static const char kbd_descriptor[] = { 0x19, 0x00, /* USAGE_MINIMUM (no event) */ 0x2A, 0xFF, 0x00, /* USAGE_MAXIMUM (reserved) */ 0x81, 0x00, /* INPUT (Data,Ary,Abs) */ + 0x85, 0x0e, /* REPORT_ID (14) */ + 0x05, 0x08, /* USAGE PAGE (LED page) */ + 0x95, 0x05, /* REPORT COUNT (5) */ + 0x75, 0x01, /* REPORT SIZE (1) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x19, 0x01, /* USAGE MINIMUM (1) */ + 0x29, 0x05, /* USAGE MAXIMUM (5) */ + 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */ + 0x95, 0x01, /* REPORT COUNT (1) */ + 0x75, 0x03, /* REPORT SIZE (3) */ + 0x91, 0x01, /* OUTPUT (Constant) */ 0xC0 }; @@ -190,9 +193,6 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = { static struct hid_ll_driver logi_dj_ll_driver; -static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, - size_t count, - unsigned char report_type); static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev); static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev, @@ -259,7 +259,6 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, } dj_hiddev->ll_driver = &logi_dj_ll_driver; - dj_hiddev->hid_output_raw_report = logi_dj_output_hidraw_report; dj_hiddev->dev.parent = &djrcv_hdev->dev; dj_hiddev->bus = BUS_USB; @@ -459,19 +458,25 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev, struct dj_report *dj_report) { struct hid_device *hdev = djrcv_dev->hdev; - int sent_bytes; + struct hid_report *report; + struct hid_report_enum *output_report_enum; + u8 *data = (u8 *)(&dj_report->device_index); + unsigned int i; + + output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; + report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; - if (!hdev->hid_output_raw_report) { - dev_err(&hdev->dev, "%s:" - "hid_output_raw_report is null\n", __func__); + if (!report) { + dev_err(&hdev->dev, "%s: unable to find dj report\n", __func__); return -ENODEV; } - sent_bytes = hdev->hid_output_raw_report(hdev, (u8 *) dj_report, - sizeof(struct dj_report), - HID_OUTPUT_REPORT); + for (i = 0; i < DJREPORT_SHORT_LENGTH - 1; i++) + report->field[0]->value[i] = data[i]; + + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); - return (sent_bytes < 0) ? sent_bytes : 0; + return 0; } static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev) @@ -511,6 +516,14 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout; retval = logi_dj_recv_send_report(djrcv_dev, dj_report); kfree(dj_report); + + /* + * Ugly sleep to work around a USB 3.0 bug when the receiver is still + * processing the "switch-to-dj" command while we send an other command. + * 50 msec should gives enough time to the receiver to be ready. + */ + msleep(50); + return retval; } @@ -527,19 +540,40 @@ static void logi_dj_ll_close(struct hid_device *hid) dbg_hid("%s:%s\n", __func__, hid->phys); } -static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, - size_t count, - unsigned char report_type) +static int logi_dj_ll_raw_request(struct hid_device *hid, + unsigned char reportnum, __u8 *buf, + size_t count, unsigned char report_type, + int reqtype) { - /* Called by hid raw to send data */ - dbg_hid("%s\n", __func__); + struct dj_device *djdev = hid->driver_data; + struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev; + u8 *out_buf; + int ret; - return 0; + if (buf[0] != REPORT_TYPE_LEDS) + return -EINVAL; + + out_buf = kzalloc(DJREPORT_SHORT_LENGTH, GFP_ATOMIC); + if (!out_buf) + return -ENOMEM; + + if (count < DJREPORT_SHORT_LENGTH - 2) + count = DJREPORT_SHORT_LENGTH - 2; + + out_buf[0] = REPORT_ID_DJ_SHORT; + out_buf[1] = djdev->device_index; + memcpy(out_buf + 2, buf, count); + + ret = hid_hw_raw_request(djrcv_dev->hdev, out_buf[0], out_buf, + DJREPORT_SHORT_LENGTH, report_type, reqtype); + + kfree(out_buf); + return ret; } -static void rdcat(char **rdesc, unsigned int *rsize, const char *data, unsigned int size) +static void rdcat(char *rdesc, unsigned int *rsize, const char *data, unsigned int size) { - memcpy(*rdesc + *rsize, data, size); + memcpy(rdesc + *rsize, data, size); *rsize += size; } @@ -562,31 +596,31 @@ static int logi_dj_ll_parse(struct hid_device *hid) if (djdev->reports_supported & STD_KEYBOARD) { dbg_hid("%s: sending a kbd descriptor, reports_supported: %x\n", __func__, djdev->reports_supported); - rdcat(&rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor)); + rdcat(rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor)); } if (djdev->reports_supported & STD_MOUSE) { dbg_hid("%s: sending a mouse descriptor, reports_supported: " "%x\n", __func__, djdev->reports_supported); - rdcat(&rdesc, &rsize, mse_descriptor, sizeof(mse_descriptor)); + rdcat(rdesc, &rsize, mse_descriptor, sizeof(mse_descriptor)); } if (djdev->reports_supported & MULTIMEDIA) { dbg_hid("%s: sending a multimedia report descriptor: %x\n", __func__, djdev->reports_supported); - rdcat(&rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor)); + rdcat(rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor)); } if (djdev->reports_supported & POWER_KEYS) { dbg_hid("%s: sending a power keys report descriptor: %x\n", __func__, djdev->reports_supported); - rdcat(&rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor)); + rdcat(rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor)); } if (djdev->reports_supported & MEDIA_CENTER) { dbg_hid("%s: sending a media center report descriptor: %x\n", __func__, djdev->reports_supported); - rdcat(&rdesc, &rsize, media_descriptor, sizeof(media_descriptor)); + rdcat(rdesc, &rsize, media_descriptor, sizeof(media_descriptor)); } if (djdev->reports_supported & KBD_LEDS) { @@ -600,50 +634,6 @@ static int logi_dj_ll_parse(struct hid_device *hid) return retval; } -static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, - unsigned int code, int value) -{ - /* Sent by the input layer to handle leds and Force Feedback */ - struct hid_device *dj_hiddev = input_get_drvdata(dev); - struct dj_device *dj_dev = dj_hiddev->driver_data; - - struct dj_receiver_dev *djrcv_dev = - dev_get_drvdata(dj_hiddev->dev.parent); - struct hid_device *dj_rcv_hiddev = djrcv_dev->hdev; - struct hid_report_enum *output_report_enum; - - struct hid_field *field; - struct hid_report *report; - unsigned char data[8]; - int offset; - - dbg_hid("%s: %s, type:%d | code:%d | value:%d\n", - __func__, dev->phys, type, code, value); - - if (type != EV_LED) - return -1; - - offset = hidinput_find_field(dj_hiddev, type, code, &field); - - if (offset == -1) { - dev_warn(&dev->dev, "event field not found\n"); - return -1; - } - hid_set_field(field, offset, value); - hid_output_report(field->report, &data[0]); - - output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT]; - report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; - hid_set_field(report->field[0], 0, dj_dev->device_index); - hid_set_field(report->field[0], 1, REPORT_TYPE_LEDS); - hid_set_field(report->field[0], 2, data[1]); - - usbhid_submit_report(dj_rcv_hiddev, report, USB_DIR_OUT); - - return 0; - -} - static int logi_dj_ll_start(struct hid_device *hid) { dbg_hid("%s\n", __func__); @@ -662,7 +652,7 @@ static struct hid_ll_driver logi_dj_ll_driver = { .stop = logi_dj_ll_stop, .open = logi_dj_ll_open, .close = logi_dj_ll_close, - .hidinput_input_event = logi_dj_ll_input_event, + .raw_request = logi_dj_ll_raw_request, }; @@ -778,6 +768,12 @@ static int logi_dj_probe(struct hid_device *hdev, goto hid_parse_fail; } + if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, REPORT_ID_DJ_SHORT, + 0, DJREPORT_SHORT_LENGTH - 1)) { + retval = -ENODEV; + goto hid_parse_fail; + } + /* Starts the usb device and connects to upper interfaces hiddev and * hidraw */ retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); @@ -796,13 +792,16 @@ static int logi_dj_probe(struct hid_device *hdev, } /* This is enabling the polling urb on the IN endpoint */ - retval = hdev->ll_driver->open(hdev); + retval = hid_hw_open(hdev); if (retval < 0) { - dev_err(&hdev->dev, "%s:hdev->ll_driver->open returned " - "error:%d\n", __func__, retval); + dev_err(&hdev->dev, "%s:hid_hw_open returned error:%d\n", + __func__, retval); goto llopen_failed; } + /* Allow incoming packets to arrive: */ + hid_device_io_start(hdev); + retval = logi_dj_recv_query_paired_devices(djrcv_dev); if (retval < 0) { dev_err(&hdev->dev, "%s:logi_dj_recv_query_paired_devices " @@ -813,7 +812,7 @@ static int logi_dj_probe(struct hid_device *hdev, return retval; logi_dj_recv_query_paired_devices_failed: - hdev->ll_driver->close(hdev); + hid_hw_close(hdev); llopen_failed: switch_to_dj_mode_fail: @@ -855,7 +854,7 @@ static void logi_dj_remove(struct hid_device *hdev) cancel_work_sync(&djrcv_dev->work); - hdev->ll_driver->close(hdev); + hid_hw_close(hdev); hid_hw_stop(hdev); /* I suppose that at this point the only context that can access diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 25ddf3e3aec..ecc2cbf300c 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -19,7 +19,6 @@ #include <linux/input/mt.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/usb.h> #include "hid-ids.h" @@ -37,7 +36,7 @@ MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel"); static unsigned int scroll_speed = 32; static int param_set_scroll_speed(const char *val, struct kernel_param *kp) { unsigned long speed; - if (!val || strict_strtoul(val, 0, &speed) || speed > 63) + if (!val || kstrtoul(val, 0, &speed) || speed > 63) return -EINVAL; scroll_speed = speed; return 0; @@ -462,6 +461,21 @@ static int magicmouse_input_mapping(struct hid_device *hdev, return 0; } +static void magicmouse_input_configured(struct hid_device *hdev, + struct hid_input *hi) + +{ + struct magicmouse_sc *msc = hid_get_drvdata(hdev); + + int ret = magicmouse_setup_input(msc->input, hdev); + if (ret) { + hid_err(hdev, "magicmouse setup input failed (%d)\n", ret); + /* clean msc->input to notify probe() of the failure */ + msc->input = NULL; + } +} + + static int magicmouse_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -470,7 +484,7 @@ static int magicmouse_probe(struct hid_device *hdev, struct hid_report *report; int ret; - msc = kzalloc(sizeof(*msc), GFP_KERNEL); + msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL); if (msc == NULL) { hid_err(hdev, "can't alloc magicmouse descriptor\n"); return -ENOMEM; @@ -484,24 +498,19 @@ static int magicmouse_probe(struct hid_device *hdev, ret = hid_parse(hdev); if (ret) { hid_err(hdev, "magicmouse hid parse failed\n"); - goto err_free; + return ret; } ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "magicmouse hw start failed\n"); - goto err_free; + return ret; } - /* We do this after hid-input is done parsing reports so that - * hid-input uses the most natural button and axis IDs. - */ - if (msc->input) { - ret = magicmouse_setup_input(msc->input, hdev); - if (ret) { - hid_err(hdev, "magicmouse setup input failed (%d)\n", ret); - goto err_stop_hw; - } + if (!msc->input) { + hid_err(hdev, "magicmouse input not registered\n"); + ret = -ENOMEM; + goto err_stop_hw; } if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE) @@ -529,8 +538,8 @@ static int magicmouse_probe(struct hid_device *hdev, * but there seems to be no other way of switching the mode. * Thus the super-ugly hacky success check below. */ - ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature), - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, feature[0], feature, sizeof(feature), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); if (ret != -EIO && ret != sizeof(feature)) { hid_err(hdev, "unable to request touch data (%d)\n", ret); goto err_stop_hw; @@ -539,19 +548,9 @@ static int magicmouse_probe(struct hid_device *hdev, return 0; err_stop_hw: hid_hw_stop(hdev); -err_free: - kfree(msc); return ret; } -static void magicmouse_remove(struct hid_device *hdev) -{ - struct magicmouse_sc *msc = hid_get_drvdata(hdev); - - hid_hw_stop(hdev); - kfree(msc); -} - static const struct hid_device_id magic_mice[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 }, @@ -565,27 +564,10 @@ static struct hid_driver magicmouse_driver = { .name = "magicmouse", .id_table = magic_mice, .probe = magicmouse_probe, - .remove = magicmouse_remove, .raw_event = magicmouse_raw_event, .input_mapping = magicmouse_input_mapping, + .input_configured = magicmouse_input_configured, }; +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 6fcd466d082..8ba17a946f2 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -62,9 +62,48 @@ static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage, { struct input_dev *input = hi->input; + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) { + switch (usage->hid & HID_USAGE) { + /* + * Microsoft uses these 2 reserved usage ids for 2 keys on + * the MS office kb labelled "Office Home" and "Task Pane". + */ + case 0x29d: + ms_map_key_clear(KEY_PROG1); + return 1; + case 0x29e: + ms_map_key_clear(KEY_PROG2); + return 1; + } + return 0; + } + + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) + return 0; + switch (usage->hid & HID_USAGE) { case 0xfd06: ms_map_key_clear(KEY_CHAT); break; case 0xfd07: ms_map_key_clear(KEY_PHONE); break; + case 0xff00: + /* Special keypad keys */ + ms_map_key_clear(KEY_KPEQUAL); + set_bit(KEY_KPLEFTPAREN, input->keybit); + set_bit(KEY_KPRIGHTPAREN, input->keybit); + break; + case 0xff01: + /* Scroll wheel */ + hid_map_usage_clear(hi, usage, bit, max, EV_REL, REL_WHEEL); + break; + case 0xff02: + /* + * This byte contains a copy of the modifier keys byte of a + * standard hid keyboard report, as send by interface 0 + * (this usage is found on interface 1). + * + * This byte only gets send when another key in the same report + * changes state, and as such is useless, ignore it. + */ + return -1; case 0xff05: set_bit(EV_REP, input->evbit); ms_map_key_clear(KEY_F13); @@ -73,6 +112,7 @@ static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage, set_bit(KEY_F16, input->keybit); set_bit(KEY_F17, input->keybit); set_bit(KEY_F18, input->keybit); + break; default: return 0; } @@ -82,6 +122,9 @@ static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage, static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage, unsigned long **bit, int *max) { + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) + return 0; + set_bit(EV_REP, hi->input->evbit); switch (usage->hid & HID_USAGE) { case 0xfd08: ms_map_key_clear(KEY_FORWARD); break; @@ -101,9 +144,6 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi, { unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) - return 0; - if (quirks & MS_ERGONOMY) { int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max); if (ret) @@ -133,14 +173,39 @@ static int ms_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + struct input_dev *input; if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || !usage->type) return 0; + input = field->hidinput->input; + /* Handling MS keyboards special buttons */ + if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff00)) { + /* Special keypad keys */ + input_report_key(input, KEY_KPEQUAL, value & 0x01); + input_report_key(input, KEY_KPLEFTPAREN, value & 0x02); + input_report_key(input, KEY_KPRIGHTPAREN, value & 0x04); + return 1; + } + + if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff01)) { + /* Scroll wheel */ + int step = ((value & 0x60) >> 5) + 1; + + switch (value & 0x1f) { + case 0x01: + input_report_rel(input, REL_WHEEL, step); + break; + case 0x1f: + input_report_rel(input, REL_WHEEL, -step); + break; + } + return 1; + } + if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) { - struct input_dev *input = field->hidinput->input; static unsigned int last_key = 0; unsigned int key = 0; switch (value) { @@ -193,8 +258,12 @@ err_free: static const struct hid_device_id ms_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV), .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB), + .driver_data = MS_ERGONOMY }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K), .driver_data = MS_ERGONOMY }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP), + .driver_data = MS_ERGONOMY }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K), .driver_data = MS_ERGONOMY | MS_RDESC }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), @@ -221,17 +290,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 cd3643e06fa..9e14c00eb1b 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 61543c02ea0..51e25b9407f 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -2,8 +2,9 @@ * HID driver for multitouch panels * * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr> - * Copyright (c) 2010-2012 Benjamin Tissoires <benjamin.tissoires@gmail.com> + * Copyright (c) 2010-2013 Benjamin Tissoires <benjamin.tissoires@gmail.com> * Copyright (c) 2010-2012 Ecole Nationale de l'Aviation Civile, France + * Copyright (c) 2012-2013 Red Hat, Inc * * This code is partly based on hid-egalax.c: * @@ -26,13 +27,24 @@ * any later version. */ +/* + * This driver is regularly tested thanks to the tool hid-test[1]. + * This tool relies on hid-replay[2] and a database of hid devices[3]. + * Please run these regression tests before patching this module so that + * your patch won't break existing known devices. + * + * [1] https://github.com/bentiss/hid-test + * [2] https://github.com/bentiss/hid-replay + * [3] https://github.com/bentiss/hid-devices + */ + #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/usb.h> #include <linux/input/mt.h> -#include "usbhid/usbhid.h" +#include <linux/string.h> MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>"); @@ -54,6 +66,10 @@ 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) + +#define MT_INPUTMODE_TOUCHSCREEN 0x02 +#define MT_INPUTMODE_TOUCHPAD 0x03 struct mt_slot { __s32 x, y, cx, cy, p, w, h; @@ -71,6 +87,7 @@ struct mt_class { __s32 sn_pressure; /* Signal/noise ratio for pressure events */ __u8 maxcontacts; bool is_indirect; /* true for touchpads */ + bool export_all_inputs; /* do not ignore mouse, keyboards, etc... */ }; struct mt_fields { @@ -83,12 +100,15 @@ struct mt_device { struct mt_class mtclass; /* our mt device class */ struct mt_fields *fields; /* temporary placeholder for storing the multitouch fields */ - unsigned last_field_index; /* last field index of the report */ + int cc_index; /* contact count field index in the report */ + int cc_value_index; /* contact count value index in the field */ unsigned last_slot_field; /* the last field of a slot */ - __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, + unsigned mt_report_id; /* the report ID of the multitouch device */ + __s16 inputmode; /* InputMode HID feature, -1 if non-existent */ + __s16 inputmode_index; /* InputMode HID feature index in the report */ + __s16 maxcontact_report_id; /* Maximum Contact Number HID feature, -1 if non-existent */ + __u8 inputmode_value; /* InputMode HID feature value */ __u8 num_received; /* how many contacts we received */ __u8 num_expected; /* expected last contact index */ __u8 maxcontacts; @@ -100,6 +120,9 @@ struct mt_device { unsigned mt_flags; /* flags to pass to input-mt */ }; +static void mt_post_parse_default_settings(struct mt_device *td); +static void mt_post_parse(struct mt_device *td); + /* classes of device behavior */ #define MT_CLS_DEFAULT 0x0001 @@ -109,12 +132,17 @@ struct mt_device { #define MT_CLS_CONFIDENCE_MINUS_ONE 0x0005 #define MT_CLS_DUAL_INRANGE_CONTACTID 0x0006 #define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0007 -#define MT_CLS_DUAL_NSMU_CONTACTID 0x0008 +/* reserved 0x0008 */ #define MT_CLS_INRANGE_CONTACTNUMBER 0x0009 +#define MT_CLS_NSMU 0x000a +/* reserved 0x0010 */ +/* reserved 0x0011 */ +#define MT_CLS_WIN_8 0x0012 +#define MT_CLS_EXPORT_ALL_INPUTS 0x0013 /* vendor specific classes */ #define MT_CLS_3M 0x0101 -#define MT_CLS_CYPRESS 0x0102 +/* reserved 0x0102 */ #define MT_CLS_EGALAX 0x0103 #define MT_CLS_EGALAX_SERIAL 0x0104 #define MT_CLS_TOPSEED 0x0105 @@ -144,6 +172,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}, @@ -163,13 +194,18 @@ static struct mt_class mt_classes[] = { .quirks = MT_QUIRK_VALID_IS_INRANGE | MT_QUIRK_SLOT_IS_CONTACTNUMBER, .maxcontacts = 2 }, - { .name = MT_CLS_DUAL_NSMU_CONTACTID, - .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | - MT_QUIRK_SLOT_IS_CONTACTID, - .maxcontacts = 2 }, { .name = MT_CLS_INRANGE_CONTACTNUMBER, .quirks = MT_QUIRK_VALID_IS_INRANGE | MT_QUIRK_SLOT_IS_CONTACTNUMBER }, + { .name = MT_CLS_WIN_8, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_IGNORE_DUPLICATES | + MT_QUIRK_HOVERING | + MT_QUIRK_CONTACT_CNT_ACCURATE }, + { .name = MT_CLS_EXPORT_ALL_INPUTS, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE, + .export_all_inputs = true }, /* * vendor specific classes @@ -182,10 +218,6 @@ static struct mt_class mt_classes[] = { .sn_height = 128, .maxcontacts = 60, }, - { .name = MT_CLS_CYPRESS, - .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | - MT_QUIRK_CYPRESS, - .maxcontacts = 10 }, { .name = MT_CLS_EGALAX, .quirks = MT_QUIRK_SLOT_IS_CONTACTID | MT_QUIRK_VALID_IS_INRANGE, @@ -209,12 +241,12 @@ static struct mt_class mt_classes[] = { { .name = MT_CLS_GENERALTOUCH_TWOFINGERS, .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | MT_QUIRK_VALID_IS_INRANGE | - MT_QUIRK_SLOT_IS_CONTACTNUMBER, + MT_QUIRK_SLOT_IS_CONTACTID, .maxcontacts = 2 }, { .name = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | - MT_QUIRK_SLOT_IS_CONTACTNUMBER + MT_QUIRK_SLOT_IS_CONTACTID }, { .name = MT_CLS_FLATFROG, @@ -250,6 +282,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; } @@ -268,20 +303,18 @@ static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { struct mt_device *td = hid_get_drvdata(hdev); - int i; switch (usage->hid) { case HID_DG_INPUTMODE: - td->inputmode = field->report->id; - td->inputmode_index = 0; /* has to be updated below */ - - for (i=0; i < field->maxusage; i++) { - if (field->usage[i].hid == usage->hid) { - td->inputmode_index = i; - break; - } + /* Ignore if value index is out of bounds. */ + if (usage->usage_index >= field->report_count) { + dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n"); + break; } + td->inputmode = field->report->id; + td->inputmode_index = usage->usage_index; + break; case HID_DG_CONTACTMAX: td->maxcontact_report_id = field->report->id; @@ -294,18 +327,6 @@ static void mt_feature_mapping(struct hid_device *hdev, td->maxcontacts = td->mtclass.maxcontacts; break; - case 0xff0000c5: - if (field->report_count == 256 && field->report_size == 8) { - /* Win 8 devices need special quirks */ - __s32 *quirks = &td->mtclass.quirks; - *quirks |= MT_QUIRK_ALWAYS_VALID; - *quirks |= MT_QUIRK_IGNORE_DUPLICATES; - *quirks |= MT_QUIRK_HOVERING; - *quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; - *quirks &= ~MT_QUIRK_VALID_IS_INRANGE; - *quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; - } - break; } } @@ -330,7 +351,7 @@ static void mt_store_field(struct hid_usage *usage, struct mt_device *td, f->usages[f->length++] = usage->hid; } -static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, +static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { @@ -339,26 +360,17 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, int code; struct hid_usage *prev_usage = NULL; - /* Only map fields from TouchScreen or TouchPad collections. - * We need to ignore fields that belong to other collections - * such as Mouse that might have the same GenericDesktop usages. */ if (field->application == HID_DG_TOUCHSCREEN) td->mt_flags |= INPUT_MT_DIRECT; - else if (field->application != HID_DG_TOUCHPAD) - return 0; /* * Model touchscreens providing buttons as touchpads. */ if (field->application == HID_DG_TOUCHPAD || - (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) + (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) { td->mt_flags |= INPUT_MT_POINTER; - - /* eGalax devices provide a Digitizer.Stylus input which overrides - * the correct Digitizers.Finger X/Y ranges. - * Let's just ignore this input. */ - if (field->physical == HID_DG_STYLUS) - return -1; + td->inputmode_value = MT_INPUTMODE_TOUCHPAD; + } if (usage->usage_index) prev_usage = &field->usage[usage->usage_index - 1]; @@ -381,7 +393,6 @@ 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; return 1; case HID_GD_Y: if (prev_usage && (prev_usage->hid == usage->hid)) { @@ -397,7 +408,6 @@ 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; return 1; } return 0; @@ -412,22 +422,19 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, ABS_MT_DISTANCE, 0, 1, 0, 0); } mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_CONFIDENCE: mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_TIPSWITCH: hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); input_set_capability(hi->input, EV_KEY, BTN_TOUCH); mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_CONTACTID: 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, @@ -436,7 +443,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, cls->sn_width); mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_HEIGHT: hid_map_usage(hi, usage, bit, max, @@ -448,7 +454,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, ABS_MT_ORIENTATION, 0, 1, 0, 0); } mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_TIPPRESSURE: hid_map_usage(hi, usage, bit, max, @@ -456,15 +461,18 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, set_abs(hi->input, ABS_MT_PRESSURE, field, cls->sn_pressure); mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_CONTACTCOUNT: - td->last_field_index = field->index; + /* Ignore if indexes are out of bounds. */ + if (field->index >= field->report->maxfield || + usage->usage_index >= field->report_count) + return 1; + td->cc_index = field->index; + td->cc_value_index = usage->usage_index; return 1; case HID_DG_CONTACTMAX: /* we don't set td->last_slot_field as contactcount and * contact max are global to the report */ - td->last_field_index = field->index; return -1; case HID_DG_TOUCH: /* Legacy devices use TIPSWITCH and not TOUCH. @@ -488,7 +496,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } -static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, +static int mt_touch_input_mapped(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { @@ -523,6 +531,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; @@ -575,11 +587,22 @@ static void mt_sync_frame(struct mt_device *td, struct input_dev *input) td->num_received = 0; } -static int mt_event(struct hid_device *hid, struct hid_field *field, +static int mt_touch_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; + struct input_dev *input = field->hidinput->input; if (hid->claimed & HID_CLAIMED_INPUT) { switch (usage->hid) { @@ -623,39 +646,162 @@ 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; + if (usage->type) + input_event(input, usage->type, usage->code, + value); + return; } if (usage->usage_index + 1 == field->report_count) { /* we only take into account the last report. */ if (usage->hid == td->last_slot_field) mt_complete_slot(td, field->hidinput->input); - - if (field->index == td->last_field_index - && td->num_received >= td->num_expected) - mt_sync_frame(td, field->hidinput->input); } } +} - /* 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_touch_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; + /* + * 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]); + } + + if (td->num_received >= td->num_expected) + mt_sync_frame(td, report->field[0]->hidinput->input); +} + +static void mt_touch_input_configured(struct hid_device *hdev, + struct hid_input *hi) +{ + struct mt_device *td = hid_get_drvdata(hdev); + struct mt_class *cls = &td->mtclass; + struct input_dev *input = hi->input; + + if (!td->maxcontacts) + td->maxcontacts = MT_DEFAULT_MAXCONTACT; + + mt_post_parse(td); + if (td->serial_maybe) + mt_post_parse_default_settings(td); + + if (cls->is_indirect) + td->mt_flags |= INPUT_MT_POINTER; + + if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) + td->mt_flags |= INPUT_MT_DROP_UNUSED; + + input_mt_init_slots(input, td->maxcontacts, td->mt_flags); + + td->mt_flags = 0; +} + +static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct mt_device *td = hid_get_drvdata(hdev); + + /* + * If mtclass.export_all_inputs is not set, only map fields from + * TouchScreen or TouchPad collections. We need to ignore fields + * that belong to other collections such as Mouse that might have + * the same GenericDesktop usages. + */ + if (!td->mtclass.export_all_inputs && + field->application != HID_DG_TOUCHSCREEN && + field->application != HID_DG_PEN && + field->application != HID_DG_TOUCHPAD) + return -1; + + /* + * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" + * for the stylus. + */ + if (field->physical == HID_DG_STYLUS) + return 0; + + if (field->application == HID_DG_TOUCHSCREEN || + field->application == HID_DG_TOUCHPAD) + return mt_touch_input_mapping(hdev, hi, field, usage, bit, max); + + /* let hid-core decide for the others */ + return 0; +} + +static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + /* + * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" + * for the stylus. + */ + if (field->physical == HID_DG_STYLUS) + return 0; + + if (field->application == HID_DG_TOUCHSCREEN || + field->application == HID_DG_TOUCHPAD) + return mt_touch_input_mapped(hdev, hi, field, usage, bit, max); + + /* let hid-core decide for the others */ + return 0; +} + +static int mt_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct mt_device *td = hid_get_drvdata(hid); + + if (field->report->id == td->mt_report_id) + return mt_touch_event(hid, field, usage, value); + + return 0; +} + +static void mt_report(struct hid_device *hid, struct hid_report *report) +{ + struct mt_device *td = hid_get_drvdata(hid); + struct hid_field *field = report->field[0]; + + if (!(hid->claimed & HID_CLAIMED_INPUT)) + return; + + if (report->id == td->mt_report_id) + return mt_touch_report(hid, report); + + if (field && field->hidinput && field->hidinput->input) + input_sync(field->hidinput->input); } static void mt_set_input_mode(struct hid_device *hdev) @@ -670,8 +816,8 @@ static void mt_set_input_mode(struct hid_device *hdev) re = &(hdev->report_enum[HID_FEATURE_REPORT]); r = re->report_id_hash[td->inputmode]; if (r) { - r->field[0]->value[td->inputmode_index] = 0x02; - usbhid_submit_report(hdev, r, USB_DIR_OUT); + r->field[0]->value[td->inputmode_index] = td->inputmode_value; + hid_hw_request(hdev, r, HID_REQ_SET_REPORT); } } @@ -696,7 +842,7 @@ static void mt_set_maxcontacts(struct hid_device *hdev) max = min(fieldmax, max); if (r->field[0]->value[0] != max) { r->field[0]->value[0] = max; - usbhid_submit_report(hdev, r, USB_DIR_OUT); + hid_hw_request(hdev, r, HID_REQ_SET_REPORT); } } } @@ -711,6 +857,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,40 +866,76 @@ 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) - { struct mt_device *td = hid_get_drvdata(hdev); - struct mt_class *cls = &td->mtclass; - struct input_dev *input = hi->input; - - /* Only initialize slots for MT input devices */ - if (!test_bit(ABS_MT_POSITION_X, input->absbit)) - return; - - if (!td->maxcontacts) - td->maxcontacts = MT_DEFAULT_MAXCONTACT; + char *name; + const char *suffix = NULL; + struct hid_field *field = hi->report->field[0]; - mt_post_parse(td); - if (td->serial_maybe) - mt_post_parse_default_settings(td); - - if (cls->is_indirect) - td->mt_flags |= INPUT_MT_POINTER; - - if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) - td->mt_flags |= INPUT_MT_DROP_UNUSED; + if (hi->report->id == td->mt_report_id) + mt_touch_input_configured(hdev, hi); - input_mt_init_slots(input, td->maxcontacts, td->mt_flags); + /* + * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" + * for the stylus. Check this first, and then rely on the application + * field. + */ + if (hi->report->field[0]->physical == HID_DG_STYLUS) { + suffix = "Pen"; + /* force BTN_STYLUS to allow tablet matching in udev */ + __set_bit(BTN_STYLUS, hi->input->keybit); + } else { + switch (field->application) { + case HID_GD_KEYBOARD: + suffix = "Keyboard"; + break; + case HID_GD_KEYPAD: + suffix = "Keypad"; + break; + case HID_GD_MOUSE: + suffix = "Mouse"; + break; + case HID_DG_STYLUS: + suffix = "Pen"; + /* force BTN_STYLUS to allow tablet matching in udev */ + __set_bit(BTN_STYLUS, hi->input->keybit); + break; + case HID_DG_TOUCHSCREEN: + /* we do not set suffix = "Touchscreen" */ + break; + case HID_GD_SYSTEM_CONTROL: + suffix = "System Control"; + break; + case HID_CP_CONSUMER_CONTROL: + suffix = "Consumer Control"; + break; + default: + suffix = "UNKNOWN"; + break; + } + } - td->mt_flags = 0; + if (suffix) { + name = devm_kzalloc(&hi->input->dev, + strlen(hdev->name) + strlen(suffix) + 2, + GFP_KERNEL); + if (name) { + sprintf(name, "%s %s", hdev->name, suffix); + hi->input->name = name; + } + } } static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) @@ -773,7 +956,27 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) */ hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; - td = kzalloc(sizeof(struct mt_device), GFP_KERNEL); + /* + * This allows the driver to handle different input sensors + * that emits events through different reports on the same HID + * device. + */ + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT; + + /* + * Handle special quirks for Windows 8 certified devices. + */ + if (id->group == HID_GROUP_MULTITOUCH_WIN_8) + /* + * Some multitouch screens do not like to be polled for input + * reports. Fortunately, the Win8 spec says that all touches + * should be sent during each report, making the initialization + * of input reports unnecessary. + */ + hdev->quirks |= HID_QUIRK_NO_INIT_INPUT_REPORTS; + + td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL); if (!td) { dev_err(&hdev->dev, "cannot allocate multitouch data\n"); return -ENOMEM; @@ -781,13 +984,16 @@ 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->inputmode_value = MT_INPUTMODE_TOUCHSCREEN; + td->cc_index = -1; + td->mt_report_id = -1; hid_set_drvdata(hdev, td); - td->fields = kzalloc(sizeof(struct mt_fields), GFP_KERNEL); + td->fields = devm_kzalloc(&hdev->dev, sizeof(struct mt_fields), + GFP_KERNEL); if (!td->fields) { dev_err(&hdev->dev, "cannot allocate multitouch fields data\n"); - ret = -ENOMEM; - goto fail; + return -ENOMEM; } if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID) @@ -795,26 +1001,22 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_parse(hdev); if (ret != 0) - goto fail; + return ret; ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) - goto fail; + return ret; ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); mt_set_maxcontacts(hdev); mt_set_input_mode(hdev); - kfree(td->fields); + /* release .fields memory as it is not used anymore */ + devm_kfree(&hdev->dev, td->fields); td->fields = NULL; return 0; - -fail: - kfree(td->fields); - kfree(td); - return ret; } #ifdef CONFIG_PM @@ -827,26 +1029,11 @@ static int mt_reset_resume(struct hid_device *hdev) static int mt_resume(struct hid_device *hdev) { - struct usb_interface *intf; - struct usb_host_interface *interface; - struct usb_device *dev; - - if (hdev->bus != BUS_USB) - return 0; - - intf = to_usb_interface(hdev->dev.parent); - interface = intf->cur_altsetting; - dev = hid_to_usb_dev(hdev); - /* Some Elan legacy devices require SET_IDLE to be set on resume. * It should be safe to send it to other devices too. * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */ - usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - HID_REQ_SET_IDLE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, interface->desc.bInterfaceNumber, - NULL, 0, USB_CTRL_SET_TIMEOUT); + hid_hw_idle(hdev, 0, 0, HID_REQ_SET_IDLE); return 0; } @@ -854,13 +1041,16 @@ static int mt_resume(struct hid_device *hdev) static void mt_remove(struct hid_device *hdev) { - struct mt_device *td = hid_get_drvdata(hdev); sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); hid_hw_stop(hdev); - kfree(td); - hid_set_drvdata(hdev, NULL); } +/* + * This list contains only: + * - VID/PID of products not working with the default multitouch handling + * - 2 generic rules. + * So there is no point in adding here any device with MT_CLS_DEFAULT. + */ static const struct hid_device_id mt_devices[] = { /* 3M panels */ @@ -874,52 +1064,39 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M3266) }, - /* ActionStar panels */ - { .driver_data = MT_CLS_DEFAULT, - MT_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR, - USB_DEVICE_ID_ACTIONSTAR_1011) }, + /* Anton devices */ + { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, + MT_USB_DEVICE(USB_VENDOR_ID_ANTON, + USB_DEVICE_ID_ANTON_TOUCH_PAD) }, /* Atmel panels */ { .driver_data = MT_CLS_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_ATMEL, - USB_DEVICE_ID_ATMEL_MULTITOUCH) }, - { .driver_data = MT_CLS_SERIAL, - MT_USB_DEVICE(USB_VENDOR_ID_ATMEL, 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, MT_USB_DEVICE(USB_VENDOR_ID_CANDO, - USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) }, - { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, - MT_USB_DEVICE(USB_VENDOR_ID_CANDO, - USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) }, - { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, - MT_USB_DEVICE(USB_VENDOR_ID_CANDO, 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) }, - /* Cypress panel */ - { .driver_data = MT_CLS_CYPRESS, - HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, - USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, - /* eGalax devices (resistive) */ { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, @@ -929,34 +1106,40 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, /* eGalax devices (capacitive) */ - { .driver_data = MT_CLS_EGALAX, - MT_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207) }, - { .driver_data = MT_CLS_EGALAX_SERIAL, + { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A) }, - { .driver_data = MT_CLS_EGALAX, + { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262) }, { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + { .driver_data = MT_CLS_EGALAX, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA) }, { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) }, + { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA) }, { .driver_data = MT_CLS_EGALAX, @@ -971,26 +1154,22 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, - { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, - { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) }, - { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) }, - /* Elo TouchSystems IntelliTouch Plus panel */ - { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID, - MT_USB_DEVICE(USB_VENDOR_ID_ELO, - USB_DEVICE_ID_ELO_TS2515) }, + /* Elitegroup panel */ + { .driver_data = MT_CLS_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_ELITEGROUP, + USB_DEVICE_ID_ELITEGROUP_05D8) }, /* Flatfrog Panels */ { .driver_data = MT_CLS_FLATFROG, MT_USB_DEVICE(USB_VENDOR_ID_FLATFROG, USB_DEVICE_ID_MULTITOUCH_3200) }, + /* FocalTech Panels */ + { .driver_data = MT_CLS_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_CYGNAL, + USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH) }, + /* GeneralTouch panel */ { .driver_data = MT_CLS_GENERALTOUCH_TWOFINGERS, MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, @@ -998,14 +1177,29 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS) }, + { .driver_data = MT_CLS_GENERALTOUCH_TWOFINGERS, + MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, + USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0101) }, + { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, + MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, + USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0102) }, + { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, + MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, + USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0106) }, + { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, + MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, + USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A) }, + { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, + MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, + USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100) }, /* 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) }, @@ -1014,37 +1208,11 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) }, - /* Ideacom panel */ - { .driver_data = MT_CLS_SERIAL, - MT_USB_DEVICE(USB_VENDOR_ID_IDEACOM, - USB_DEVICE_ID_IDEACOM_IDC6650) }, - { .driver_data = MT_CLS_SERIAL, - MT_USB_DEVICE(USB_VENDOR_ID_IDEACOM, - 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) }, - /* IRTOUCH panels */ - { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, - MT_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, - USB_DEVICE_ID_IRTOUCH_INFRARED_USB) }, - - /* LG Display panels */ - { .driver_data = MT_CLS_DEFAULT, - MT_USB_DEVICE(USB_VENDOR_ID_LG, - USB_DEVICE_ID_LG_MULTITOUCH) }, - - /* Lumio panels */ - { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, - MT_USB_DEVICE(USB_VENDOR_ID_LUMIO, - USB_DEVICE_ID_CRYSTALTOUCH) }, - { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, - MT_USB_DEVICE(USB_VENDOR_ID_LUMIO, - USB_DEVICE_ID_CRYSTALTOUCH_DUAL) }, - /* MosArt panels */ { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, MT_USB_DEVICE(USB_VENDOR_ID_ASUS, @@ -1065,15 +1233,10 @@ 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) }, - /* PenMount panels */ - { .driver_data = MT_CLS_CONFIDENCE, - MT_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, - USB_DEVICE_ID_PENMOUNT_PCI) }, - /* PixArt optical touch screen */ { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, MT_USB_DEVICE(USB_VENDOR_ID_PIXART, @@ -1087,33 +1250,18 @@ static const struct hid_device_id mt_devices[] = { /* PixCir-based panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, - MT_USB_DEVICE(USB_VENDOR_ID_HANVON, - USB_DEVICE_ID_HANVON_MULTITOUCH) }, - { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, MT_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) }, /* Quanta-based panels */ { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, MT_USB_DEVICE(USB_VENDOR_ID_QUANTA, - USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, - { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, - MT_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001) }, - { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, - MT_USB_DEVICE(USB_VENDOR_ID_QUANTA, - USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) }, /* Stantum panels */ { .driver_data = MT_CLS_CONFIDENCE, - MT_USB_DEVICE(USB_VENDOR_ID_STANTUM, - USB_DEVICE_ID_MTP)}, - { .driver_data = MT_CLS_CONFIDENCE, MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_MTP_STM)}, - { .driver_data = MT_CLS_CONFIDENCE, - MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, - USB_DEVICE_ID_MTP_SITRONIX)}, /* TopSeed panels */ { .driver_data = MT_CLS_TOPSEED, @@ -1121,58 +1269,64 @@ 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) }, + + /* Wistron panels */ + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_WISTRON, + USB_DEVICE_ID_WISTRON_OPTICAL_TOUCH) }, + /* 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) }, - /* Zytronic panels */ - { .driver_data = MT_CLS_SERIAL, - MT_USB_DEVICE(USB_VENDOR_ID_ZYTRONIC, - USB_DEVICE_ID_ZYTRONIC_ZXY100) }, - /* Generic MT device */ { HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, + + /* Generic Win 8 certified MT device */ + { .driver_data = MT_CLS_WIN_8, + HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH_WIN_8, + HID_ANY_ID, HID_ANY_ID) }, { } }; MODULE_DEVICE_TABLE(hid, mt_devices); @@ -1193,21 +1347,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 86a969f6329..600f2075512 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -115,11 +115,12 @@ static inline int ntrig_get_mode(struct hid_device *hdev) struct hid_report *report = hdev->report_enum[HID_FEATURE_REPORT]. report_id_hash[0x0d]; - if (!report) + if (!report || report->maxfield < 1 || + report->field[0]->report_count < 1) return -EINVAL; - usbhid_submit_report(hdev, report, USB_DIR_IN); - usbhid_wait_io(hdev); + hid_hw_request(hdev, report, HID_REQ_GET_REPORT); + hid_hw_wait(hdev); return (int)report->field[0]->value[0]; } @@ -137,7 +138,7 @@ static inline void ntrig_set_mode(struct hid_device *hdev, const int mode) if (!report) return; - usbhid_submit_report(hdev, report, USB_DIR_IN); + hid_hw_request(hdev, report, HID_REQ_GET_REPORT); } static void ntrig_report_version(struct hid_device *hdev) @@ -237,7 +238,7 @@ static ssize_t set_min_width(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val > nd->sensor_physical_width) @@ -272,7 +273,7 @@ static ssize_t set_min_height(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val > nd->sensor_physical_height) @@ -306,7 +307,7 @@ static ssize_t set_activate_slack(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val > 0x7f) @@ -341,7 +342,7 @@ static ssize_t set_activation_width(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val > nd->sensor_physical_width) @@ -377,7 +378,7 @@ static ssize_t set_activation_height(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; if (val > nd->sensor_physical_height) @@ -411,7 +412,7 @@ static ssize_t set_deactivate_slack(struct device *dev, unsigned long val; - if (strict_strtoul(buf, 0, &val)) + if (kstrtoul(buf, 0, &val)) return -EINVAL; /* @@ -858,12 +859,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,45 +933,13 @@ 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) { /* Let the device settle to ensure the wakeup message gets * through */ - usbhid_wait_io(hdev); - usbhid_submit_report(hdev, report, USB_DIR_IN); + hid_hw_wait(hdev); + hid_hw_request(hdev, report, HID_REQ_GET_REPORT); /* * Sanity check: if the current mode is invalid reset it to @@ -1023,20 +1023,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 0ffa1d2d64f..6620f15fec2 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 4c521de4e7e..736b2502df4 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.h b/drivers/hid/hid-picolcd.h index 020cef69f6a..e56d847b2ef 100644 --- a/drivers/hid/hid-picolcd.h +++ b/drivers/hid/hid-picolcd.h @@ -142,10 +142,10 @@ struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir); #ifdef CONFIG_DEBUG_FS void picolcd_debug_out_report(struct picolcd_data *data, struct hid_device *hdev, struct hid_report *report); -#define usbhid_submit_report(a, b, c) \ +#define hid_hw_request(a, b, c) \ do { \ picolcd_debug_out_report(hid_get_drvdata(a), a, b); \ - usbhid_submit_report(a, b, c); \ + hid_hw_request(a, b, c); \ } while (0) void picolcd_debug_raw_event(struct picolcd_data *data, @@ -302,7 +302,7 @@ static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report static inline void picolcd_exit_cir(struct picolcd_data *data) { } -#endif /* CONFIG_HID_PICOLCD_LIRC */ +#endif /* CONFIG_HID_PICOLCD_CIR */ int picolcd_reset(struct hid_device *hdev); struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, diff --git a/drivers/hid/hid-picolcd_backlight.c b/drivers/hid/hid-picolcd_backlight.c index b91f30945f9..a32c5f86b0b 100644 --- a/drivers/hid/hid-picolcd_backlight.c +++ b/drivers/hid/hid-picolcd_backlight.c @@ -18,8 +18,6 @@ ***************************************************************************/ #include <linux/hid.h> -#include "usbhid/usbhid.h" -#include <linux/usb.h> #include <linux/fb.h> #include <linux/backlight.h> @@ -46,7 +44,7 @@ static int picolcd_set_brightness(struct backlight_device *bdev) spin_lock_irqsave(&data->lock, flags); hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0); if (!(data->status & PICOLCD_FAILED)) - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); return 0; } diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c index a79e95bb9fb..cf1a9f1c121 100644 --- a/drivers/hid/hid-picolcd_cir.c +++ b/drivers/hid/hid-picolcd_cir.c @@ -21,8 +21,6 @@ #include <linux/hid-debug.h> #include <linux/input.h> #include "hid-ids.h" -#include "usbhid/usbhid.h" -#include <linux/usb.h> #include <linux/fb.h> #include <linux/vmalloc.h> @@ -116,7 +114,7 @@ int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report) rdev->priv = data; rdev->driver_type = RC_DRIVER_IR_RAW; - rdev->allowed_protos = RC_BIT_ALL; + rc_set_allowed_protocols(rdev, RC_BIT_ALL); rdev->open = picolcd_cir_open; rdev->close = picolcd_cir_close; rdev->input_name = data->hdev->name; @@ -147,6 +145,7 @@ void picolcd_exit_cir(struct picolcd_data *data) struct rc_dev *rdev = data->rc_dev; data->rc_dev = NULL; - rc_unregister_device(rdev); + if (rdev) + rc_unregister_device(rdev); } diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index 86df26e58ab..acbb021065e 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -21,8 +21,6 @@ #include <linux/hid-debug.h> #include <linux/input.h> #include "hid-ids.h" -#include "usbhid/usbhid.h" -#include <linux/usb.h> #include <linux/fb.h> #include <linux/vmalloc.h> @@ -110,7 +108,7 @@ struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, work = NULL; } else { data->pending = work; - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); wait_for_completion_interruptible_timeout(&work->ready, HZ*2); spin_lock_irqsave(&data->lock, flags); @@ -244,7 +242,7 @@ int picolcd_reset(struct hid_device *hdev) spin_unlock_irqrestore(&data->lock, flags); return -ENODEV; } - usbhid_submit_report(hdev, report, USB_DIR_OUT); + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); error = picolcd_check_version(hdev); @@ -292,7 +290,7 @@ static ssize_t picolcd_operation_mode_store(struct device *dev, buf += 10; cnt -= 10; } - if (!report) + if (!report || report->maxfield != 1) return -EINVAL; while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r')) @@ -303,7 +301,7 @@ static ssize_t picolcd_operation_mode_store(struct device *dev, spin_lock_irqsave(&data->lock, flags); hid_set_field(report->field[0], 0, timeout & 0xff); hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff); - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); return count; } @@ -672,18 +670,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-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c index 4809aa1bdb9..024cdf3c229 100644 --- a/drivers/hid/hid-picolcd_debugfs.c +++ b/drivers/hid/hid-picolcd_debugfs.c @@ -19,8 +19,6 @@ #include <linux/hid.h> #include <linux/hid-debug.h> -#include "usbhid/usbhid.h" -#include <linux/usb.h> #include <linux/fb.h> #include <linux/seq_file.h> @@ -396,7 +394,7 @@ static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data, void picolcd_debug_out_report(struct picolcd_data *data, struct hid_device *hdev, struct hid_report *report) { - u8 raw_data[70]; + u8 *raw_data; int raw_size = (report->size >> 3) + 1; char *buff; #define BUFF_SZ 256 @@ -409,20 +407,20 @@ void picolcd_debug_out_report(struct picolcd_data *data, if (!buff) return; - snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", - report->id, raw_size); - hid_debug_event(hdev, buff); - if (raw_size + 5 > sizeof(raw_data)) { + raw_data = hid_alloc_report_buf(report, GFP_ATOMIC); + if (!raw_data) { kfree(buff); - hid_debug_event(hdev, " TOO BIG\n"); return; - } else { - raw_data[0] = report->id; - hid_output_report(report, raw_data); - dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); - hid_debug_event(hdev, buff); } + snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", + report->id, raw_size); + hid_debug_event(hdev, buff); + raw_data[0] = report->id; + hid_output_report(report, raw_data); + dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); + hid_debug_event(hdev, buff); + switch (report->id) { case REPORT_LED_STATE: /* 1 data byte with GPO state */ @@ -646,6 +644,7 @@ void picolcd_debug_out_report(struct picolcd_data *data, break; } wake_up_interruptible(&hdev->debug_wait); + kfree(raw_data); kfree(buff); } diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index eb003574b63..7f965e23143 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c @@ -19,8 +19,6 @@ #include <linux/hid.h> #include <linux/vmalloc.h> -#include "usbhid/usbhid.h" -#include <linux/usb.h> #include <linux/fb.h> #include <linux/module.h> @@ -143,8 +141,8 @@ static int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap, else hid_set_field(report2->field[0], 4 + i - 32, tdata[i]); - usbhid_submit_report(data->hdev, report1, USB_DIR_OUT); - usbhid_submit_report(data->hdev, report2, USB_DIR_OUT); + hid_hw_request(data->hdev, report1, HID_REQ_SET_REPORT); + hid_hw_request(data->hdev, report2, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); return 0; } @@ -214,7 +212,7 @@ int picolcd_fb_reset(struct picolcd_data *data, int clear) hid_set_field(report->field[0], j, mapcmd[j]); else hid_set_field(report->field[0], j, 0); - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); } spin_unlock_irqrestore(&data->lock, flags); @@ -270,7 +268,7 @@ static void picolcd_fb_update(struct fb_info *info) mutex_unlock(&info->lock); if (!data) return; - usbhid_wait_io(data->hdev); + hid_hw_wait(data->hdev); mutex_lock(&info->lock); n = 0; } @@ -288,7 +286,7 @@ static void picolcd_fb_update(struct fb_info *info) spin_unlock_irqrestore(&fbdata->lock, flags); mutex_unlock(&info->lock); if (data) - usbhid_wait_io(data->hdev); + hid_hw_wait(data->hdev); return; } out: @@ -503,7 +501,7 @@ static ssize_t picolcd_fb_update_rate_store(struct device *dev, return count; } -static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show, +static DEVICE_ATTR(fb_update_rate, 0664, picolcd_fb_update_rate_show, picolcd_fb_update_rate_store); /* initialize Framebuffer device */ @@ -595,10 +593,14 @@ err_nomem: void picolcd_exit_framebuffer(struct picolcd_data *data) { struct fb_info *info = data->fb_info; - struct picolcd_fb_data *fbdata = info->par; + struct picolcd_fb_data *fbdata; unsigned long flags; + if (!info) + return; + device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); + fbdata = info->par; /* disconnect framebuffer from HID dev */ spin_lock_irqsave(&fbdata->lock, flags); diff --git a/drivers/hid/hid-picolcd_lcd.c b/drivers/hid/hid-picolcd_lcd.c index 2d0ddc5ac65..89821c2da6d 100644 --- a/drivers/hid/hid-picolcd_lcd.c +++ b/drivers/hid/hid-picolcd_lcd.c @@ -18,8 +18,6 @@ ***************************************************************************/ #include <linux/hid.h> -#include "usbhid/usbhid.h" -#include <linux/usb.h> #include <linux/fb.h> #include <linux/lcd.h> @@ -48,7 +46,7 @@ static int picolcd_set_contrast(struct lcd_device *ldev, int contrast) spin_lock_irqsave(&data->lock, flags); hid_set_field(report->field[0], 0, data->lcd_contrast); if (!(data->status & PICOLCD_FAILED)) - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); return 0; } diff --git a/drivers/hid/hid-picolcd_leds.c b/drivers/hid/hid-picolcd_leds.c index 28cb6a4f963..e994f9c2901 100644 --- a/drivers/hid/hid-picolcd_leds.c +++ b/drivers/hid/hid-picolcd_leds.c @@ -21,8 +21,6 @@ #include <linux/hid-debug.h> #include <linux/input.h> #include "hid-ids.h" -#include "usbhid/usbhid.h" -#include <linux/usb.h> #include <linux/fb.h> #include <linux/vmalloc.h> @@ -55,7 +53,7 @@ void picolcd_leds_set(struct picolcd_data *data) spin_lock_irqsave(&data->lock, flags); hid_set_field(report->field[0], 0, data->led_state); if (!(data->status & PICOLCD_FAILED)) - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); } diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index 47ed74c46b6..2dcd7d98dbd 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> */ @@ -41,16 +43,15 @@ #include <linux/input.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/usb.h> #include <linux/hid.h> #include "hid-ids.h" #ifdef CONFIG_PANTHERLORD_FF -#include "usbhid/usbhid.h" struct plff_device { struct hid_report *report; + s32 maxval; s32 *strong; s32 *weak; }; @@ -66,13 +67,13 @@ 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; debug("running with 0x%02x 0x%02x", left, right); - usbhid_submit_report(hid, plff->report, USB_DIR_OUT); + hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT); return 0; } @@ -87,6 +88,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,18 +125,27 @@ 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; strong = &report->field[0]->value[2]; weak = &report->field[0]->value[3]; debug("detected single-field device"); - } else if (report->maxfield >= 4 && report->field[0]->maxusage == 1 && - report->field[0]->usage[0].hid == (HID_UP_LED | 0x43)) { + } else if (report->field[0]->maxusage == 1 && + report->field[0]->usage[0].hid == + (HID_UP_LED | 0x43) && + report->maxfield >= 4 && + report->field[0]->report_count >= 1 && + report->field[1]->report_count >= 1 && + report->field[2]->report_count >= 1 && + report->field[3]->report_count >= 1) { report->field[0]->value[0] = 0x00; 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,10 +169,11 @@ static int plff_init(struct hid_device *hid) plff->report = report; plff->strong = strong; plff->weak = weak; + plff->maxval = maxval; *strong = 0x00; *weak = 0x00; - usbhid_submit_report(hid, plff->report, USB_DIR_OUT); + hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT); } hid_info(hid, "Force feedback for PantherLord/GreenAsia devices by Anssi Hannula <anssi.hannula@gmail.com>\n"); @@ -207,6 +219,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 +229,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 c15adb0c98a..3a1c3c4c50d 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 ec8ca333631..91fab975063 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c @@ -26,7 +26,6 @@ #include <sound/core.h> #include <sound/initval.h> #include <sound/rawmidi.h> -#include "usbhid/usbhid.h" #include "hid-ids.h" @@ -306,7 +305,7 @@ static void pcmidi_submit_output_report(struct pcmidi_snd *pm, int state) report->field[0]->value[0] = 0x01; report->field[0]->value[1] = state; - usbhid_submit_report(hdev, report, USB_DIR_OUT); + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); } static int pcmidi_handle_report1(struct pcmidi_snd *pm, u8 *data) @@ -625,7 +624,8 @@ static int pcmidi_snd_initialise(struct pcmidi_snd *pm) /* Setup sound card */ - err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); + err = snd_card_new(&pm->pk->hdev->dev, index[dev], id[dev], + THIS_MODULE, 0, &card); if (err < 0) { pk_error("failed to create pc-midi sound card\n"); err = -ENOMEM; @@ -661,8 +661,6 @@ static int pcmidi_snd_initialise(struct pcmidi_snd *pm) snd_rawmidi_set_ops(rwmidi, SNDRV_RAWMIDI_STREAM_INPUT, &pcmidi_in_ops); - snd_card_set_dev(card, &pm->pk->hdev->dev); - /* create sysfs variables */ err = device_create_file(&pm->pk->hdev->dev, sysfs_device_attr_channel); @@ -889,23 +887,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 deleted file mode 100644 index 03811e539d7..00000000000 --- a/drivers/hid/hid-ps3remote.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * HID driver for Sony PS3 BD Remote Control - * - * Copyright (c) 2012 David Dillow <dave@thedillows.org> - * Based on a blend of the bluez fakehid user-space code by Marcel Holtmann - * and other kernel HID drivers. - */ - -/* - * 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. - */ - -/* NOTE: in order for the Sony PS3 BD Remote Control to be found by - * a Bluetooth host, the key combination Start+Enter has to be kept pressed - * for about 7 seconds with the Bluetooth Host Controller in discovering mode. - * - * There will be no PIN request from the device. - */ - -#include <linux/device.h> -#include <linux/hid.h> -#include <linux/module.h> - -#include "hid-ids.h" - -static __u8 ps3remote_rdesc[] = { - 0x05, 0x01, /* GUsagePage Generic Desktop */ - 0x09, 0x05, /* LUsage 0x05 [Game Pad] */ - 0xA1, 0x01, /* MCollection Application (mouse, keyboard) */ - - /* Use collection 1 for joypad buttons */ - 0xA1, 0x02, /* MCollection Logical (interrelated data) */ - - /* Ignore the 1st byte, maybe it is used for a controller - * number but it's not needed for correct operation */ - 0x75, 0x08, /* GReportSize 0x08 [8] */ - 0x95, 0x01, /* GReportCount 0x01 [1] */ - 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */ - - /* Bytes from 2nd to 4th are a bitmap for joypad buttons, for these - * buttons multiple keypresses are allowed */ - 0x05, 0x09, /* GUsagePage Button */ - 0x19, 0x01, /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */ - 0x29, 0x18, /* LUsageMaximum 0x18 [Button 24] */ - 0x14, /* GLogicalMinimum [0] */ - 0x25, 0x01, /* GLogicalMaximum 0x01 [1] */ - 0x75, 0x01, /* GReportSize 0x01 [1] */ - 0x95, 0x18, /* GReportCount 0x18 [24] */ - 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */ - - 0xC0, /* MEndCollection */ - - /* Use collection 2 for remote control buttons */ - 0xA1, 0x02, /* MCollection Logical (interrelated data) */ - - /* 5th byte is used for remote control buttons */ - 0x05, 0x09, /* GUsagePage Button */ - 0x18, /* LUsageMinimum [No button pressed] */ - 0x29, 0xFE, /* LUsageMaximum 0xFE [Button 254] */ - 0x14, /* GLogicalMinimum [0] */ - 0x26, 0xFE, 0x00, /* GLogicalMaximum 0x00FE [254] */ - 0x75, 0x08, /* GReportSize 0x08 [8] */ - 0x95, 0x01, /* GReportCount 0x01 [1] */ - 0x80, /* MInput */ - - /* Ignore bytes from 6th to 11th, 6th to 10th are always constant at - * 0xff and 11th is for press indication */ - 0x75, 0x08, /* GReportSize 0x08 [8] */ - 0x95, 0x06, /* GReportCount 0x06 [6] */ - 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */ - - /* 12th byte is for battery strength */ - 0x05, 0x06, /* GUsagePage Generic Device Controls */ - 0x09, 0x20, /* LUsage 0x20 [Battery Strength] */ - 0x14, /* GLogicalMinimum [0] */ - 0x25, 0x05, /* GLogicalMaximum 0x05 [5] */ - 0x75, 0x08, /* GReportSize 0x08 [8] */ - 0x95, 0x01, /* GReportCount 0x01 [1] */ - 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */ - - 0xC0, /* MEndCollection */ - - 0xC0 /* MEndCollection [Game Pad] */ -}; - -static const unsigned int ps3remote_keymap_joypad_buttons[] = { - [0x01] = KEY_SELECT, - [0x02] = BTN_THUMBL, /* L3 */ - [0x03] = BTN_THUMBR, /* R3 */ - [0x04] = BTN_START, - [0x05] = KEY_UP, - [0x06] = KEY_RIGHT, - [0x07] = KEY_DOWN, - [0x08] = KEY_LEFT, - [0x09] = BTN_TL2, /* L2 */ - [0x0a] = BTN_TR2, /* R2 */ - [0x0b] = BTN_TL, /* L1 */ - [0x0c] = BTN_TR, /* R1 */ - [0x0d] = KEY_OPTION, /* options/triangle */ - [0x0e] = KEY_BACK, /* back/circle */ - [0x0f] = BTN_0, /* cross */ - [0x10] = KEY_SCREEN, /* view/square */ - [0x11] = KEY_HOMEPAGE, /* PS button */ - [0x14] = KEY_ENTER, -}; -static const unsigned int ps3remote_keymap_remote_buttons[] = { - [0x00] = KEY_1, - [0x01] = KEY_2, - [0x02] = KEY_3, - [0x03] = KEY_4, - [0x04] = KEY_5, - [0x05] = KEY_6, - [0x06] = KEY_7, - [0x07] = KEY_8, - [0x08] = KEY_9, - [0x09] = KEY_0, - [0x0e] = KEY_ESC, /* return */ - [0x0f] = KEY_CLEAR, - [0x16] = KEY_EJECTCD, - [0x1a] = KEY_MENU, /* top menu */ - [0x28] = KEY_TIME, - [0x30] = KEY_PREVIOUS, - [0x31] = KEY_NEXT, - [0x32] = KEY_PLAY, - [0x33] = KEY_REWIND, /* scan back */ - [0x34] = KEY_FORWARD, /* scan forward */ - [0x38] = KEY_STOP, - [0x39] = KEY_PAUSE, - [0x40] = KEY_CONTEXT_MENU, /* pop up/menu */ - [0x60] = KEY_FRAMEBACK, /* slow/step back */ - [0x61] = KEY_FRAMEFORWARD, /* slow/step forward */ - [0x63] = KEY_SUBTITLE, - [0x64] = KEY_AUDIO, - [0x65] = KEY_ANGLE, - [0x70] = KEY_INFO, /* display */ - [0x80] = KEY_BLUE, - [0x81] = KEY_RED, - [0x82] = KEY_GREEN, - [0x83] = KEY_YELLOW, -}; - -static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) -{ - *rsize = sizeof(ps3remote_rdesc); - return ps3remote_rdesc; -} - -static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - unsigned int key = usage->hid & HID_USAGE; - - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON) - return -1; - - switch (usage->collection_index) { - case 1: - if (key >= ARRAY_SIZE(ps3remote_keymap_joypad_buttons)) - return -1; - - key = ps3remote_keymap_joypad_buttons[key]; - if (!key) - return -1; - break; - case 2: - if (key >= ARRAY_SIZE(ps3remote_keymap_remote_buttons)) - return -1; - - key = ps3remote_keymap_remote_buttons[key]; - if (!key) - return -1; - break; - default: - return -1; - } - - hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); - return 1; -} - -static const struct hid_device_id ps3remote_devices[] = { - /* PS3 BD Remote Control */ - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) }, - /* Logitech Harmony Adapter for PS3 */ - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) }, - { } -}; -MODULE_DEVICE_TABLE(hid, ps3remote_devices); - -static struct hid_driver ps3remote_driver = { - .name = "ps3_remote", - .id_table = ps3remote_devices, - .report_fixup = ps3remote_fixup, - .input_mapping = ps3remote_mapping, -}; - -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-rmi.c b/drivers/hid/hid-rmi.c new file mode 100644 index 00000000000..578bbe65902 --- /dev/null +++ b/drivers/hid/hid-rmi.c @@ -0,0 +1,922 @@ +/* + * Copyright (c) 2013 Andrew Duggan <aduggan@synaptics.com> + * Copyright (c) 2013 Synaptics Incorporated + * Copyright (c) 2014 Benjamin Tissoires <benjamin.tissoires@gmail.com> + * Copyright (c) 2014 Red Hat, Inc + * + * 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/kernel.h> +#include <linux/hid.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include "hid-ids.h" + +#define RMI_MOUSE_REPORT_ID 0x01 /* Mouse emulation Report */ +#define RMI_WRITE_REPORT_ID 0x09 /* Output Report */ +#define RMI_READ_ADDR_REPORT_ID 0x0a /* Output Report */ +#define RMI_READ_DATA_REPORT_ID 0x0b /* Input Report */ +#define RMI_ATTN_REPORT_ID 0x0c /* Input Report */ +#define RMI_SET_RMI_MODE_REPORT_ID 0x0f /* Feature Report */ + +/* flags */ +#define RMI_READ_REQUEST_PENDING BIT(0) +#define RMI_READ_DATA_PENDING BIT(1) +#define RMI_STARTED BIT(2) + +enum rmi_mode_type { + RMI_MODE_OFF = 0, + RMI_MODE_ATTN_REPORTS = 1, + RMI_MODE_NO_PACKED_ATTN_REPORTS = 2, +}; + +struct rmi_function { + unsigned page; /* page of the function */ + u16 query_base_addr; /* base address for queries */ + u16 command_base_addr; /* base address for commands */ + u16 control_base_addr; /* base address for controls */ + u16 data_base_addr; /* base address for datas */ + unsigned int interrupt_base; /* cross-function interrupt number + * (uniq in the device)*/ + unsigned int interrupt_count; /* number of interrupts */ + unsigned int report_size; /* size of a report */ + unsigned long irq_mask; /* mask of the interrupts + * (to be applied against ATTN IRQ) */ +}; + +/** + * struct rmi_data - stores information for hid communication + * + * @page_mutex: Locks current page to avoid changing pages in unexpected ways. + * @page: Keeps track of the current virtual page + * + * @wait: Used for waiting for read data + * + * @writeReport: output buffer when writing RMI registers + * @readReport: input buffer when reading RMI registers + * + * @input_report_size: size of an input report (advertised by HID) + * @output_report_size: size of an output report (advertised by HID) + * + * @flags: flags for the current device (started, reading, etc...) + * + * @f11: placeholder of internal RMI function F11 description + * @f30: placeholder of internal RMI function F30 description + * + * @max_fingers: maximum finger count reported by the device + * @max_x: maximum x value reported by the device + * @max_y: maximum y value reported by the device + * + * @gpio_led_count: count of GPIOs + LEDs reported by F30 + * @button_count: actual physical buttons count + * @button_mask: button mask used to decode GPIO ATTN reports + * @button_state_mask: pull state of the buttons + * + * @input: pointer to the kernel input device + * + * @reset_work: worker which will be called in case of a mouse report + * @hdev: pointer to the struct hid_device + */ +struct rmi_data { + struct mutex page_mutex; + int page; + + wait_queue_head_t wait; + + u8 *writeReport; + u8 *readReport; + + int input_report_size; + int output_report_size; + + unsigned long flags; + + struct rmi_function f11; + struct rmi_function f30; + + unsigned int max_fingers; + unsigned int max_x; + unsigned int max_y; + unsigned int x_size_mm; + unsigned int y_size_mm; + + unsigned int gpio_led_count; + unsigned int button_count; + unsigned long button_mask; + unsigned long button_state_mask; + + struct input_dev *input; + + struct work_struct reset_work; + struct hid_device *hdev; +}; + +#define RMI_PAGE(addr) (((addr) >> 8) & 0xff) + +static int rmi_write_report(struct hid_device *hdev, u8 *report, int len); + +/** + * rmi_set_page - Set RMI page + * @hdev: The pointer to the hid_device struct + * @page: The new page address. + * + * RMI devices have 16-bit addressing, but some of the physical + * implementations (like SMBus) only have 8-bit addressing. So RMI implements + * a page address at 0xff of every page so we can reliable page addresses + * every 256 registers. + * + * The page_mutex lock must be held when this function is entered. + * + * Returns zero on success, non-zero on failure. + */ +static int rmi_set_page(struct hid_device *hdev, u8 page) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + int retval; + + data->writeReport[0] = RMI_WRITE_REPORT_ID; + data->writeReport[1] = 1; + data->writeReport[2] = 0xFF; + data->writeReport[4] = page; + + retval = rmi_write_report(hdev, data->writeReport, + data->output_report_size); + if (retval != data->output_report_size) { + dev_err(&hdev->dev, + "%s: set page failed: %d.", __func__, retval); + return retval; + } + + data->page = page; + return 0; +} + +static int rmi_set_mode(struct hid_device *hdev, u8 mode) +{ + int ret; + u8 txbuf[2] = {RMI_SET_RMI_MODE_REPORT_ID, mode}; + + ret = hid_hw_raw_request(hdev, RMI_SET_RMI_MODE_REPORT_ID, txbuf, + sizeof(txbuf), HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret < 0) { + dev_err(&hdev->dev, "unable to set rmi mode to %d (%d)\n", mode, + ret); + return ret; + } + + return 0; +} + +static int rmi_write_report(struct hid_device *hdev, u8 *report, int len) +{ + int ret; + + ret = hid_hw_output_report(hdev, (void *)report, len); + if (ret < 0) { + dev_err(&hdev->dev, "failed to write hid report (%d)\n", ret); + return ret; + } + + return ret; +} + +static int rmi_read_block(struct hid_device *hdev, u16 addr, void *buf, + const int len) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + int ret; + int bytes_read; + int bytes_needed; + int retries; + int read_input_count; + + mutex_lock(&data->page_mutex); + + if (RMI_PAGE(addr) != data->page) { + ret = rmi_set_page(hdev, RMI_PAGE(addr)); + if (ret < 0) + goto exit; + } + + for (retries = 5; retries > 0; retries--) { + data->writeReport[0] = RMI_READ_ADDR_REPORT_ID; + data->writeReport[1] = 0; /* old 1 byte read count */ + data->writeReport[2] = addr & 0xFF; + data->writeReport[3] = (addr >> 8) & 0xFF; + data->writeReport[4] = len & 0xFF; + data->writeReport[5] = (len >> 8) & 0xFF; + + set_bit(RMI_READ_REQUEST_PENDING, &data->flags); + + ret = rmi_write_report(hdev, data->writeReport, + data->output_report_size); + if (ret != data->output_report_size) { + clear_bit(RMI_READ_REQUEST_PENDING, &data->flags); + dev_err(&hdev->dev, + "failed to write request output report (%d)\n", + ret); + goto exit; + } + + bytes_read = 0; + bytes_needed = len; + while (bytes_read < len) { + if (!wait_event_timeout(data->wait, + test_bit(RMI_READ_DATA_PENDING, &data->flags), + msecs_to_jiffies(1000))) { + hid_warn(hdev, "%s: timeout elapsed\n", + __func__); + ret = -EAGAIN; + break; + } + + read_input_count = data->readReport[1]; + memcpy(buf + bytes_read, &data->readReport[2], + read_input_count < bytes_needed ? + read_input_count : bytes_needed); + + bytes_read += read_input_count; + bytes_needed -= read_input_count; + clear_bit(RMI_READ_DATA_PENDING, &data->flags); + } + + if (ret >= 0) { + ret = 0; + break; + } + } + +exit: + clear_bit(RMI_READ_REQUEST_PENDING, &data->flags); + mutex_unlock(&data->page_mutex); + return ret; +} + +static inline int rmi_read(struct hid_device *hdev, u16 addr, void *buf) +{ + return rmi_read_block(hdev, addr, buf, 1); +} + +static void rmi_f11_process_touch(struct rmi_data *hdata, int slot, + u8 finger_state, u8 *touch_data) +{ + int x, y, wx, wy; + int wide, major, minor; + int z; + + input_mt_slot(hdata->input, slot); + input_mt_report_slot_state(hdata->input, MT_TOOL_FINGER, + finger_state == 0x01); + if (finger_state == 0x01) { + x = (touch_data[0] << 4) | (touch_data[2] & 0x0F); + y = (touch_data[1] << 4) | (touch_data[2] >> 4); + wx = touch_data[3] & 0x0F; + wy = touch_data[3] >> 4; + wide = (wx > wy); + major = max(wx, wy); + minor = min(wx, wy); + z = touch_data[4]; + + /* y is inverted */ + y = hdata->max_y - y; + + input_event(hdata->input, EV_ABS, ABS_MT_POSITION_X, x); + input_event(hdata->input, EV_ABS, ABS_MT_POSITION_Y, y); + input_event(hdata->input, EV_ABS, ABS_MT_ORIENTATION, wide); + input_event(hdata->input, EV_ABS, ABS_MT_PRESSURE, z); + input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); + input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); + } +} + +static void rmi_reset_work(struct work_struct *work) +{ + struct rmi_data *hdata = container_of(work, struct rmi_data, + reset_work); + + /* switch the device to RMI if we receive a generic mouse report */ + rmi_set_mode(hdata->hdev, RMI_MODE_ATTN_REPORTS); +} + +static inline int rmi_schedule_reset(struct hid_device *hdev) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + return schedule_work(&hdata->reset_work); +} + +static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data, + int size) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + int offset; + int i; + + if (size < hdata->f11.report_size) + return 0; + + if (!(irq & hdata->f11.irq_mask)) + return 0; + + offset = (hdata->max_fingers >> 2) + 1; + for (i = 0; i < hdata->max_fingers; i++) { + int fs_byte_position = i >> 2; + int fs_bit_position = (i & 0x3) << 1; + int finger_state = (data[fs_byte_position] >> fs_bit_position) & + 0x03; + + rmi_f11_process_touch(hdata, i, finger_state, + &data[offset + 5 * i]); + } + input_mt_sync_frame(hdata->input); + input_sync(hdata->input); + return hdata->f11.report_size; +} + +static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data, + int size) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + int i; + int button = 0; + bool value; + + if (!(irq & hdata->f30.irq_mask)) + return 0; + + for (i = 0; i < hdata->gpio_led_count; i++) { + if (test_bit(i, &hdata->button_mask)) { + value = (data[i / 8] >> (i & 0x07)) & BIT(0); + if (test_bit(i, &hdata->button_state_mask)) + value = !value; + input_event(hdata->input, EV_KEY, BTN_LEFT + button++, + value); + } + } + return hdata->f30.report_size; +} + +static int rmi_input_event(struct hid_device *hdev, u8 *data, int size) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + unsigned long irq_mask = 0; + unsigned index = 2; + + if (!(test_bit(RMI_STARTED, &hdata->flags))) + return 0; + + irq_mask |= hdata->f11.irq_mask; + irq_mask |= hdata->f30.irq_mask; + + if (data[1] & ~irq_mask) + hid_warn(hdev, "unknown intr source:%02lx %s:%d\n", + data[1] & ~irq_mask, __FILE__, __LINE__); + + if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) { + index += rmi_f11_input_event(hdev, data[1], &data[index], + size - index); + index += rmi_f30_input_event(hdev, data[1], &data[index], + size - index); + } else { + index += rmi_f30_input_event(hdev, data[1], &data[index], + size - index); + index += rmi_f11_input_event(hdev, data[1], &data[index], + size - index); + } + + return 1; +} + +static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + + if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) { + hid_err(hdev, "no read request pending\n"); + return 0; + } + + memcpy(hdata->readReport, data, size < hdata->input_report_size ? + size : hdata->input_report_size); + set_bit(RMI_READ_DATA_PENDING, &hdata->flags); + wake_up(&hdata->wait); + + return 1; +} + +static int rmi_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + switch (data[0]) { + case RMI_READ_DATA_REPORT_ID: + return rmi_read_data_event(hdev, data, size); + case RMI_ATTN_REPORT_ID: + return rmi_input_event(hdev, data, size); + case RMI_MOUSE_REPORT_ID: + rmi_schedule_reset(hdev); + break; + } + + return 0; +} + +#ifdef CONFIG_PM +static int rmi_post_reset(struct hid_device *hdev) +{ + return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); +} + +static int rmi_post_resume(struct hid_device *hdev) +{ + return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); +} +#endif /* CONFIG_PM */ + +#define RMI4_MAX_PAGE 0xff +#define RMI4_PAGE_SIZE 0x0100 + +#define PDT_START_SCAN_LOCATION 0x00e9 +#define PDT_END_SCAN_LOCATION 0x0005 +#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff) + +struct pdt_entry { + u8 query_base_addr:8; + u8 command_base_addr:8; + u8 control_base_addr:8; + u8 data_base_addr:8; + u8 interrupt_source_count:3; + u8 bits3and4:2; + u8 function_version:2; + u8 bit7:1; + u8 function_number:8; +} __attribute__((__packed__)); + +static inline unsigned long rmi_gen_mask(unsigned irq_base, unsigned irq_count) +{ + return GENMASK(irq_count + irq_base - 1, irq_base); +} + +static void rmi_register_function(struct rmi_data *data, + struct pdt_entry *pdt_entry, int page, unsigned interrupt_count) +{ + struct rmi_function *f = NULL; + u16 page_base = page << 8; + + switch (pdt_entry->function_number) { + case 0x11: + f = &data->f11; + break; + case 0x30: + f = &data->f30; + break; + } + + if (f) { + f->page = page; + f->query_base_addr = page_base | pdt_entry->query_base_addr; + f->command_base_addr = page_base | pdt_entry->command_base_addr; + f->control_base_addr = page_base | pdt_entry->control_base_addr; + f->data_base_addr = page_base | pdt_entry->data_base_addr; + f->interrupt_base = interrupt_count; + f->interrupt_count = pdt_entry->interrupt_source_count; + f->irq_mask = rmi_gen_mask(f->interrupt_base, + f->interrupt_count); + } +} + +static int rmi_scan_pdt(struct hid_device *hdev) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + struct pdt_entry entry; + int page; + bool page_has_function; + int i; + int retval; + int interrupt = 0; + u16 page_start, pdt_start , pdt_end; + + hid_info(hdev, "Scanning PDT...\n"); + + for (page = 0; (page <= RMI4_MAX_PAGE); page++) { + page_start = RMI4_PAGE_SIZE * page; + pdt_start = page_start + PDT_START_SCAN_LOCATION; + pdt_end = page_start + PDT_END_SCAN_LOCATION; + + page_has_function = false; + for (i = pdt_start; i >= pdt_end; i -= sizeof(entry)) { + retval = rmi_read_block(hdev, i, &entry, sizeof(entry)); + if (retval) { + hid_err(hdev, + "Read of PDT entry at %#06x failed.\n", + i); + goto error_exit; + } + + if (RMI4_END_OF_PDT(entry.function_number)) + break; + + page_has_function = true; + + hid_info(hdev, "Found F%02X on page %#04x\n", + entry.function_number, page); + + rmi_register_function(data, &entry, page, interrupt); + interrupt += entry.interrupt_source_count; + } + + if (!page_has_function) + break; + } + + hid_info(hdev, "%s: Done with PDT scan.\n", __func__); + retval = 0; + +error_exit: + return retval; +} + +static int rmi_populate_f11(struct hid_device *hdev) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + u8 buf[20]; + int ret; + bool has_query9; + bool has_query10; + bool has_query11; + bool has_query12; + bool has_physical_props; + unsigned x_size, y_size; + u16 query12_offset; + + if (!data->f11.query_base_addr) { + hid_err(hdev, "No 2D sensor found, giving up.\n"); + return -ENODEV; + } + + /* query 0 contains some useful information */ + ret = rmi_read(hdev, data->f11.query_base_addr, buf); + if (ret) { + hid_err(hdev, "can not get query 0: %d.\n", ret); + return ret; + } + has_query9 = !!(buf[0] & BIT(3)); + has_query11 = !!(buf[0] & BIT(4)); + has_query12 = !!(buf[0] & BIT(5)); + + /* query 1 to get the max number of fingers */ + ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf); + if (ret) { + hid_err(hdev, "can not get NumberOfFingers: %d.\n", ret); + return ret; + } + data->max_fingers = (buf[0] & 0x07) + 1; + if (data->max_fingers > 5) + data->max_fingers = 10; + + data->f11.report_size = data->max_fingers * 5 + + DIV_ROUND_UP(data->max_fingers, 4); + + if (!(buf[0] & BIT(4))) { + hid_err(hdev, "No absolute events, giving up.\n"); + return -ENODEV; + } + + /* query 8 to find out if query 10 exists */ + ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf); + if (ret) { + hid_err(hdev, "can not read gesture information: %d.\n", ret); + return ret; + } + has_query10 = !!(buf[0] & BIT(2)); + + /* + * At least 8 queries are guaranteed to be present in F11 + * +1 for query12. + */ + query12_offset = 9; + + if (has_query9) + ++query12_offset; + + if (has_query10) + ++query12_offset; + + if (has_query11) + ++query12_offset; + + /* query 12 to know if the physical properties are reported */ + if (has_query12) { + ret = rmi_read(hdev, data->f11.query_base_addr + + query12_offset, buf); + if (ret) { + hid_err(hdev, "can not get query 12: %d.\n", ret); + return ret; + } + has_physical_props = !!(buf[0] & BIT(5)); + + if (has_physical_props) { + ret = rmi_read_block(hdev, + data->f11.query_base_addr + + query12_offset + 1, buf, 4); + if (ret) { + hid_err(hdev, "can not read query 15-18: %d.\n", + ret); + return ret; + } + + x_size = buf[0] | (buf[1] << 8); + y_size = buf[2] | (buf[3] << 8); + + data->x_size_mm = DIV_ROUND_CLOSEST(x_size, 10); + data->y_size_mm = DIV_ROUND_CLOSEST(y_size, 10); + + hid_info(hdev, "%s: size in mm: %d x %d\n", + __func__, data->x_size_mm, data->y_size_mm); + } + } + + /* + * retrieve the ctrl registers + * the ctrl register has a size of 20 but a fw bug split it into 16 + 4, + * and there is no way to know if the first 20 bytes are here or not. + * We use only the first 10 bytes, so get only them. + */ + ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 10); + if (ret) { + hid_err(hdev, "can not read ctrl block of size 10: %d.\n", ret); + return ret; + } + + data->max_x = buf[6] | (buf[7] << 8); + data->max_y = buf[8] | (buf[9] << 8); + + return 0; +} + +static int rmi_populate_f30(struct hid_device *hdev) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + u8 buf[20]; + int ret; + bool has_gpio, has_led; + unsigned bytes_per_ctrl; + u8 ctrl2_addr; + int ctrl2_3_length; + int i; + + /* function F30 is for physical buttons */ + if (!data->f30.query_base_addr) { + hid_err(hdev, "No GPIO/LEDs found, giving up.\n"); + return -ENODEV; + } + + ret = rmi_read_block(hdev, data->f30.query_base_addr, buf, 2); + if (ret) { + hid_err(hdev, "can not get F30 query registers: %d.\n", ret); + return ret; + } + + has_gpio = !!(buf[0] & BIT(3)); + has_led = !!(buf[0] & BIT(2)); + data->gpio_led_count = buf[1] & 0x1f; + + /* retrieve ctrl 2 & 3 registers */ + bytes_per_ctrl = (data->gpio_led_count + 7) / 8; + /* Ctrl0 is present only if both has_gpio and has_led are set*/ + ctrl2_addr = (has_gpio && has_led) ? bytes_per_ctrl : 0; + /* Ctrl1 is always be present */ + ctrl2_addr += bytes_per_ctrl; + ctrl2_3_length = 2 * bytes_per_ctrl; + + data->f30.report_size = bytes_per_ctrl; + + ret = rmi_read_block(hdev, data->f30.control_base_addr + ctrl2_addr, + buf, ctrl2_3_length); + if (ret) { + hid_err(hdev, "can not read ctrl 2&3 block of size %d: %d.\n", + ctrl2_3_length, ret); + return ret; + } + + for (i = 0; i < data->gpio_led_count; i++) { + int byte_position = i >> 3; + int bit_position = i & 0x07; + u8 dir_byte = buf[byte_position]; + u8 data_byte = buf[byte_position + bytes_per_ctrl]; + bool dir = (dir_byte >> bit_position) & BIT(0); + bool dat = (data_byte >> bit_position) & BIT(0); + + if (dir == 0) { + /* input mode */ + if (dat) { + /* actual buttons have pull up resistor */ + data->button_count++; + set_bit(i, &data->button_mask); + set_bit(i, &data->button_state_mask); + } + } + + } + + return 0; +} + +static int rmi_populate(struct hid_device *hdev) +{ + int ret; + + ret = rmi_scan_pdt(hdev); + if (ret) { + hid_err(hdev, "PDT scan failed with code %d.\n", ret); + return ret; + } + + ret = rmi_populate_f11(hdev); + if (ret) { + hid_err(hdev, "Error while initializing F11 (%d).\n", ret); + return ret; + } + + ret = rmi_populate_f30(hdev); + if (ret) + hid_warn(hdev, "Error while initializing F30 (%d).\n", ret); + + return 0; +} + +static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + struct input_dev *input = hi->input; + int ret; + int res_x, res_y, i; + + data->input = input; + + hid_dbg(hdev, "Opening low level driver\n"); + ret = hid_hw_open(hdev); + if (ret) + return; + + /* Allow incoming hid reports */ + hid_device_io_start(hdev); + + ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); + if (ret < 0) { + dev_err(&hdev->dev, "failed to set rmi mode\n"); + goto exit; + } + + ret = rmi_set_page(hdev, 0); + if (ret < 0) { + dev_err(&hdev->dev, "failed to set page select to 0.\n"); + goto exit; + } + + ret = rmi_populate(hdev); + if (ret) + goto exit; + + __set_bit(EV_ABS, input->evbit); + input_set_abs_params(input, ABS_MT_POSITION_X, 1, data->max_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 1, data->max_y, 0, 0); + + if (data->x_size_mm && data->y_size_mm) { + res_x = (data->max_x - 1) / data->x_size_mm; + res_y = (data->max_y - 1) / data->y_size_mm; + + input_abs_set_res(input, ABS_MT_POSITION_X, res_x); + input_abs_set_res(input, ABS_MT_POSITION_Y, res_y); + } + + input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); + + input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER); + + if (data->button_count) { + __set_bit(EV_KEY, input->evbit); + for (i = 0; i < data->button_count; i++) + __set_bit(BTN_LEFT + i, input->keybit); + + if (data->button_count == 1) + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + } + + set_bit(RMI_STARTED, &data->flags); + +exit: + hid_device_io_stop(hdev); + hid_hw_close(hdev); +} + +static int rmi_input_mapping(struct hid_device *hdev, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + /* we want to make HID ignore the advertised HID collection */ + return -1; +} + +static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct rmi_data *data = NULL; + int ret; + size_t alloc_size; + + data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + INIT_WORK(&data->reset_work, rmi_reset_work); + data->hdev = hdev; + + hid_set_drvdata(hdev, data); + + hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + return ret; + } + + data->input_report_size = (hdev->report_enum[HID_INPUT_REPORT] + .report_id_hash[RMI_ATTN_REPORT_ID]->size >> 3) + + 1 /* report id */; + data->output_report_size = (hdev->report_enum[HID_OUTPUT_REPORT] + .report_id_hash[RMI_WRITE_REPORT_ID]->size >> 3) + + 1 /* report id */; + + alloc_size = data->output_report_size + data->input_report_size; + + data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL); + if (!data->writeReport) { + ret = -ENOMEM; + return ret; + } + + data->readReport = data->writeReport + data->output_report_size; + + init_waitqueue_head(&data->wait); + + mutex_init(&data->page_mutex); + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + if (!test_bit(RMI_STARTED, &data->flags)) { + hid_hw_stop(hdev); + return -EIO; + } + + return 0; +} + +static void rmi_remove(struct hid_device *hdev) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + + clear_bit(RMI_STARTED, &hdata->flags); + + hid_hw_stop(hdev); +} + +static const struct hid_device_id rmi_id[] = { + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) }, + { } +}; +MODULE_DEVICE_TABLE(hid, rmi_id); + +static struct hid_driver rmi_driver = { + .name = "hid-rmi", + .id_table = rmi_id, + .probe = rmi_probe, + .remove = rmi_remove, + .raw_event = rmi_raw_event, + .input_mapping = rmi_input_mapping, + .input_configured = rmi_input_configured, +#ifdef CONFIG_PM + .resume = rmi_post_resume, + .reset_resume = rmi_post_reset, +#endif +}; + +module_hid_driver(rmi_driver); + +MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>"); +MODULE_DESCRIPTION("RMI HID driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index 327f9b8ed1f..1948208fe03 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -59,7 +59,7 @@ static ssize_t arvo_sysfs_set_mode_key(struct device *dev, unsigned long state; int retval; - retval = strict_strtoul(buf, 10, &state); + retval = kstrtoul(buf, 10, &state); if (retval) return retval; @@ -75,6 +75,8 @@ static ssize_t arvo_sysfs_set_mode_key(struct device *dev, return size; } +static DEVICE_ATTR(mode_key, 0660, + arvo_sysfs_show_mode_key, arvo_sysfs_set_mode_key); static ssize_t arvo_sysfs_show_key_mask(struct device *dev, struct device_attribute *attr, char *buf) @@ -107,7 +109,7 @@ static ssize_t arvo_sysfs_set_key_mask(struct device *dev, unsigned long key_mask; int retval; - retval = strict_strtoul(buf, 10, &key_mask); + retval = kstrtoul(buf, 10, &key_mask); if (retval) return retval; @@ -123,6 +125,8 @@ static ssize_t arvo_sysfs_set_key_mask(struct device *dev, return size; } +static DEVICE_ATTR(key_mask, 0660, + arvo_sysfs_show_key_mask, arvo_sysfs_set_key_mask); /* retval is 1-5 on success, < 0 on error */ static int arvo_get_actual_profile(struct usb_device *usb_dev) @@ -159,7 +163,7 @@ static ssize_t arvo_sysfs_set_actual_profile(struct device *dev, unsigned long profile; int retval; - retval = strict_strtoul(buf, 10, &profile); + retval = kstrtoul(buf, 10, &profile); if (retval) return retval; @@ -179,6 +183,9 @@ static ssize_t arvo_sysfs_set_actual_profile(struct device *dev, mutex_unlock(&arvo->arvo_lock); return retval; } +static DEVICE_ATTR(actual_profile, 0660, + arvo_sysfs_show_actual_profile, + arvo_sysfs_set_actual_profile); static ssize_t arvo_sysfs_write(struct file *fp, struct kobject *kobj, void const *buf, @@ -230,6 +237,8 @@ static ssize_t arvo_sysfs_write_button(struct file *fp, return arvo_sysfs_write(fp, kobj, buf, off, count, sizeof(struct arvo_button), ARVO_COMMAND_BUTTON); } +static BIN_ATTR(button, 0220, NULL, arvo_sysfs_write_button, + sizeof(struct arvo_button)); static ssize_t arvo_sysfs_read_info(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, @@ -238,31 +247,30 @@ static ssize_t arvo_sysfs_read_info(struct file *fp, return arvo_sysfs_read(fp, kobj, buf, off, count, sizeof(struct arvo_info), ARVO_COMMAND_INFO); } +static BIN_ATTR(info, 0440, arvo_sysfs_read_info, NULL, + sizeof(struct arvo_info)); + +static struct attribute *arvo_attrs[] = { + &dev_attr_mode_key.attr, + &dev_attr_key_mask.attr, + &dev_attr_actual_profile.attr, + NULL, +}; +static struct bin_attribute *arvo_bin_attributes[] = { + &bin_attr_button, + &bin_attr_info, + NULL, +}; -static struct device_attribute arvo_attributes[] = { - __ATTR(mode_key, 0660, - arvo_sysfs_show_mode_key, arvo_sysfs_set_mode_key), - __ATTR(key_mask, 0660, - arvo_sysfs_show_key_mask, arvo_sysfs_set_key_mask), - __ATTR(actual_profile, 0660, - arvo_sysfs_show_actual_profile, - arvo_sysfs_set_actual_profile), - __ATTR_NULL +static const struct attribute_group arvo_group = { + .attrs = arvo_attrs, + .bin_attrs = arvo_bin_attributes, }; -static struct bin_attribute arvo_bin_attributes[] = { - { - .attr = { .name = "button", .mode = 0220 }, - .size = sizeof(struct arvo_button), - .write = arvo_sysfs_write_button - }, - { - .attr = { .name = "info", .mode = 0440 }, - .size = sizeof(struct arvo_info), - .read = arvo_sysfs_read_info - }, - __ATTR_NULL +static const struct attribute_group *arvo_groups[] = { + &arvo_group, + NULL, }; static int arvo_init_arvo_device_struct(struct usb_device *usb_dev, @@ -430,8 +438,7 @@ static int __init arvo_init(void) arvo_class = class_create(THIS_MODULE, "arvo"); if (IS_ERR(arvo_class)) return PTR_ERR(arvo_class); - arvo_class->dev_attrs = arvo_attributes; - arvo_class->dev_bin_attrs = arvo_bin_attributes; + arvo_class->dev_groups = arvo_groups; retval = hid_register_driver(&arvo_driver); if (retval) diff --git a/drivers/hid/hid-roccat-common.c b/drivers/hid/hid-roccat-common.c index 74f70403262..02e28e9f4ea 100644 --- a/drivers/hid/hid-roccat-common.c +++ b/drivers/hid/hid-roccat-common.c @@ -65,10 +65,11 @@ int roccat_common2_send(struct usb_device *usb_dev, uint report_id, EXPORT_SYMBOL_GPL(roccat_common2_send); enum roccat_common2_control_states { - ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD = 0, + ROCCAT_COMMON_CONTROL_STATUS_CRITICAL = 0, ROCCAT_COMMON_CONTROL_STATUS_OK = 1, ROCCAT_COMMON_CONTROL_STATUS_INVALID = 2, - ROCCAT_COMMON_CONTROL_STATUS_WAIT = 3, + ROCCAT_COMMON_CONTROL_STATUS_BUSY = 3, + ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW = 4, }; static int roccat_common2_receive_control_status(struct usb_device *usb_dev) @@ -88,13 +89,12 @@ static int roccat_common2_receive_control_status(struct usb_device *usb_dev) switch (control.value) { case ROCCAT_COMMON_CONTROL_STATUS_OK: return 0; - case ROCCAT_COMMON_CONTROL_STATUS_WAIT: + case ROCCAT_COMMON_CONTROL_STATUS_BUSY: msleep(500); continue; case ROCCAT_COMMON_CONTROL_STATUS_INVALID: - - case ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD: - /* seems to be critical - replug necessary */ + case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL: + case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW: return -EINVAL; default: dev_err(&usb_dev->dev, @@ -122,6 +122,59 @@ int roccat_common2_send_with_status(struct usb_device *usb_dev, } EXPORT_SYMBOL_GPL(roccat_common2_send_with_status); +int roccat_common2_device_init_struct(struct usb_device *usb_dev, + struct roccat_common2_device *dev) +{ + mutex_init(&dev->lock); + return 0; +} +EXPORT_SYMBOL_GPL(roccat_common2_device_init_struct); + +ssize_t roccat_common2_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off >= real_size) + return 0; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&roccat_dev->lock); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); + mutex_unlock(&roccat_dev->lock); + + return retval ? retval : real_size; +} +EXPORT_SYMBOL_GPL(roccat_common2_sysfs_read); + +ssize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj, + void const *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&roccat_dev->lock); + retval = roccat_common2_send_with_status(usb_dev, command, buf, real_size); + mutex_unlock(&roccat_dev->lock); + + return retval ? retval : real_size; +} +EXPORT_SYMBOL_GPL(roccat_common2_sysfs_write); + MODULE_AUTHOR("Stefan Achatz"); MODULE_DESCRIPTION("USB Roccat common driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-common.h b/drivers/hid/hid-roccat-common.h index a97746a63b7..eaa56eb7d5d 100644 --- a/drivers/hid/hid-roccat-common.h +++ b/drivers/hid/hid-roccat-common.h @@ -32,4 +32,66 @@ int roccat_common2_send(struct usb_device *usb_dev, uint report_id, int roccat_common2_send_with_status(struct usb_device *usb_dev, uint command, void const *buf, uint size); +struct roccat_common2_device { + int roccat_claimed; + int chrdev_minor; + struct mutex lock; +}; + +int roccat_common2_device_init_struct(struct usb_device *usb_dev, + struct roccat_common2_device *dev); +ssize_t roccat_common2_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command); +ssize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj, + void const *buf, loff_t off, size_t count, + size_t real_size, uint command); + +#define ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE) \ +static ssize_t roccat_common2_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return roccat_common2_sysfs_write(fp, kobj, buf, off, count, \ + SIZE, COMMAND); \ +} + +#define ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE) \ +static ssize_t roccat_common2_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return roccat_common2_sysfs_read(fp, kobj, buf, off, count, \ + SIZE, COMMAND); \ +} + +#define ROCCAT_COMMON2_SYSFS_RW(thingy, COMMAND, SIZE) \ +ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE) \ +ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE) + +#define ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(thingy, COMMAND, SIZE) \ +ROCCAT_COMMON2_SYSFS_RW(thingy, COMMAND, SIZE); \ +static struct bin_attribute bin_attr_ ## thingy = { \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = SIZE, \ + .read = roccat_common2_sysfs_read_ ## thingy, \ + .write = roccat_common2_sysfs_write_ ## thingy \ +} + +#define ROCCAT_COMMON2_BIN_ATTRIBUTE_R(thingy, COMMAND, SIZE) \ +ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE); \ +static struct bin_attribute bin_attr_ ## thingy = { \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = SIZE, \ + .read = roccat_common2_sysfs_read_ ## thingy, \ +} + +#define ROCCAT_COMMON2_BIN_ATTRIBUTE_W(thingy, COMMAND, SIZE) \ +ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE); \ +static struct bin_attribute bin_attr_ ## thingy = { \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = SIZE, \ + .write = roccat_common2_sysfs_write_ ## thingy \ +} + #endif diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 1219998a02d..bc62ed91e45 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -82,7 +82,7 @@ static ssize_t isku_sysfs_set_actual_profile(struct device *dev, isku = hid_get_drvdata(dev_get_drvdata(dev)); usb_dev = interface_to_usbdev(to_usb_interface(dev)); - retval = strict_strtoul(buf, 10, &profile); + retval = kstrtoul(buf, 10, &profile); if (retval) return retval; @@ -109,12 +109,12 @@ static ssize_t isku_sysfs_set_actual_profile(struct device *dev, return size; } +static DEVICE_ATTR(actual_profile, 0660, isku_sysfs_show_actual_profile, + isku_sysfs_set_actual_profile); -static struct device_attribute isku_attributes[] = { - __ATTR(actual_profile, 0660, - isku_sysfs_show_actual_profile, - isku_sysfs_set_actual_profile), - __ATTR_NULL +static struct attribute *isku_attrs[] = { + &dev_attr_actual_profile.attr, + NULL, }; static ssize_t isku_sysfs_read(struct file *fp, struct kobject *kobj, @@ -130,14 +130,14 @@ static ssize_t isku_sysfs_read(struct file *fp, struct kobject *kobj, if (off >= real_size) return 0; - if (off != 0 || count != real_size) + if (off != 0 || count > real_size) return -EINVAL; mutex_lock(&isku->isku_lock); - retval = isku_receive(usb_dev, command, buf, real_size); + retval = isku_receive(usb_dev, command, buf, count); mutex_unlock(&isku->isku_lock); - return retval ? retval : real_size; + return retval ? retval : count; } static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj, @@ -150,15 +150,15 @@ static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj, struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); int retval; - if (off != 0 || count != real_size) + if (off != 0 || count > real_size) return -EINVAL; mutex_lock(&isku->isku_lock); retval = roccat_common2_send_with_status(usb_dev, command, - (void *)buf, real_size); + (void *)buf, count); mutex_unlock(&isku->isku_lock); - return retval ? retval : real_size; + return retval ? retval : count; } #define ISKU_SYSFS_W(thingy, THINGY) \ @@ -184,7 +184,8 @@ ISKU_SYSFS_R(thingy, THINGY) \ ISKU_SYSFS_W(thingy, THINGY) #define ISKU_BIN_ATTR_RW(thingy, THINGY) \ -{ \ +ISKU_SYSFS_RW(thingy, THINGY); \ +static struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = ISKU_SIZE_ ## THINGY, \ .read = isku_sysfs_read_ ## thingy, \ @@ -192,50 +193,64 @@ ISKU_SYSFS_W(thingy, THINGY) } #define ISKU_BIN_ATTR_R(thingy, THINGY) \ -{ \ +ISKU_SYSFS_R(thingy, THINGY); \ +static struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0440 }, \ .size = ISKU_SIZE_ ## THINGY, \ .read = isku_sysfs_read_ ## thingy, \ } #define ISKU_BIN_ATTR_W(thingy, THINGY) \ -{ \ +ISKU_SYSFS_W(thingy, THINGY); \ +static struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = ISKU_SIZE_ ## THINGY, \ .write = isku_sysfs_write_ ## thingy \ } -ISKU_SYSFS_RW(macro, MACRO) -ISKU_SYSFS_RW(keys_function, KEYS_FUNCTION) -ISKU_SYSFS_RW(keys_easyzone, KEYS_EASYZONE) -ISKU_SYSFS_RW(keys_media, KEYS_MEDIA) -ISKU_SYSFS_RW(keys_thumbster, KEYS_THUMBSTER) -ISKU_SYSFS_RW(keys_macro, KEYS_MACRO) -ISKU_SYSFS_RW(keys_capslock, KEYS_CAPSLOCK) -ISKU_SYSFS_RW(light, LIGHT) -ISKU_SYSFS_RW(key_mask, KEY_MASK) -ISKU_SYSFS_RW(last_set, LAST_SET) -ISKU_SYSFS_W(talk, TALK) -ISKU_SYSFS_R(info, INFO) -ISKU_SYSFS_W(control, CONTROL) -ISKU_SYSFS_W(reset, RESET) - -static struct bin_attribute isku_bin_attributes[] = { - ISKU_BIN_ATTR_RW(macro, MACRO), - ISKU_BIN_ATTR_RW(keys_function, KEYS_FUNCTION), - ISKU_BIN_ATTR_RW(keys_easyzone, KEYS_EASYZONE), - ISKU_BIN_ATTR_RW(keys_media, KEYS_MEDIA), - ISKU_BIN_ATTR_RW(keys_thumbster, KEYS_THUMBSTER), - ISKU_BIN_ATTR_RW(keys_macro, KEYS_MACRO), - ISKU_BIN_ATTR_RW(keys_capslock, KEYS_CAPSLOCK), - ISKU_BIN_ATTR_RW(light, LIGHT), - ISKU_BIN_ATTR_RW(key_mask, KEY_MASK), - ISKU_BIN_ATTR_RW(last_set, LAST_SET), - ISKU_BIN_ATTR_W(talk, TALK), - ISKU_BIN_ATTR_R(info, INFO), - ISKU_BIN_ATTR_W(control, CONTROL), - ISKU_BIN_ATTR_W(reset, RESET), - __ATTR_NULL +ISKU_BIN_ATTR_RW(macro, MACRO); +ISKU_BIN_ATTR_RW(keys_function, KEYS_FUNCTION); +ISKU_BIN_ATTR_RW(keys_easyzone, KEYS_EASYZONE); +ISKU_BIN_ATTR_RW(keys_media, KEYS_MEDIA); +ISKU_BIN_ATTR_RW(keys_thumbster, KEYS_THUMBSTER); +ISKU_BIN_ATTR_RW(keys_macro, KEYS_MACRO); +ISKU_BIN_ATTR_RW(keys_capslock, KEYS_CAPSLOCK); +ISKU_BIN_ATTR_RW(light, LIGHT); +ISKU_BIN_ATTR_RW(key_mask, KEY_MASK); +ISKU_BIN_ATTR_RW(last_set, LAST_SET); +ISKU_BIN_ATTR_W(talk, TALK); +ISKU_BIN_ATTR_W(talkfx, TALKFX); +ISKU_BIN_ATTR_W(control, CONTROL); +ISKU_BIN_ATTR_W(reset, RESET); +ISKU_BIN_ATTR_R(info, INFO); + +static struct bin_attribute *isku_bin_attributes[] = { + &bin_attr_macro, + &bin_attr_keys_function, + &bin_attr_keys_easyzone, + &bin_attr_keys_media, + &bin_attr_keys_thumbster, + &bin_attr_keys_macro, + &bin_attr_keys_capslock, + &bin_attr_light, + &bin_attr_key_mask, + &bin_attr_last_set, + &bin_attr_talk, + &bin_attr_talkfx, + &bin_attr_control, + &bin_attr_reset, + &bin_attr_info, + NULL, +}; + +static const struct attribute_group isku_group = { + .attrs = isku_attrs, + .bin_attrs = isku_bin_attributes, +}; + +static const struct attribute_group *isku_groups[] = { + &isku_group, + NULL, }; static int isku_init_isku_device_struct(struct usb_device *usb_dev, @@ -405,6 +420,7 @@ static int isku_raw_event(struct hid_device *hdev, static const struct hid_device_id isku_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKUFX) }, { } }; @@ -424,8 +440,7 @@ static int __init isku_init(void) isku_class = class_create(THIS_MODULE, "isku"); if (IS_ERR(isku_class)) return PTR_ERR(isku_class); - isku_class->dev_attrs = isku_attributes; - isku_class->dev_bin_attrs = isku_bin_attributes; + isku_class->dev_groups = isku_groups; retval = hid_register_driver(&isku_driver); if (retval) @@ -443,5 +458,5 @@ module_init(isku_init); module_exit(isku_exit); MODULE_AUTHOR("Stefan Achatz"); -MODULE_DESCRIPTION("USB Roccat Isku driver"); +MODULE_DESCRIPTION("USB Roccat Isku/FX driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-isku.h b/drivers/hid/hid-roccat-isku.h index cf6896c8386..53056860d4d 100644 --- a/drivers/hid/hid-roccat-isku.h +++ b/drivers/hid/hid-roccat-isku.h @@ -25,10 +25,11 @@ enum { ISKU_SIZE_KEYS_MACRO = 0x23, ISKU_SIZE_KEYS_CAPSLOCK = 0x06, ISKU_SIZE_LAST_SET = 0x14, - ISKU_SIZE_LIGHT = 0x0a, + ISKU_SIZE_LIGHT = 0x10, ISKU_SIZE_MACRO = 0x823, ISKU_SIZE_RESET = 0x03, ISKU_SIZE_TALK = 0x10, + ISKU_SIZE_TALKFX = 0x10, }; enum { @@ -59,6 +60,7 @@ enum isku_commands { ISKU_COMMAND_LAST_SET = 0x14, ISKU_COMMAND_15 = 0x15, ISKU_COMMAND_TALK = 0x16, + ISKU_COMMAND_TALKFX = 0x17, ISKU_COMMAND_FIRMWARE_WRITE = 0x1b, ISKU_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c, }; diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 9ce2d0b615a..6101816a7dd 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -324,6 +324,8 @@ static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj, return sizeof(struct kone_settings); } +static BIN_ATTR(settings, 0660, kone_sysfs_read_settings, + kone_sysfs_write_settings, sizeof(struct kone_settings)); static ssize_t kone_sysfs_read_profilex(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, @@ -378,6 +380,19 @@ static ssize_t kone_sysfs_write_profilex(struct file *fp, return sizeof(struct kone_profile); } +#define PROFILE_ATTR(number) \ +static struct bin_attribute bin_attr_profile##number = { \ + .attr = { .name = "profile" #number, .mode = 0660 }, \ + .size = sizeof(struct kone_profile), \ + .read = kone_sysfs_read_profilex, \ + .write = kone_sysfs_write_profilex, \ + .private = &profile_numbers[number-1], \ +}; +PROFILE_ATTR(1); +PROFILE_ATTR(2); +PROFILE_ATTR(3); +PROFILE_ATTR(4); +PROFILE_ATTR(5); static ssize_t kone_sysfs_show_actual_profile(struct device *dev, struct device_attribute *attr, char *buf) @@ -386,6 +401,7 @@ static ssize_t kone_sysfs_show_actual_profile(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); return snprintf(buf, PAGE_SIZE, "%d\n", kone->actual_profile); } +static DEVICE_ATTR(actual_profile, 0440, kone_sysfs_show_actual_profile, NULL); static ssize_t kone_sysfs_show_actual_dpi(struct device *dev, struct device_attribute *attr, char *buf) @@ -394,6 +410,7 @@ static ssize_t kone_sysfs_show_actual_dpi(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); return snprintf(buf, PAGE_SIZE, "%d\n", kone->actual_dpi); } +static DEVICE_ATTR(actual_dpi, 0440, kone_sysfs_show_actual_dpi, NULL); /* weight is read each time, since we don't get informed when it's changed */ static ssize_t kone_sysfs_show_weight(struct device *dev, @@ -416,6 +433,7 @@ static ssize_t kone_sysfs_show_weight(struct device *dev, return retval; return snprintf(buf, PAGE_SIZE, "%d\n", weight); } +static DEVICE_ATTR(weight, 0440, kone_sysfs_show_weight, NULL); static ssize_t kone_sysfs_show_firmware_version(struct device *dev, struct device_attribute *attr, char *buf) @@ -424,6 +442,8 @@ static ssize_t kone_sysfs_show_firmware_version(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); return snprintf(buf, PAGE_SIZE, "%d\n", kone->firmware_version); } +static DEVICE_ATTR(firmware_version, 0440, kone_sysfs_show_firmware_version, + NULL); static ssize_t kone_sysfs_show_tcu(struct device *dev, struct device_attribute *attr, char *buf) @@ -456,7 +476,7 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev, kone = hid_get_drvdata(dev_get_drvdata(dev)); usb_dev = interface_to_usbdev(to_usb_interface(dev)); - retval = strict_strtoul(buf, 10, &state); + retval = kstrtoul(buf, 10, &state); if (retval) return retval; @@ -524,6 +544,7 @@ exit_unlock: mutex_unlock(&kone->kone_lock); return retval; } +static DEVICE_ATTR(tcu, 0660, kone_sysfs_show_tcu, kone_sysfs_set_tcu); static ssize_t kone_sysfs_show_startup_profile(struct device *dev, struct device_attribute *attr, char *buf) @@ -545,7 +566,7 @@ static ssize_t kone_sysfs_set_startup_profile(struct device *dev, kone = hid_get_drvdata(dev_get_drvdata(dev)); usb_dev = interface_to_usbdev(to_usb_interface(dev)); - retval = strict_strtoul(buf, 10, &new_startup_profile); + retval = kstrtoul(buf, 10, &new_startup_profile); if (retval) return retval; @@ -570,15 +591,17 @@ static ssize_t kone_sysfs_set_startup_profile(struct device *dev, mutex_unlock(&kone->kone_lock); return size; } +static DEVICE_ATTR(startup_profile, 0660, kone_sysfs_show_startup_profile, + kone_sysfs_set_startup_profile); -static struct device_attribute kone_attributes[] = { +static struct attribute *kone_attrs[] = { /* * Read actual dpi settings. * Returns raw value for further processing. Refer to enum * kone_polling_rates to get real value. */ - __ATTR(actual_dpi, 0440, kone_sysfs_show_actual_dpi, NULL), - __ATTR(actual_profile, 0440, kone_sysfs_show_actual_profile, NULL), + &dev_attr_actual_dpi.attr, + &dev_attr_actual_profile.attr, /* * The mouse can be equipped with one of four supplied weights from 5 @@ -587,7 +610,7 @@ static struct device_attribute kone_attributes[] = { * by software. Refer to enum kone_weights to get corresponding real * weight. */ - __ATTR(weight, 0440, kone_sysfs_show_weight, NULL), + &dev_attr_weight.attr, /* * Prints firmware version stored in mouse as integer. @@ -595,66 +618,38 @@ static struct device_attribute kone_attributes[] = { * to get the real version number the decimal point has to be shifted 2 * positions to the left. E.g. a value of 138 means 1.38. */ - __ATTR(firmware_version, 0440, - kone_sysfs_show_firmware_version, NULL), + &dev_attr_firmware_version.attr, /* * Prints state of Tracking Control Unit as number where 0 = off and * 1 = on. Writing 0 deactivates tcu and writing 1 calibrates and * activates the tcu */ - __ATTR(tcu, 0660, kone_sysfs_show_tcu, kone_sysfs_set_tcu), + &dev_attr_tcu.attr, /* Prints and takes the number of the profile the mouse starts with */ - __ATTR(startup_profile, 0660, - kone_sysfs_show_startup_profile, - kone_sysfs_set_startup_profile), - __ATTR_NULL + &dev_attr_startup_profile.attr, + NULL, +}; + +static struct bin_attribute *kone_bin_attributes[] = { + &bin_attr_settings, + &bin_attr_profile1, + &bin_attr_profile2, + &bin_attr_profile3, + &bin_attr_profile4, + &bin_attr_profile5, + NULL, +}; + +static const struct attribute_group kone_group = { + .attrs = kone_attrs, + .bin_attrs = kone_bin_attributes, }; -static struct bin_attribute kone_bin_attributes[] = { - { - .attr = { .name = "settings", .mode = 0660 }, - .size = sizeof(struct kone_settings), - .read = kone_sysfs_read_settings, - .write = kone_sysfs_write_settings - }, - { - .attr = { .name = "profile1", .mode = 0660 }, - .size = sizeof(struct kone_profile), - .read = kone_sysfs_read_profilex, - .write = kone_sysfs_write_profilex, - .private = &profile_numbers[0] - }, - { - .attr = { .name = "profile2", .mode = 0660 }, - .size = sizeof(struct kone_profile), - .read = kone_sysfs_read_profilex, - .write = kone_sysfs_write_profilex, - .private = &profile_numbers[1] - }, - { - .attr = { .name = "profile3", .mode = 0660 }, - .size = sizeof(struct kone_profile), - .read = kone_sysfs_read_profilex, - .write = kone_sysfs_write_profilex, - .private = &profile_numbers[2] - }, - { - .attr = { .name = "profile4", .mode = 0660 }, - .size = sizeof(struct kone_profile), - .read = kone_sysfs_read_profilex, - .write = kone_sysfs_write_profilex, - .private = &profile_numbers[3] - }, - { - .attr = { .name = "profile5", .mode = 0660 }, - .size = sizeof(struct kone_profile), - .read = kone_sysfs_read_profilex, - .write = kone_sysfs_write_profilex, - .private = &profile_numbers[4] - }, - __ATTR_NULL +static const struct attribute_group *kone_groups[] = { + &kone_group, + NULL, }; static int kone_init_kone_device_struct(struct usb_device *usb_dev, @@ -818,8 +813,9 @@ static void kone_report_to_chrdev(struct kone_device const *kone, (uint8_t *)&roccat_report); break; case kone_mouse_event_call_overlong_macro: + case kone_mouse_event_multimedia: if (event->value == kone_keystroke_action_press) { - roccat_report.event = kone_mouse_event_call_overlong_macro; + roccat_report.event = event->event; roccat_report.value = kone->actual_profile; roccat_report.key = event->macro_key; roccat_report_event(kone->chrdev_minor, @@ -890,8 +886,7 @@ static int __init kone_init(void) kone_class = class_create(THIS_MODULE, "kone"); if (IS_ERR(kone_class)) return PTR_ERR(kone_class); - kone_class->dev_attrs = kone_attributes; - kone_class->dev_bin_attrs = kone_bin_attributes; + kone_class->dev_groups = kone_groups; retval = hid_register_driver(&kone_driver); if (retval) diff --git a/drivers/hid/hid-roccat-kone.h b/drivers/hid/hid-roccat-kone.h index 64abb5b8a59..52c6167d023 100644 --- a/drivers/hid/hid-roccat-kone.h +++ b/drivers/hid/hid-roccat-kone.h @@ -169,6 +169,7 @@ enum kone_mouse_events { /* TODO clarify meaning and occurence of kone_mouse_event_calibration */ kone_mouse_event_calibration = 0xc0, kone_mouse_event_call_overlong_macro = 0xe0, + kone_mouse_event_multimedia = 0xe1, /* switch events notify if user changed values with mousebutton click */ kone_mouse_event_switch_dpi = 0xf0, kone_mouse_event_switch_profile = 0xf1 diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 6a48fa3c7da..5e99fcdc71b 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -156,7 +156,8 @@ KONEPLUS_SYSFS_W(thingy, THINGY) \ KONEPLUS_SYSFS_R(thingy, THINGY) #define KONEPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \ -{ \ +KONEPLUS_SYSFS_RW(thingy, THINGY); \ +static struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = KONEPLUS_SIZE_ ## THINGY, \ .read = koneplus_sysfs_read_ ## thingy, \ @@ -164,28 +165,29 @@ KONEPLUS_SYSFS_R(thingy, THINGY) } #define KONEPLUS_BIN_ATTRIBUTE_R(thingy, THINGY) \ -{ \ +KONEPLUS_SYSFS_R(thingy, THINGY); \ +static struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0440 }, \ .size = KONEPLUS_SIZE_ ## THINGY, \ .read = koneplus_sysfs_read_ ## thingy, \ } #define KONEPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \ -{ \ +KONEPLUS_SYSFS_W(thingy, THINGY); \ +static struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = KONEPLUS_SIZE_ ## THINGY, \ .write = koneplus_sysfs_write_ ## thingy \ } - -KONEPLUS_SYSFS_W(control, CONTROL) -KONEPLUS_SYSFS_RW(info, INFO) -KONEPLUS_SYSFS_W(talk, TALK) -KONEPLUS_SYSFS_W(macro, MACRO) -KONEPLUS_SYSFS_RW(sensor, SENSOR) -KONEPLUS_SYSFS_RW(tcu, TCU) -KONEPLUS_SYSFS_R(tcu_image, TCU_IMAGE) -KONEPLUS_SYSFS_RW(profile_settings, PROFILE_SETTINGS) -KONEPLUS_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) +KONEPLUS_BIN_ATTRIBUTE_W(control, CONTROL); +KONEPLUS_BIN_ATTRIBUTE_W(talk, TALK); +KONEPLUS_BIN_ATTRIBUTE_W(macro, MACRO); +KONEPLUS_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE); +KONEPLUS_BIN_ATTRIBUTE_RW(info, INFO); +KONEPLUS_BIN_ATTRIBUTE_RW(sensor, SENSOR); +KONEPLUS_BIN_ATTRIBUTE_RW(tcu, TCU); +KONEPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS); +KONEPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS); static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, @@ -225,6 +227,25 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp, KONEPLUS_COMMAND_PROFILE_BUTTONS); } +#define PROFILE_ATTR(number) \ +static struct bin_attribute bin_attr_profile##number##_settings = { \ + .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ + .size = KONEPLUS_SIZE_PROFILE_SETTINGS, \ + .read = koneplus_sysfs_read_profilex_settings, \ + .private = &profile_numbers[number-1], \ +}; \ +static struct bin_attribute bin_attr_profile##number##_buttons = { \ + .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ + .size = KONEPLUS_SIZE_PROFILE_BUTTONS, \ + .read = koneplus_sysfs_read_profilex_buttons, \ + .private = &profile_numbers[number-1], \ +}; +PROFILE_ATTR(1); +PROFILE_ATTR(2); +PROFILE_ATTR(3); +PROFILE_ATTR(4); +PROFILE_ATTR(5); + static ssize_t koneplus_sysfs_show_actual_profile(struct device *dev, struct device_attribute *attr, char *buf) { @@ -246,7 +267,7 @@ static ssize_t koneplus_sysfs_set_actual_profile(struct device *dev, koneplus = hid_get_drvdata(dev_get_drvdata(dev)); usb_dev = interface_to_usbdev(to_usb_interface(dev)); - retval = strict_strtoul(buf, 10, &profile); + retval = kstrtoul(buf, 10, &profile); if (retval) return retval; @@ -274,6 +295,12 @@ static ssize_t koneplus_sysfs_set_actual_profile(struct device *dev, return size; } +static DEVICE_ATTR(actual_profile, 0660, + koneplus_sysfs_show_actual_profile, + koneplus_sysfs_set_actual_profile); +static DEVICE_ATTR(startup_profile, 0660, + koneplus_sysfs_show_actual_profile, + koneplus_sysfs_set_actual_profile); static ssize_t koneplus_sysfs_show_firmware_version(struct device *dev, struct device_attribute *attr, char *buf) @@ -293,90 +320,47 @@ static ssize_t koneplus_sysfs_show_firmware_version(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); } +static DEVICE_ATTR(firmware_version, 0440, + koneplus_sysfs_show_firmware_version, NULL); + +static struct attribute *koneplus_attrs[] = { + &dev_attr_actual_profile.attr, + &dev_attr_startup_profile.attr, + &dev_attr_firmware_version.attr, + NULL, +}; + +static struct bin_attribute *koneplus_bin_attributes[] = { + &bin_attr_control, + &bin_attr_talk, + &bin_attr_macro, + &bin_attr_tcu_image, + &bin_attr_info, + &bin_attr_sensor, + &bin_attr_tcu, + &bin_attr_profile_settings, + &bin_attr_profile_buttons, + &bin_attr_profile1_settings, + &bin_attr_profile2_settings, + &bin_attr_profile3_settings, + &bin_attr_profile4_settings, + &bin_attr_profile5_settings, + &bin_attr_profile1_buttons, + &bin_attr_profile2_buttons, + &bin_attr_profile3_buttons, + &bin_attr_profile4_buttons, + &bin_attr_profile5_buttons, + NULL, +}; -static struct device_attribute koneplus_attributes[] = { - __ATTR(actual_profile, 0660, - koneplus_sysfs_show_actual_profile, - koneplus_sysfs_set_actual_profile), - __ATTR(startup_profile, 0660, - koneplus_sysfs_show_actual_profile, - koneplus_sysfs_set_actual_profile), - __ATTR(firmware_version, 0440, - koneplus_sysfs_show_firmware_version, NULL), - __ATTR_NULL +static const struct attribute_group koneplus_group = { + .attrs = koneplus_attrs, + .bin_attrs = koneplus_bin_attributes, }; -static struct bin_attribute koneplus_bin_attributes[] = { - KONEPLUS_BIN_ATTRIBUTE_W(control, CONTROL), - KONEPLUS_BIN_ATTRIBUTE_RW(info, INFO), - KONEPLUS_BIN_ATTRIBUTE_W(talk, TALK), - KONEPLUS_BIN_ATTRIBUTE_W(macro, MACRO), - KONEPLUS_BIN_ATTRIBUTE_RW(sensor, SENSOR), - KONEPLUS_BIN_ATTRIBUTE_RW(tcu, TCU), - KONEPLUS_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE), - KONEPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), - KONEPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), - { - .attr = { .name = "profile1_settings", .mode = 0440 }, - .size = KONEPLUS_SIZE_PROFILE_SETTINGS, - .read = koneplus_sysfs_read_profilex_settings, - .private = &profile_numbers[0] - }, - { - .attr = { .name = "profile2_settings", .mode = 0440 }, - .size = KONEPLUS_SIZE_PROFILE_SETTINGS, - .read = koneplus_sysfs_read_profilex_settings, - .private = &profile_numbers[1] - }, - { - .attr = { .name = "profile3_settings", .mode = 0440 }, - .size = KONEPLUS_SIZE_PROFILE_SETTINGS, - .read = koneplus_sysfs_read_profilex_settings, - .private = &profile_numbers[2] - }, - { - .attr = { .name = "profile4_settings", .mode = 0440 }, - .size = KONEPLUS_SIZE_PROFILE_SETTINGS, - .read = koneplus_sysfs_read_profilex_settings, - .private = &profile_numbers[3] - }, - { - .attr = { .name = "profile5_settings", .mode = 0440 }, - .size = KONEPLUS_SIZE_PROFILE_SETTINGS, - .read = koneplus_sysfs_read_profilex_settings, - .private = &profile_numbers[4] - }, - { - .attr = { .name = "profile1_buttons", .mode = 0440 }, - .size = KONEPLUS_SIZE_PROFILE_BUTTONS, - .read = koneplus_sysfs_read_profilex_buttons, - .private = &profile_numbers[0] - }, - { - .attr = { .name = "profile2_buttons", .mode = 0440 }, - .size = KONEPLUS_SIZE_PROFILE_BUTTONS, - .read = koneplus_sysfs_read_profilex_buttons, - .private = &profile_numbers[1] - }, - { - .attr = { .name = "profile3_buttons", .mode = 0440 }, - .size = KONEPLUS_SIZE_PROFILE_BUTTONS, - .read = koneplus_sysfs_read_profilex_buttons, - .private = &profile_numbers[2] - }, - { - .attr = { .name = "profile4_buttons", .mode = 0440 }, - .size = KONEPLUS_SIZE_PROFILE_BUTTONS, - .read = koneplus_sysfs_read_profilex_buttons, - .private = &profile_numbers[3] - }, - { - .attr = { .name = "profile5_buttons", .mode = 0440 }, - .size = KONEPLUS_SIZE_PROFILE_BUTTONS, - .read = koneplus_sysfs_read_profilex_buttons, - .private = &profile_numbers[4] - }, - __ATTR_NULL +static const struct attribute_group *koneplus_groups[] = { + &koneplus_group, + NULL, }; static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev, @@ -572,8 +556,7 @@ static int __init koneplus_init(void) koneplus_class = class_create(THIS_MODULE, "koneplus"); if (IS_ERR(koneplus_class)) return PTR_ERR(koneplus_class); - koneplus_class->dev_attrs = koneplus_attributes; - koneplus_class->dev_bin_attrs = koneplus_bin_attributes; + koneplus_class->dev_groups = koneplus_groups; retval = hid_register_driver(&koneplus_driver); if (retval) diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c new file mode 100644 index 00000000000..07de2f9014c --- /dev/null +++ b/drivers/hid/hid-roccat-konepure.c @@ -0,0 +1,232 @@ +/* + * Roccat KonePure driver for Linux + * + * Copyright (c) 2012 Stefan Achatz <erazor_de@users.sourceforge.net> + */ + +/* + * 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. + */ + +/* + * Roccat KonePure is a smaller version of KoneXTD with less buttons and lights. + */ + +#include <linux/types.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/hid-roccat.h> +#include "hid-ids.h" +#include "hid-roccat-common.h" + +enum { + KONEPURE_MOUSE_REPORT_NUMBER_BUTTON = 3, +}; + +struct konepure_mouse_report_button { + uint8_t report_number; /* always KONEPURE_MOUSE_REPORT_NUMBER_BUTTON */ + uint8_t zero; + uint8_t type; + uint8_t data1; + uint8_t data2; + uint8_t zero2; + uint8_t unknown[2]; +} __packed; + +static struct class *konepure_class; + +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x04, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(actual_profile, 0x05, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile_settings, 0x06, 0x1f); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile_buttons, 0x07, 0x3b); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(macro, 0x08, 0x0822); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(info, 0x09, 0x06); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(tcu, 0x0c, 0x04); +ROCCAT_COMMON2_BIN_ATTRIBUTE_R(tcu_image, 0x0c, 0x0404); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0x0f, 0x06); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(talk, 0x10, 0x10); + +static struct bin_attribute *konepure_bin_attrs[] = { + &bin_attr_actual_profile, + &bin_attr_control, + &bin_attr_info, + &bin_attr_talk, + &bin_attr_macro, + &bin_attr_sensor, + &bin_attr_tcu, + &bin_attr_tcu_image, + &bin_attr_profile_settings, + &bin_attr_profile_buttons, + NULL, +}; + +static const struct attribute_group konepure_group = { + .bin_attrs = konepure_bin_attrs, +}; + +static const struct attribute_group *konepure_groups[] = { + &konepure_group, + NULL, +}; + +static int konepure_init_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct roccat_common2_device *konepure; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) { + hid_set_drvdata(hdev, NULL); + return 0; + } + + konepure = kzalloc(sizeof(*konepure), GFP_KERNEL); + if (!konepure) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, konepure); + + retval = roccat_common2_device_init_struct(usb_dev, konepure); + if (retval) { + hid_err(hdev, "couldn't init KonePure device\n"); + goto exit_free; + } + + retval = roccat_connect(konepure_class, hdev, + sizeof(struct konepure_mouse_report_button)); + if (retval < 0) { + hid_err(hdev, "couldn't init char dev\n"); + } else { + konepure->chrdev_minor = retval; + konepure->roccat_claimed = 1; + } + + return 0; +exit_free: + kfree(konepure); + return retval; +} + +static void konepure_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct roccat_common2_device *konepure; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return; + + konepure = hid_get_drvdata(hdev); + if (konepure->roccat_claimed) + roccat_disconnect(konepure->chrdev_minor); + kfree(konepure); +} + +static int konepure_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + hid_err(hdev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + hid_err(hdev, "hw start failed\n"); + goto exit; + } + + retval = konepure_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install mouse\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void konepure_remove(struct hid_device *hdev) +{ + konepure_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static int konepure_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct roccat_common2_device *konepure = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return 0; + + if (data[0] != KONEPURE_MOUSE_REPORT_NUMBER_BUTTON) + return 0; + + if (konepure != NULL && konepure->roccat_claimed) + roccat_report_event(konepure->chrdev_minor, data); + + return 0; +} + +static const struct hid_device_id konepure_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, konepure_devices); + +static struct hid_driver konepure_driver = { + .name = "konepure", + .id_table = konepure_devices, + .probe = konepure_probe, + .remove = konepure_remove, + .raw_event = konepure_raw_event +}; + +static int __init konepure_init(void) +{ + int retval; + + konepure_class = class_create(THIS_MODULE, "konepure"); + if (IS_ERR(konepure_class)) + return PTR_ERR(konepure_class); + konepure_class->dev_groups = konepure_groups; + + retval = hid_register_driver(&konepure_driver); + if (retval) + class_destroy(konepure_class); + return retval; +} + +static void __exit konepure_exit(void) +{ + hid_unregister_driver(&konepure_driver); + class_destroy(konepure_class); +} + +module_init(konepure_init); +module_exit(konepure_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat KonePure/Optical driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index b8b37789b86..966047711fb 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -197,31 +197,25 @@ KOVAPLUS_SYSFS_W(thingy, THINGY) \ KOVAPLUS_SYSFS_R(thingy, THINGY) #define KOVAPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \ -{ \ +KOVAPLUS_SYSFS_RW(thingy, THINGY); \ +static struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = KOVAPLUS_SIZE_ ## THINGY, \ .read = kovaplus_sysfs_read_ ## thingy, \ .write = kovaplus_sysfs_write_ ## thingy \ } -#define KOVAPLUS_BIN_ATTRIBUTE_R(thingy, THINGY) \ -{ \ - .attr = { .name = #thingy, .mode = 0440 }, \ - .size = KOVAPLUS_SIZE_ ## THINGY, \ - .read = kovaplus_sysfs_read_ ## thingy, \ -} - #define KOVAPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \ -{ \ +KOVAPLUS_SYSFS_W(thingy, THINGY); \ +static struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = KOVAPLUS_SIZE_ ## THINGY, \ .write = kovaplus_sysfs_write_ ## thingy \ } - -KOVAPLUS_SYSFS_W(control, CONTROL) -KOVAPLUS_SYSFS_RW(info, INFO) -KOVAPLUS_SYSFS_RW(profile_settings, PROFILE_SETTINGS) -KOVAPLUS_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) +KOVAPLUS_BIN_ATTRIBUTE_W(control, CONTROL); +KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO); +KOVAPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS); +KOVAPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS); static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, @@ -261,6 +255,25 @@ static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp, KOVAPLUS_COMMAND_PROFILE_BUTTONS); } +#define PROFILE_ATTR(number) \ +static struct bin_attribute bin_attr_profile##number##_settings = { \ + .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ + .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, \ + .read = kovaplus_sysfs_read_profilex_settings, \ + .private = &profile_numbers[number-1], \ +}; \ +static struct bin_attribute bin_attr_profile##number##_buttons = { \ + .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ + .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, \ + .read = kovaplus_sysfs_read_profilex_buttons, \ + .private = &profile_numbers[number-1], \ +}; +PROFILE_ATTR(1); +PROFILE_ATTR(2); +PROFILE_ATTR(3); +PROFILE_ATTR(4); +PROFILE_ATTR(5); + static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev, struct device_attribute *attr, char *buf) { @@ -282,7 +295,7 @@ static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev, kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); usb_dev = interface_to_usbdev(to_usb_interface(dev)); - retval = strict_strtoul(buf, 10, &profile); + retval = kstrtoul(buf, 10, &profile); if (retval) return retval; @@ -310,6 +323,9 @@ static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev, return size; } +static DEVICE_ATTR(actual_profile, 0660, + kovaplus_sysfs_show_actual_profile, + kovaplus_sysfs_set_actual_profile); static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev, struct device_attribute *attr, char *buf) @@ -318,6 +334,7 @@ static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi); } +static DEVICE_ATTR(actual_cpi, 0440, kovaplus_sysfs_show_actual_cpi, NULL); static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev, struct device_attribute *attr, char *buf) @@ -326,6 +343,8 @@ static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity); } +static DEVICE_ATTR(actual_sensitivity_x, 0440, + kovaplus_sysfs_show_actual_sensitivity_x, NULL); static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev, struct device_attribute *attr, char *buf) @@ -334,6 +353,8 @@ static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity); } +static DEVICE_ATTR(actual_sensitivity_y, 0440, + kovaplus_sysfs_show_actual_sensitivity_y, NULL); static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev, struct device_attribute *attr, char *buf) @@ -353,88 +374,44 @@ static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); } +static DEVICE_ATTR(firmware_version, 0440, + kovaplus_sysfs_show_firmware_version, NULL); + +static struct attribute *kovaplus_attrs[] = { + &dev_attr_actual_cpi.attr, + &dev_attr_firmware_version.attr, + &dev_attr_actual_profile.attr, + &dev_attr_actual_sensitivity_x.attr, + &dev_attr_actual_sensitivity_y.attr, + NULL, +}; + +static struct bin_attribute *kovaplus_bin_attributes[] = { + &bin_attr_control, + &bin_attr_info, + &bin_attr_profile_settings, + &bin_attr_profile_buttons, + &bin_attr_profile1_settings, + &bin_attr_profile2_settings, + &bin_attr_profile3_settings, + &bin_attr_profile4_settings, + &bin_attr_profile5_settings, + &bin_attr_profile1_buttons, + &bin_attr_profile2_buttons, + &bin_attr_profile3_buttons, + &bin_attr_profile4_buttons, + &bin_attr_profile5_buttons, + NULL, +}; -static struct device_attribute kovaplus_attributes[] = { - __ATTR(actual_cpi, 0440, - kovaplus_sysfs_show_actual_cpi, NULL), - __ATTR(firmware_version, 0440, - kovaplus_sysfs_show_firmware_version, NULL), - __ATTR(actual_profile, 0660, - kovaplus_sysfs_show_actual_profile, - kovaplus_sysfs_set_actual_profile), - __ATTR(actual_sensitivity_x, 0440, - kovaplus_sysfs_show_actual_sensitivity_x, NULL), - __ATTR(actual_sensitivity_y, 0440, - kovaplus_sysfs_show_actual_sensitivity_y, NULL), - __ATTR_NULL +static const struct attribute_group kovaplus_group = { + .attrs = kovaplus_attrs, + .bin_attrs = kovaplus_bin_attributes, }; -static struct bin_attribute kovaplus_bin_attributes[] = { - KOVAPLUS_BIN_ATTRIBUTE_W(control, CONTROL), - KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO), - KOVAPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), - KOVAPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), - { - .attr = { .name = "profile1_settings", .mode = 0440 }, - .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, - .read = kovaplus_sysfs_read_profilex_settings, - .private = &profile_numbers[0] - }, - { - .attr = { .name = "profile2_settings", .mode = 0440 }, - .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, - .read = kovaplus_sysfs_read_profilex_settings, - .private = &profile_numbers[1] - }, - { - .attr = { .name = "profile3_settings", .mode = 0440 }, - .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, - .read = kovaplus_sysfs_read_profilex_settings, - .private = &profile_numbers[2] - }, - { - .attr = { .name = "profile4_settings", .mode = 0440 }, - .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, - .read = kovaplus_sysfs_read_profilex_settings, - .private = &profile_numbers[3] - }, - { - .attr = { .name = "profile5_settings", .mode = 0440 }, - .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, - .read = kovaplus_sysfs_read_profilex_settings, - .private = &profile_numbers[4] - }, - { - .attr = { .name = "profile1_buttons", .mode = 0440 }, - .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, - .read = kovaplus_sysfs_read_profilex_buttons, - .private = &profile_numbers[0] - }, - { - .attr = { .name = "profile2_buttons", .mode = 0440 }, - .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, - .read = kovaplus_sysfs_read_profilex_buttons, - .private = &profile_numbers[1] - }, - { - .attr = { .name = "profile3_buttons", .mode = 0440 }, - .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, - .read = kovaplus_sysfs_read_profilex_buttons, - .private = &profile_numbers[2] - }, - { - .attr = { .name = "profile4_buttons", .mode = 0440 }, - .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, - .read = kovaplus_sysfs_read_profilex_buttons, - .private = &profile_numbers[3] - }, - { - .attr = { .name = "profile5_buttons", .mode = 0440 }, - .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, - .read = kovaplus_sysfs_read_profilex_buttons, - .private = &profile_numbers[4] - }, - __ATTR_NULL +static const struct attribute_group *kovaplus_groups[] = { + &kovaplus_group, + NULL, }; static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev, @@ -577,9 +554,13 @@ static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus, break; case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI: kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1); + break; case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY: kovaplus->actual_x_sensitivity = button_report->data1; kovaplus->actual_y_sensitivity = button_report->data2; + break; + default: + break; } } @@ -662,8 +643,7 @@ static int __init kovaplus_init(void) kovaplus_class = class_create(THIS_MODULE, "kovaplus"); if (IS_ERR(kovaplus_class)) return PTR_ERR(kovaplus_class); - kovaplus_class->dev_attrs = kovaplus_attributes; - kovaplus_class->dev_bin_attrs = kovaplus_bin_attributes; + kovaplus_class->dev_groups = kovaplus_groups; retval = hid_register_driver(&kovaplus_driver); if (retval) diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c index 5084fb4b7e9..6adc0fa08d9 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-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index d4f1e3bee59..1a07e07d99a 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -156,7 +156,8 @@ PYRA_SYSFS_W(thingy, THINGY) \ PYRA_SYSFS_R(thingy, THINGY) #define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ -{ \ +PYRA_SYSFS_RW(thingy, THINGY); \ +static struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = PYRA_SIZE_ ## THINGY, \ .read = pyra_sysfs_read_ ## thingy, \ @@ -164,24 +165,25 @@ PYRA_SYSFS_R(thingy, THINGY) } #define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \ -{ \ +PYRA_SYSFS_R(thingy, THINGY); \ +static struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0440 }, \ .size = PYRA_SIZE_ ## THINGY, \ .read = pyra_sysfs_read_ ## thingy, \ } #define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \ -{ \ +PYRA_SYSFS_W(thingy, THINGY); \ +static struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = PYRA_SIZE_ ## THINGY, \ .write = pyra_sysfs_write_ ## thingy \ } -PYRA_SYSFS_W(control, CONTROL) -PYRA_SYSFS_RW(info, INFO) -PYRA_SYSFS_RW(profile_settings, PROFILE_SETTINGS) -PYRA_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) -PYRA_SYSFS_R(settings, SETTINGS) +PYRA_BIN_ATTRIBUTE_W(control, CONTROL); +PYRA_BIN_ATTRIBUTE_RW(info, INFO); +PYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS); +PYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS); static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, @@ -221,6 +223,25 @@ static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, PYRA_COMMAND_PROFILE_BUTTONS); } +#define PROFILE_ATTR(number) \ +static struct bin_attribute bin_attr_profile##number##_settings = { \ + .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ + .size = PYRA_SIZE_PROFILE_SETTINGS, \ + .read = pyra_sysfs_read_profilex_settings, \ + .private = &profile_numbers[number-1], \ +}; \ +static struct bin_attribute bin_attr_profile##number##_buttons = { \ + .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ + .size = PYRA_SIZE_PROFILE_BUTTONS, \ + .read = pyra_sysfs_read_profilex_buttons, \ + .private = &profile_numbers[number-1], \ +}; +PROFILE_ATTR(1); +PROFILE_ATTR(2); +PROFILE_ATTR(3); +PROFILE_ATTR(4); +PROFILE_ATTR(5); + static ssize_t pyra_sysfs_write_settings(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) @@ -258,6 +279,11 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp, return PYRA_SIZE_SETTINGS; } +PYRA_SYSFS_R(settings, SETTINGS); +static struct bin_attribute bin_attr_settings = + __BIN_ATTR(settings, (S_IWUSR | S_IRUGO), + pyra_sysfs_read_settings, pyra_sysfs_write_settings, + PYRA_SIZE_SETTINGS); static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev, struct device_attribute *attr, char *buf) @@ -266,6 +292,7 @@ static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi); } +static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL); static ssize_t pyra_sysfs_show_actual_profile(struct device *dev, struct device_attribute *attr, char *buf) @@ -282,6 +309,8 @@ static ssize_t pyra_sysfs_show_actual_profile(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile); } +static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL); +static DEVICE_ATTR(startup_profile, 0440, pyra_sysfs_show_actual_profile, NULL); static ssize_t pyra_sysfs_show_firmware_version(struct device *dev, struct device_attribute *attr, char *buf) @@ -301,84 +330,44 @@ static ssize_t pyra_sysfs_show_firmware_version(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); } +static DEVICE_ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version, + NULL); + +static struct attribute *pyra_attrs[] = { + &dev_attr_actual_cpi.attr, + &dev_attr_actual_profile.attr, + &dev_attr_firmware_version.attr, + &dev_attr_startup_profile.attr, + NULL, +}; + +static struct bin_attribute *pyra_bin_attributes[] = { + &bin_attr_control, + &bin_attr_info, + &bin_attr_profile_settings, + &bin_attr_profile_buttons, + &bin_attr_settings, + &bin_attr_profile1_settings, + &bin_attr_profile2_settings, + &bin_attr_profile3_settings, + &bin_attr_profile4_settings, + &bin_attr_profile5_settings, + &bin_attr_profile1_buttons, + &bin_attr_profile2_buttons, + &bin_attr_profile3_buttons, + &bin_attr_profile4_buttons, + &bin_attr_profile5_buttons, + NULL, +}; -static struct device_attribute pyra_attributes[] = { - __ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL), - __ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL), - __ATTR(firmware_version, 0440, - pyra_sysfs_show_firmware_version, NULL), - __ATTR(startup_profile, 0440, - pyra_sysfs_show_actual_profile, NULL), - __ATTR_NULL +static const struct attribute_group pyra_group = { + .attrs = pyra_attrs, + .bin_attrs = pyra_bin_attributes, }; -static struct bin_attribute pyra_bin_attributes[] = { - PYRA_BIN_ATTRIBUTE_W(control, CONTROL), - PYRA_BIN_ATTRIBUTE_RW(info, INFO), - PYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), - PYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), - PYRA_BIN_ATTRIBUTE_RW(settings, SETTINGS), - { - .attr = { .name = "profile1_settings", .mode = 0440 }, - .size = PYRA_SIZE_PROFILE_SETTINGS, - .read = pyra_sysfs_read_profilex_settings, - .private = &profile_numbers[0] - }, - { - .attr = { .name = "profile2_settings", .mode = 0440 }, - .size = PYRA_SIZE_PROFILE_SETTINGS, - .read = pyra_sysfs_read_profilex_settings, - .private = &profile_numbers[1] - }, - { - .attr = { .name = "profile3_settings", .mode = 0440 }, - .size = PYRA_SIZE_PROFILE_SETTINGS, - .read = pyra_sysfs_read_profilex_settings, - .private = &profile_numbers[2] - }, - { - .attr = { .name = "profile4_settings", .mode = 0440 }, - .size = PYRA_SIZE_PROFILE_SETTINGS, - .read = pyra_sysfs_read_profilex_settings, - .private = &profile_numbers[3] - }, - { - .attr = { .name = "profile5_settings", .mode = 0440 }, - .size = PYRA_SIZE_PROFILE_SETTINGS, - .read = pyra_sysfs_read_profilex_settings, - .private = &profile_numbers[4] - }, - { - .attr = { .name = "profile1_buttons", .mode = 0440 }, - .size = PYRA_SIZE_PROFILE_BUTTONS, - .read = pyra_sysfs_read_profilex_buttons, - .private = &profile_numbers[0] - }, - { - .attr = { .name = "profile2_buttons", .mode = 0440 }, - .size = PYRA_SIZE_PROFILE_BUTTONS, - .read = pyra_sysfs_read_profilex_buttons, - .private = &profile_numbers[1] - }, - { - .attr = { .name = "profile3_buttons", .mode = 0440 }, - .size = PYRA_SIZE_PROFILE_BUTTONS, - .read = pyra_sysfs_read_profilex_buttons, - .private = &profile_numbers[2] - }, - { - .attr = { .name = "profile4_buttons", .mode = 0440 }, - .size = PYRA_SIZE_PROFILE_BUTTONS, - .read = pyra_sysfs_read_profilex_buttons, - .private = &profile_numbers[3] - }, - { - .attr = { .name = "profile5_buttons", .mode = 0440 }, - .size = PYRA_SIZE_PROFILE_BUTTONS, - .read = pyra_sysfs_read_profilex_buttons, - .private = &profile_numbers[4] - }, - __ATTR_NULL +static const struct attribute_group *pyra_groups[] = { + &pyra_group, + NULL, }; static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, @@ -600,8 +589,7 @@ static int __init pyra_init(void) pyra_class = class_create(THIS_MODULE, "pyra"); if (IS_ERR(pyra_class)) return PTR_ERR(pyra_class); - pyra_class->dev_attrs = pyra_attributes; - pyra_class->dev_bin_attrs = pyra_bin_attributes; + pyra_class->dev_groups = pyra_groups; retval = hid_register_driver(&pyra_driver); if (retval) diff --git a/drivers/hid/hid-roccat-ryos.c b/drivers/hid/hid-roccat-ryos.c new file mode 100644 index 00000000000..47cc8f30ff6 --- /dev/null +++ b/drivers/hid/hid-roccat-ryos.c @@ -0,0 +1,241 @@ +/* + * Roccat Ryos driver for Linux + * + * Copyright (c) 2013 Stefan Achatz <erazor_de@users.sourceforge.net> + */ + +/* + * 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/types.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/hid-roccat.h> +#include "hid-ids.h" +#include "hid-roccat-common.h" + +enum { + RYOS_REPORT_NUMBER_SPECIAL = 3, + RYOS_USB_INTERFACE_PROTOCOL = 0, +}; + +struct ryos_report_special { + uint8_t number; /* RYOS_REPORT_NUMBER_SPECIAL */ + uint8_t data[4]; +} __packed; + +static struct class *ryos_class; + +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x04, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x05, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_primary, 0x06, 0x7d); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_function, 0x07, 0x5f); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_macro, 0x08, 0x23); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_thumbster, 0x09, 0x17); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_extra, 0x0a, 0x08); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_easyzone, 0x0b, 0x126); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(key_mask, 0x0c, 0x06); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(light, 0x0d, 0x10); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(macro, 0x0e, 0x7d2); +ROCCAT_COMMON2_BIN_ATTRIBUTE_R(info, 0x0f, 0x08); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(reset, 0x11, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(light_control, 0x13, 0x08); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(talk, 0x16, 0x10); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(stored_lights, 0x17, 0x0566); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(custom_lights, 0x18, 0x14); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(light_macro, 0x19, 0x07d2); + +static struct bin_attribute *ryos_bin_attrs[] = { + &bin_attr_control, + &bin_attr_profile, + &bin_attr_keys_primary, + &bin_attr_keys_function, + &bin_attr_keys_macro, + &bin_attr_keys_thumbster, + &bin_attr_keys_extra, + &bin_attr_keys_easyzone, + &bin_attr_key_mask, + &bin_attr_light, + &bin_attr_macro, + &bin_attr_info, + &bin_attr_reset, + &bin_attr_light_control, + &bin_attr_talk, + &bin_attr_stored_lights, + &bin_attr_custom_lights, + &bin_attr_light_macro, + NULL, +}; + +static const struct attribute_group ryos_group = { + .bin_attrs = ryos_bin_attrs, +}; + +static const struct attribute_group *ryos_groups[] = { + &ryos_group, + NULL, +}; + +static int ryos_init_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct roccat_common2_device *ryos; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != RYOS_USB_INTERFACE_PROTOCOL) { + hid_set_drvdata(hdev, NULL); + return 0; + } + + ryos = kzalloc(sizeof(*ryos), GFP_KERNEL); + if (!ryos) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, ryos); + + retval = roccat_common2_device_init_struct(usb_dev, ryos); + if (retval) { + hid_err(hdev, "couldn't init Ryos device\n"); + goto exit_free; + } + + retval = roccat_connect(ryos_class, hdev, + sizeof(struct ryos_report_special)); + if (retval < 0) { + hid_err(hdev, "couldn't init char dev\n"); + } else { + ryos->chrdev_minor = retval; + ryos->roccat_claimed = 1; + } + + return 0; +exit_free: + kfree(ryos); + return retval; +} + +static void ryos_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct roccat_common2_device *ryos; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != RYOS_USB_INTERFACE_PROTOCOL) + return; + + ryos = hid_get_drvdata(hdev); + if (ryos->roccat_claimed) + roccat_disconnect(ryos->chrdev_minor); + kfree(ryos); +} + +static int ryos_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + hid_err(hdev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + hid_err(hdev, "hw start failed\n"); + goto exit; + } + + retval = ryos_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install mouse\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void ryos_remove(struct hid_device *hdev) +{ + ryos_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static int ryos_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct roccat_common2_device *ryos = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != RYOS_USB_INTERFACE_PROTOCOL) + return 0; + + if (data[0] != RYOS_REPORT_NUMBER_SPECIAL) + return 0; + + if (ryos != NULL && ryos->roccat_claimed) + roccat_report_event(ryos->chrdev_minor, data); + + return 0; +} + +static const struct hid_device_id ryos_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, ryos_devices); + +static struct hid_driver ryos_driver = { + .name = "ryos", + .id_table = ryos_devices, + .probe = ryos_probe, + .remove = ryos_remove, + .raw_event = ryos_raw_event +}; + +static int __init ryos_init(void) +{ + int retval; + + ryos_class = class_create(THIS_MODULE, "ryos"); + if (IS_ERR(ryos_class)) + return PTR_ERR(ryos_class); + ryos_class->dev_groups = ryos_groups; + + retval = hid_register_driver(&ryos_driver); + if (retval) + class_destroy(ryos_class); + return retval; +} + +static void __exit ryos_exit(void) +{ + hid_unregister_driver(&ryos_driver); + class_destroy(ryos_class); +} + +module_init(ryos_init); +module_exit(ryos_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Ryos MK/Glow/Pro driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c index 31747a29c09..6dbf6e04dce 100644 --- a/drivers/hid/hid-roccat-savu.c +++ b/drivers/hid/hid-roccat-savu.c @@ -27,126 +27,39 @@ static struct class *savu_class; -static ssize_t savu_sysfs_read(struct file *fp, struct kobject *kobj, - char *buf, loff_t off, size_t count, - size_t real_size, uint command) -{ - struct device *dev = - container_of(kobj, struct device, kobj)->parent->parent; - struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev)); - struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); - int retval; - - if (off >= real_size) - return 0; - - if (off != 0 || count != real_size) - return -EINVAL; - - mutex_lock(&savu->savu_lock); - retval = roccat_common2_receive(usb_dev, command, buf, real_size); - mutex_unlock(&savu->savu_lock); - - return retval ? retval : real_size; -} - -static ssize_t savu_sysfs_write(struct file *fp, struct kobject *kobj, - void const *buf, loff_t off, size_t count, - size_t real_size, uint command) -{ - struct device *dev = - container_of(kobj, struct device, kobj)->parent->parent; - struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev)); - struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); - int retval; - - if (off != 0 || count != real_size) - return -EINVAL; - - mutex_lock(&savu->savu_lock); - retval = roccat_common2_send_with_status(usb_dev, command, - (void *)buf, real_size); - mutex_unlock(&savu->savu_lock); - - return retval ? retval : real_size; -} - -#define SAVU_SYSFS_W(thingy, THINGY) \ -static ssize_t savu_sysfs_write_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ -{ \ - return savu_sysfs_write(fp, kobj, buf, off, count, \ - SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \ -} - -#define SAVU_SYSFS_R(thingy, THINGY) \ -static ssize_t savu_sysfs_read_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ -{ \ - return savu_sysfs_read(fp, kobj, buf, off, count, \ - SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \ -} - -#define SAVU_SYSFS_RW(thingy, THINGY) \ -SAVU_SYSFS_W(thingy, THINGY) \ -SAVU_SYSFS_R(thingy, THINGY) - -#define SAVU_BIN_ATTRIBUTE_RW(thingy, THINGY) \ -{ \ - .attr = { .name = #thingy, .mode = 0660 }, \ - .size = SAVU_SIZE_ ## THINGY, \ - .read = savu_sysfs_read_ ## thingy, \ - .write = savu_sysfs_write_ ## thingy \ -} - -#define SAVU_BIN_ATTRIBUTE_R(thingy, THINGY) \ -{ \ - .attr = { .name = #thingy, .mode = 0440 }, \ - .size = SAVU_SIZE_ ## THINGY, \ - .read = savu_sysfs_read_ ## thingy, \ -} - -#define SAVU_BIN_ATTRIBUTE_W(thingy, THINGY) \ -{ \ - .attr = { .name = #thingy, .mode = 0220 }, \ - .size = SAVU_SIZE_ ## THINGY, \ - .write = savu_sysfs_write_ ## thingy \ -} - -SAVU_SYSFS_W(control, CONTROL) -SAVU_SYSFS_RW(profile, PROFILE) -SAVU_SYSFS_RW(general, GENERAL) -SAVU_SYSFS_RW(buttons, BUTTONS) -SAVU_SYSFS_RW(macro, MACRO) -SAVU_SYSFS_RW(info, INFO) -SAVU_SYSFS_RW(sensor, SENSOR) - -static struct bin_attribute savu_bin_attributes[] = { - SAVU_BIN_ATTRIBUTE_W(control, CONTROL), - SAVU_BIN_ATTRIBUTE_RW(profile, PROFILE), - SAVU_BIN_ATTRIBUTE_RW(general, GENERAL), - SAVU_BIN_ATTRIBUTE_RW(buttons, BUTTONS), - SAVU_BIN_ATTRIBUTE_RW(macro, MACRO), - SAVU_BIN_ATTRIBUTE_RW(info, INFO), - SAVU_BIN_ATTRIBUTE_RW(sensor, SENSOR), - __ATTR_NULL +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x4, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x5, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(general, 0x6, 0x10); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(buttons, 0x7, 0x2f); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(macro, 0x8, 0x0823); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(info, 0x9, 0x08); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0xc, 0x04); + +static struct bin_attribute *savu_bin_attrs[] = { + &bin_attr_control, + &bin_attr_profile, + &bin_attr_general, + &bin_attr_buttons, + &bin_attr_macro, + &bin_attr_info, + &bin_attr_sensor, + NULL, }; -static int savu_init_savu_device_struct(struct usb_device *usb_dev, - struct savu_device *savu) -{ - mutex_init(&savu->savu_lock); +static const struct attribute_group savu_group = { + .bin_attrs = savu_bin_attrs, +}; - return 0; -} +static const struct attribute_group *savu_groups[] = { + &savu_group, + NULL, +}; static int savu_init_specials(struct hid_device *hdev) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); struct usb_device *usb_dev = interface_to_usbdev(intf); - struct savu_device *savu; + struct roccat_common2_device *savu; int retval; if (intf->cur_altsetting->desc.bInterfaceProtocol @@ -162,9 +75,9 @@ static int savu_init_specials(struct hid_device *hdev) } hid_set_drvdata(hdev, savu); - retval = savu_init_savu_device_struct(usb_dev, savu); + retval = roccat_common2_device_init_struct(usb_dev, savu); if (retval) { - hid_err(hdev, "couldn't init struct savu_device\n"); + hid_err(hdev, "couldn't init Savu device\n"); goto exit_free; } @@ -186,7 +99,7 @@ exit_free: static void savu_remove_specials(struct hid_device *hdev) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - struct savu_device *savu; + struct roccat_common2_device *savu; if (intf->cur_altsetting->desc.bInterfaceProtocol != USB_INTERFACE_PROTOCOL_MOUSE) @@ -235,7 +148,7 @@ static void savu_remove(struct hid_device *hdev) hid_hw_stop(hdev); } -static void savu_report_to_chrdev(struct savu_device const *savu, +static void savu_report_to_chrdev(struct roccat_common2_device const *savu, u8 const *data) { struct savu_roccat_report roccat_report; @@ -257,7 +170,7 @@ static int savu_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - struct savu_device *savu = hid_get_drvdata(hdev); + struct roccat_common2_device *savu = hid_get_drvdata(hdev); if (intf->cur_altsetting->desc.bInterfaceProtocol != USB_INTERFACE_PROTOCOL_MOUSE) @@ -294,7 +207,7 @@ static int __init savu_init(void) savu_class = class_create(THIS_MODULE, "savu"); if (IS_ERR(savu_class)) return PTR_ERR(savu_class); - savu_class->dev_bin_attrs = savu_bin_attributes; + savu_class->dev_groups = savu_groups; retval = hid_register_driver(&savu_driver); if (retval) diff --git a/drivers/hid/hid-roccat-savu.h b/drivers/hid/hid-roccat-savu.h index 9120ba72087..d23217bd2b8 100644 --- a/drivers/hid/hid-roccat-savu.h +++ b/drivers/hid/hid-roccat-savu.h @@ -14,31 +14,6 @@ #include <linux/types.h> -enum { - SAVU_SIZE_CONTROL = 0x03, - SAVU_SIZE_PROFILE = 0x03, - SAVU_SIZE_GENERAL = 0x10, - SAVU_SIZE_BUTTONS = 0x2f, - SAVU_SIZE_MACRO = 0x0823, - SAVU_SIZE_INFO = 0x08, - SAVU_SIZE_SENSOR = 0x04, -}; - -enum savu_control_requests { - SAVU_CONTROL_REQUEST_GENERAL = 0x80, - SAVU_CONTROL_REQUEST_BUTTONS = 0x90, -}; - -enum savu_commands { - SAVU_COMMAND_CONTROL = 0x4, - SAVU_COMMAND_PROFILE = 0x5, - SAVU_COMMAND_GENERAL = 0x6, - SAVU_COMMAND_BUTTONS = 0x7, - SAVU_COMMAND_MACRO = 0x8, - SAVU_COMMAND_INFO = 0x9, - SAVU_COMMAND_SENSOR = 0xc, -}; - struct savu_mouse_report_special { uint8_t report_number; /* always 3 */ uint8_t zero; @@ -77,11 +52,4 @@ struct savu_roccat_report { uint8_t data[2]; } __packed; -struct savu_device { - int roccat_claimed; - int chrdev_minor; - - struct mutex savu_lock; -}; - #endif diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index b685b04dbf9..65c4ccfcbd2 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -242,7 +242,6 @@ static int roccat_release(struct inode *inode, struct file *file) * roccat_report_event() - output data to readers * @minor: minor device number returned by roccat_connect() * @data: pointer to data - * @len: size of data * * Return value is zero on success, a negative error code on failure. * @@ -290,6 +289,7 @@ EXPORT_SYMBOL_GPL(roccat_report_event); * @class: the class thats used to create the device. Meant to hold device * specific sysfs attributes. * @hid: the hid device the char device should be connected to. + * @report_size: size of reports * * Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on * success, a negative error code on failure. @@ -366,7 +366,7 @@ void roccat_disconnect(int minor) mutex_lock(&devices_lock); devices[minor] = NULL; mutex_unlock(&devices_lock); - + if (device->open) { hid_hw_close(device->hid); wake_up_interruptible(&device->wait); @@ -378,7 +378,7 @@ EXPORT_SYMBOL_GPL(roccat_disconnect); static long roccat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct roccat_device *device; unsigned int minor = iminor(inode); long retval = 0; @@ -426,13 +426,23 @@ static int __init roccat_init(void) if (retval < 0) { pr_warn("can't get major number\n"); - return retval; + goto error; } cdev_init(&roccat_cdev, &roccat_ops); - cdev_add(&roccat_cdev, dev_id, ROCCAT_MAX_DEVICES); + retval = cdev_add(&roccat_cdev, dev_id, ROCCAT_MAX_DEVICES); + if (retval < 0) { + pr_warn("cannot add cdev\n"); + goto cleanup_alloc_chrdev_region; + } return 0; + + + cleanup_alloc_chrdev_region: + unregister_chrdev_region(dev_id, ROCCAT_MAX_DEVICES); + error: + return retval; } static void __exit roccat_exit(void) diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c index 45aea77bb61..69cca1476a0 100644 --- a/drivers/hid/hid-saitek.c +++ b/drivers/hid/hid-saitek.c @@ -1,10 +1,17 @@ /* - * HID driver for Saitek devices, currently only the PS1000 (USB gamepad). + * HID driver for Saitek devices. + * + * PS1000 (USB gamepad): * Fixes the HID report descriptor by removing a non-existent axis and * clearing the constant bit on the input reports for buttons and d-pad. * (This module is based on "hid-ortek".) - * * Copyright (c) 2012 Andreas Hübner + * + * R.A.T.7, M.M.O.7 (USB gaming mice): + * Fixes the mode button which cycles through three constantly pressed + * buttons. All three press events are mapped to one button and the + * missing release event is generated immediately. + * */ /* @@ -21,12 +28,57 @@ #include "hid-ids.h" +#define SAITEK_FIX_PS1000 0x0001 +#define SAITEK_RELEASE_MODE_RAT7 0x0002 +#define SAITEK_RELEASE_MODE_MMO7 0x0004 + +struct saitek_sc { + unsigned long quirks; + int mode; +}; + +static int saitek_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + struct saitek_sc *ssc; + int ret; + + ssc = devm_kzalloc(&hdev->dev, sizeof(*ssc), GFP_KERNEL); + if (ssc == NULL) { + hid_err(hdev, "can't alloc saitek descriptor\n"); + return -ENOMEM; + } + + ssc->quirks = quirks; + ssc->mode = -1; + + hid_set_drvdata(hdev, ssc); + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + return 0; +} + static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { - if (*rsize == 137 && rdesc[20] == 0x09 && rdesc[21] == 0x33 - && rdesc[94] == 0x81 && rdesc[95] == 0x03 - && rdesc[110] == 0x81 && rdesc[111] == 0x03) { + struct saitek_sc *ssc = hid_get_drvdata(hdev); + + if ((ssc->quirks & SAITEK_FIX_PS1000) && *rsize == 137 && + rdesc[20] == 0x09 && rdesc[21] == 0x33 && + rdesc[94] == 0x81 && rdesc[95] == 0x03 && + rdesc[110] == 0x81 && rdesc[111] == 0x03) { hid_info(hdev, "Fixing up Saitek PS1000 report descriptor\n"); @@ -42,8 +94,93 @@ static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc, return rdesc; } +static int saitek_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *raw_data, int size) +{ + struct saitek_sc *ssc = hid_get_drvdata(hdev); + + if (ssc->quirks & SAITEK_RELEASE_MODE_RAT7 && size == 7) { + /* R.A.T.7 uses bits 13, 14, 15 for the mode */ + int mode = -1; + if (raw_data[1] & 0x01) + mode = 0; + else if (raw_data[1] & 0x02) + mode = 1; + else if (raw_data[1] & 0x04) + mode = 2; + + /* clear mode bits */ + raw_data[1] &= ~0x07; + + if (mode != ssc->mode) { + hid_dbg(hdev, "entered mode %d\n", mode); + if (ssc->mode != -1) { + /* use bit 13 as the mode button */ + raw_data[1] |= 0x04; + } + ssc->mode = mode; + } + } else if (ssc->quirks & SAITEK_RELEASE_MODE_MMO7 && size == 8) { + + /* M.M.O.7 uses bits 8, 22, 23 for the mode */ + int mode = -1; + if (raw_data[1] & 0x80) + mode = 0; + else if (raw_data[2] & 0x01) + mode = 1; + else if (raw_data[2] & 0x02) + mode = 2; + + /* clear mode bits */ + raw_data[1] &= ~0x80; + raw_data[2] &= ~0x03; + + if (mode != ssc->mode) { + hid_dbg(hdev, "entered mode %d\n", mode); + if (ssc->mode != -1) { + /* use bit 8 as the mode button, bits 22 + * and 23 do not represent buttons + * according to the HID report descriptor + */ + raw_data[1] |= 0x80; + } + ssc->mode = mode; + } + } + + return 0; +} + +static int saitek_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct saitek_sc *ssc = hid_get_drvdata(hdev); + struct input_dev *input = field->hidinput->input; + + if (usage->type == EV_KEY && value && + (((ssc->quirks & SAITEK_RELEASE_MODE_RAT7) && + usage->code - BTN_MOUSE == 10) || + ((ssc->quirks & SAITEK_RELEASE_MODE_MMO7) && + usage->code - BTN_MOUSE == 15))) { + + input_report_key(input, usage->code, 1); + + /* report missing release event */ + input_report_key(input, usage->code, 0); + + return 1; + } + + return 0; +} + static const struct hid_device_id saitek_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000)}, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000), + .driver_data = SAITEK_FIX_PS1000 }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7), + .driver_data = SAITEK_RELEASE_MODE_RAT7 }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7), + .driver_data = SAITEK_RELEASE_MODE_MMO7 }, { } }; @@ -52,19 +189,11 @@ MODULE_DEVICE_TABLE(hid, saitek_devices); static struct hid_driver saitek_driver = { .name = "saitek", .id_table = saitek_devices, - .report_fixup = saitek_report_fixup + .probe = saitek_probe, + .report_fixup = saitek_report_fixup, + .raw_event = saitek_raw_event, + .event = saitek_event, }; +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 a5821d31722..7cbb067d4a9 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 0bc58bd8d4f..e244e449cbb 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -18,8 +18,6 @@ */ #include <linux/device.h> #include <linux/hid.h> -#include <linux/usb.h> -#include "usbhid/usbhid.h" #include <linux/module.h> #include <linux/slab.h> #include <linux/mfd/core.h> @@ -28,6 +26,8 @@ #include <linux/hid-sensor-hub.h> #include "hid-ids.h" +#define HID_SENSOR_HUB_ENUM_QUIRK 0x01 + /** * struct sensor_hub_pending - Synchronous read pending information * @status: Pending status true/false. @@ -56,9 +56,9 @@ struct sensor_hub_pending { * @dyn_callback_lock: spin lock to protect callback list * @hid_sensor_hub_client_devs: Stores all MFD cells for a hub instance. * @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached). + * @ref_cnt: Number of MFD clients have opened this device */ struct sensor_hub_data { - struct hid_sensor_hub_device *hsdev; struct mutex mutex; spinlock_t lock; struct sensor_hub_pending pending; @@ -66,6 +66,8 @@ struct sensor_hub_data { spinlock_t dyn_callback_lock; struct mfd_cell *hid_sensor_hub_client_devs; int hid_sensor_client_cnt; + unsigned long quirks; + int ref_cnt; }; /** @@ -78,6 +80,7 @@ struct sensor_hub_data { struct hid_sensor_hub_callbacks_list { struct list_head list; u32 usage_id; + struct hid_sensor_hub_device *hsdev; struct hid_sensor_hub_callbacks *usage_callback; void *priv; }; @@ -96,45 +99,52 @@ static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev, return NULL; } -static int sensor_hub_get_physical_device_count( - struct hid_report_enum *report_enum) +static int sensor_hub_get_physical_device_count(struct hid_device *hdev) { - struct hid_report *report; - struct hid_field *field; - int cnt = 0; + int i; + int count = 0; - list_for_each_entry(report, &report_enum->report_list, list) { - field = report->field[0]; - if (report->maxfield && field && - field->physical) - cnt++; + for (i = 0; i < hdev->maxcollection; ++i) { + struct hid_collection *collection = &hdev->collection[i]; + if (collection->type == HID_COLLECTION_PHYSICAL) + ++count; } - return cnt; + return count; } static void sensor_hub_fill_attr_info( struct hid_sensor_hub_attribute_info *info, - s32 index, s32 report_id, s32 units, s32 unit_expo, s32 size) + s32 index, s32 report_id, struct hid_field *field) { info->index = index; info->report_id = report_id; - info->units = units; - info->unit_expo = unit_expo; - info->size = size/8; + info->units = field->unit; + info->unit_expo = field->unit_exponent; + info->size = (field->report_size * field->report_count)/8; + info->logical_minimum = field->logical_minimum; + info->logical_maximum = field->logical_maximum; } static struct hid_sensor_hub_callbacks *sensor_hub_get_callback( struct hid_device *hdev, - u32 usage_id, void **priv) + u32 usage_id, + int collection_index, + struct hid_sensor_hub_device **hsdev, + void **priv) { struct hid_sensor_hub_callbacks_list *callback; struct sensor_hub_data *pdata = hid_get_drvdata(hdev); spin_lock(&pdata->dyn_callback_lock); list_for_each_entry(callback, &pdata->dyn_callback_list, list) - if (callback->usage_id == usage_id) { + if (callback->usage_id == usage_id && + (collection_index >= + callback->hsdev->start_collection_index) && + (collection_index < + callback->hsdev->end_collection_index)) { *priv = callback->priv; + *hsdev = callback->hsdev; spin_unlock(&pdata->dyn_callback_lock); return callback->usage_callback; } @@ -149,23 +159,26 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, { struct hid_sensor_hub_callbacks_list *callback; struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); + unsigned long flags; - spin_lock(&pdata->dyn_callback_lock); + spin_lock_irqsave(&pdata->dyn_callback_lock, flags); list_for_each_entry(callback, &pdata->dyn_callback_list, list) - if (callback->usage_id == usage_id) { - spin_unlock(&pdata->dyn_callback_lock); + if (callback->usage_id == usage_id && + callback->hsdev == hsdev) { + spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return -EINVAL; } callback = kzalloc(sizeof(*callback), GFP_ATOMIC); if (!callback) { - spin_unlock(&pdata->dyn_callback_lock); + spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return -ENOMEM; } + callback->hsdev = hsdev; callback->usage_callback = usage_callback; callback->usage_id = usage_id; callback->priv = NULL; list_add_tail(&callback->list, &pdata->dyn_callback_list); - spin_unlock(&pdata->dyn_callback_lock); + spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return 0; } @@ -176,15 +189,17 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, { struct hid_sensor_hub_callbacks_list *callback; struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); + unsigned long flags; - spin_lock(&pdata->dyn_callback_lock); + spin_lock_irqsave(&pdata->dyn_callback_lock, flags); list_for_each_entry(callback, &pdata->dyn_callback_list, list) - if (callback->usage_id == usage_id) { + if (callback->usage_id == usage_id && + callback->hsdev == hsdev) { list_del(&callback->list); kfree(callback); break; } - spin_unlock(&pdata->dyn_callback_lock); + spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return 0; } @@ -194,18 +209,18 @@ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, u32 field_index, s32 value) { struct hid_report *report; - struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); int ret = 0; mutex_lock(&data->mutex); report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); - if (!report || (field_index >= report->maxfield)) { + if (!report || (field_index >= report->maxfield)) { ret = -EINVAL; goto done_proc; } hid_set_field(report->field[field_index], 0, value); - usbhid_submit_report(hsdev->hdev, report, USB_DIR_OUT); - usbhid_wait_io(hsdev->hdev); + hid_hw_request(hsdev->hdev, report, HID_REQ_SET_REPORT); + hid_hw_wait(hsdev->hdev); done_proc: mutex_unlock(&data->mutex); @@ -218,17 +233,18 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, u32 field_index, s32 *value) { struct hid_report *report; - struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); int ret = 0; mutex_lock(&data->mutex); report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); - if (!report || (field_index >= report->maxfield)) { + if (!report || (field_index >= report->maxfield) || + report->field[field_index]->report_count < 1) { ret = -EINVAL; goto done_proc; } - usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); - usbhid_wait_io(hsdev->hdev); + hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); + hid_hw_wait(hsdev->hdev); *value = report->field[field_index]->value[0]; done_proc: @@ -243,7 +259,7 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, u32 usage_id, u32 attr_usage_id, u32 report_id) { - struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); unsigned long flags; struct hid_report *report; int ret_val = 0; @@ -257,13 +273,12 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, spin_lock_irqsave(&data->lock, flags); data->pending.status = true; + spin_unlock_irqrestore(&data->lock, flags); report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT); - if (!report) { - spin_unlock_irqrestore(&data->lock, flags); + if (!report) goto err_free; - } - usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); - spin_unlock_irqrestore(&data->lock, flags); + + hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5); switch (data->pending.raw_size) { case 1: @@ -288,6 +303,28 @@ err_free: } EXPORT_SYMBOL_GPL(sensor_hub_input_attr_get_raw_value); +int hid_sensor_get_usage_index(struct hid_sensor_hub_device *hsdev, + u32 report_id, int field_index, u32 usage_id) +{ + struct hid_report *report; + struct hid_field *field; + int i; + + report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); + if (!report || (field_index >= report->maxfield)) + goto done_proc; + + field = report->field[field_index]; + for (i = 0; i < field->maxusage; ++i) { + if (field->usage[i].hid == usage_id) + return field->usage[i].usage_index; + } + +done_proc: + return -EINVAL; +} +EXPORT_SYMBOL_GPL(hid_sensor_get_usage_index); + int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, u8 type, u32 usage_id, @@ -295,8 +332,7 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, struct hid_sensor_hub_attribute_info *info) { int ret = -1; - int i, j; - int collection_index = -1; + int i; struct hid_report *report; struct hid_field *field; struct hid_report_enum *report_enum; @@ -304,54 +340,37 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, /* Initialize with defaults */ info->usage_id = usage_id; - info->attrib_id = attr_usage_id; + info->attrib_id = attr_usage_id; info->report_id = -1; info->index = -1; info->units = -1; info->unit_expo = -1; - for (i = 0; i < hdev->maxcollection; ++i) { - struct hid_collection *collection = &hdev->collection[i]; - if (usage_id == collection->usage) { - collection_index = i; - break; - } - } - if (collection_index == -1) - goto err_ret; - report_enum = &hdev->report_enum[type]; list_for_each_entry(report, &report_enum->report_list, list) { for (i = 0; i < report->maxfield; ++i) { field = report->field[i]; - if (field->physical == usage_id && - field->logical == attr_usage_id) { - sensor_hub_fill_attr_info(info, i, report->id, - field->unit, field->unit_exponent, - field->report_size); - ret = 0; - } else { - for (j = 0; j < field->maxusage; ++j) { - if (field->usage[j].hid == - attr_usage_id && - field->usage[j].collection_index == - collection_index) { - sensor_hub_fill_attr_info(info, - i, report->id, - field->unit, - field->unit_exponent, - field->report_size); - ret = 0; - break; - } + if (field->maxusage) { + if (field->physical == usage_id && + (field->logical == attr_usage_id || + field->usage[0].hid == + attr_usage_id) && + (field->usage[0].collection_index >= + hsdev->start_collection_index) && + (field->usage[0].collection_index < + hsdev->end_collection_index)) { + + sensor_hub_fill_attr_info(info, i, + report->id, + field); + ret = 0; + break; } } - if (ret == 0) - break; } + } -err_ret: return ret; } EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info); @@ -359,34 +378,36 @@ EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info); #ifdef CONFIG_PM static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message) { - struct sensor_hub_data *pdata = hid_get_drvdata(hdev); + struct sensor_hub_data *pdata = hid_get_drvdata(hdev); struct hid_sensor_hub_callbacks_list *callback; + unsigned long flags; hid_dbg(hdev, " sensor_hub_suspend\n"); - spin_lock(&pdata->dyn_callback_lock); + spin_lock_irqsave(&pdata->dyn_callback_lock, flags); list_for_each_entry(callback, &pdata->dyn_callback_list, list) { if (callback->usage_callback->suspend) callback->usage_callback->suspend( - pdata->hsdev, callback->priv); + callback->hsdev, callback->priv); } - spin_unlock(&pdata->dyn_callback_lock); + spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return 0; } static int sensor_hub_resume(struct hid_device *hdev) { - struct sensor_hub_data *pdata = hid_get_drvdata(hdev); + struct sensor_hub_data *pdata = hid_get_drvdata(hdev); struct hid_sensor_hub_callbacks_list *callback; + unsigned long flags; hid_dbg(hdev, " sensor_hub_resume\n"); - spin_lock(&pdata->dyn_callback_lock); + spin_lock_irqsave(&pdata->dyn_callback_lock, flags); list_for_each_entry(callback, &pdata->dyn_callback_list, list) { if (callback->usage_callback->resume) callback->usage_callback->resume( - pdata->hsdev, callback->priv); + callback->hsdev, callback->priv); } - spin_unlock(&pdata->dyn_callback_lock); + spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return 0; } @@ -396,6 +417,7 @@ static int sensor_hub_reset_resume(struct hid_device *hdev) return 0; } #endif + /* * Handle raw report as sent by device */ @@ -410,6 +432,7 @@ static int sensor_hub_raw_event(struct hid_device *hdev, struct hid_sensor_hub_callbacks *callback = NULL; struct hid_collection *collection = NULL; void *priv = NULL; + struct hid_sensor_hub_device *hsdev = NULL; hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n", report->id, size, report->type); @@ -418,26 +441,25 @@ static int sensor_hub_raw_event(struct hid_device *hdev, return 1; ptr = raw_data; - ptr++; /*Skip report id*/ + ptr++; /* Skip report id */ spin_lock_irqsave(&pdata->lock, flags); for (i = 0; i < report->maxfield; ++i) { - hid_dbg(hdev, "%d collection_index:%x hid:%x sz:%x\n", i, report->field[i]->usage->collection_index, report->field[i]->usage->hid, - report->field[i]->report_size/8); - - sz = report->field[i]->report_size/8; + (report->field[i]->report_size * + report->field[i]->report_count)/8); + sz = (report->field[i]->report_size * + report->field[i]->report_count)/8; if (pdata->pending.status && pdata->pending.attr_usage_id == report->field[i]->usage->hid) { hid_dbg(hdev, "data was pending ...\n"); - pdata->pending.raw_data = kmalloc(sz, GFP_ATOMIC); - if (pdata->pending.raw_data) { - memcpy(pdata->pending.raw_data, ptr, sz); - pdata->pending.raw_size = sz; - } else + pdata->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC); + if (pdata->pending.raw_data) + pdata->pending.raw_size = sz; + else pdata->pending.raw_size = 0; complete(&pdata->pending.ready); } @@ -445,29 +467,99 @@ static int sensor_hub_raw_event(struct hid_device *hdev, report->field[i]->usage->collection_index]; hid_dbg(hdev, "collection->usage %x\n", collection->usage); - callback = sensor_hub_get_callback(pdata->hsdev->hdev, - report->field[i]->physical, - &priv); + + callback = sensor_hub_get_callback(hdev, + report->field[i]->physical, + report->field[i]->usage[0].collection_index, + &hsdev, &priv); + if (callback && callback->capture_sample) { if (report->field[i]->logical) - callback->capture_sample(pdata->hsdev, + callback->capture_sample(hsdev, report->field[i]->logical, sz, ptr, callback->pdev); else - callback->capture_sample(pdata->hsdev, + callback->capture_sample(hsdev, report->field[i]->usage->hid, sz, ptr, callback->pdev); } ptr += sz; } if (callback && collection && callback->send_event) - callback->send_event(pdata->hsdev, collection->usage, + callback->send_event(hsdev, collection->usage, callback->pdev); spin_unlock_irqrestore(&pdata->lock, flags); return 1; } +int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev) +{ + int ret = 0; + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + + mutex_lock(&data->mutex); + if (!data->ref_cnt) { + ret = hid_hw_open(hsdev->hdev); + if (ret) { + hid_err(hsdev->hdev, "failed to open hid device\n"); + mutex_unlock(&data->mutex); + return ret; + } + } + data->ref_cnt++; + mutex_unlock(&data->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(sensor_hub_device_open); + +void sensor_hub_device_close(struct hid_sensor_hub_device *hsdev) +{ + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + + mutex_lock(&data->mutex); + data->ref_cnt--; + if (!data->ref_cnt) + hid_hw_close(hsdev->hdev); + mutex_unlock(&data->mutex); +} +EXPORT_SYMBOL_GPL(sensor_hub_device_close); + +static __u8 *sensor_hub_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + int index; + struct sensor_hub_data *sd = hid_get_drvdata(hdev); + unsigned char report_block[] = { + 0x0a, 0x16, 0x03, 0x15, 0x00, 0x25, 0x05}; + unsigned char power_block[] = { + 0x0a, 0x19, 0x03, 0x15, 0x00, 0x25, 0x05}; + + if (!(sd->quirks & HID_SENSOR_HUB_ENUM_QUIRK)) { + hid_dbg(hdev, "No Enum quirks\n"); + return rdesc; + } + + /* Looks for power and report state usage id and force to 1 */ + for (index = 0; index < *rsize; ++index) { + if (((*rsize - index) > sizeof(report_block)) && + !memcmp(&rdesc[index], report_block, + sizeof(report_block))) { + rdesc[index + 4] = 0x01; + index += sizeof(report_block); + } + if (((*rsize - index) > sizeof(power_block)) && + !memcmp(&rdesc[index], power_block, + sizeof(power_block))) { + rdesc[index + 4] = 0x01; + index += sizeof(power_block); + } + } + + return rdesc; +} + static int sensor_hub_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -475,56 +567,42 @@ static int sensor_hub_probe(struct hid_device *hdev, struct sensor_hub_data *sd; int i; char *name; - struct hid_report *report; - struct hid_report_enum *report_enum; - struct hid_field *field; int dev_cnt; + struct hid_sensor_hub_device *hsdev; + struct hid_sensor_hub_device *last_hsdev = NULL; - sd = kzalloc(sizeof(struct sensor_hub_data), GFP_KERNEL); + sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL); if (!sd) { hid_err(hdev, "cannot allocate Sensor data\n"); return -ENOMEM; } - sd->hsdev = kzalloc(sizeof(struct hid_sensor_hub_device), GFP_KERNEL); - if (!sd->hsdev) { - hid_err(hdev, "cannot allocate hid_sensor_hub_device\n"); - ret = -ENOMEM; - goto err_free_hub; - } + hid_set_drvdata(hdev, sd); - sd->hsdev->hdev = hdev; - sd->hsdev->vendor_id = hdev->vendor; - sd->hsdev->product_id = hdev->product; + sd->quirks = id->driver_data; + spin_lock_init(&sd->lock); spin_lock_init(&sd->dyn_callback_lock); mutex_init(&sd->mutex); ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err_free; + return ret; } INIT_LIST_HEAD(&hdev->inputs); ret = hid_hw_start(hdev, 0); if (ret) { hid_err(hdev, "hw start failed\n"); - goto err_free; - } - ret = hid_hw_open(hdev); - if (ret) { - hid_err(hdev, "failed to open input interrupt pipe\n"); - goto err_stop_hw; + return ret; } - INIT_LIST_HEAD(&sd->dyn_callback_list); sd->hid_sensor_client_cnt = 0; - report_enum = &hdev->report_enum[HID_INPUT_REPORT]; - dev_cnt = sensor_hub_get_physical_device_count(report_enum); + dev_cnt = sensor_hub_get_physical_device_count(hdev); if (dev_cnt > HID_MAX_PHY_DEVICES) { hid_err(hdev, "Invalid Physical device count\n"); ret = -EINVAL; - goto err_close; + goto err_stop_hw; } sd->hid_sensor_hub_client_devs = kzalloc(dev_cnt * sizeof(struct mfd_cell), @@ -532,51 +610,69 @@ static int sensor_hub_probe(struct hid_device *hdev, if (sd->hid_sensor_hub_client_devs == NULL) { hid_err(hdev, "Failed to allocate memory for mfd cells\n"); ret = -ENOMEM; - goto err_close; + goto err_stop_hw; } - list_for_each_entry(report, &report_enum->report_list, list) { - hid_dbg(hdev, "Report id:%x\n", report->id); - field = report->field[0]; - if (report->maxfield && field && - field->physical) { + + for (i = 0; i < hdev->maxcollection; ++i) { + struct hid_collection *collection = &hdev->collection[i]; + + if (collection->type == HID_COLLECTION_PHYSICAL) { + + hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL); + if (!hsdev) { + hid_err(hdev, "cannot allocate hid_sensor_hub_device\n"); + ret = -ENOMEM; + goto err_no_mem; + } + hsdev->hdev = hdev; + hsdev->vendor_id = hdev->vendor; + hsdev->product_id = hdev->product; + hsdev->start_collection_index = i; + if (last_hsdev) + last_hsdev->end_collection_index = i; + last_hsdev = hsdev; name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x", - field->physical); - if (name == NULL) { + collection->usage); + if (name == NULL) { hid_err(hdev, "Failed MFD device name\n"); ret = -ENOMEM; - goto err_free_names; + kfree(hsdev); + goto err_no_mem; } sd->hid_sensor_hub_client_devs[ + sd->hid_sensor_client_cnt].id = + PLATFORM_DEVID_AUTO; + sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].name = name; sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].platform_data = - sd->hsdev; + hsdev; sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].pdata_size = - sizeof(*sd->hsdev); - hid_dbg(hdev, "Adding %s:%p\n", name, sd); + sizeof(*hsdev); + hid_dbg(hdev, "Adding %s:%d\n", name, + hsdev->start_collection_index); sd->hid_sensor_client_cnt++; } } + if (last_hsdev) + last_hsdev->end_collection_index = i; + ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs, sd->hid_sensor_client_cnt, NULL, 0, NULL); if (ret < 0) - goto err_free_names; + goto err_no_mem; return ret; -err_free_names: - for (i = 0; i < sd->hid_sensor_client_cnt ; ++i) +err_no_mem: + for (i = 0; i < sd->hid_sensor_client_cnt; ++i) { kfree(sd->hid_sensor_hub_client_devs[i].name); + kfree(sd->hid_sensor_hub_client_devs[i].platform_data); + } kfree(sd->hid_sensor_hub_client_devs); -err_close: - hid_hw_close(hdev); err_stop_hw: hid_hw_stop(hdev); -err_free: - kfree(sd->hsdev); -err_free_hub: - kfree(sd); return ret; } @@ -595,51 +691,60 @@ static void sensor_hub_remove(struct hid_device *hdev) complete(&data->pending.ready); spin_unlock_irqrestore(&data->lock, flags); mfd_remove_devices(&hdev->dev); - for (i = 0; i < data->hid_sensor_client_cnt ; ++i) + for (i = 0; i < data->hid_sensor_client_cnt; ++i) { kfree(data->hid_sensor_hub_client_devs[i].name); + kfree(data->hid_sensor_hub_client_devs[i].platform_data); + } kfree(data->hid_sensor_hub_client_devs); hid_set_drvdata(hdev, NULL); mutex_destroy(&data->mutex); - kfree(data->hsdev); - kfree(data); } 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, USB_VENDOR_ID_INTEL_0, + USB_DEVICE_ID_INTEL_HID_SENSOR_0), + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1, + USB_DEVICE_ID_INTEL_HID_SENSOR_0), + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1, + USB_DEVICE_ID_INTEL_HID_SENSOR_1), + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_SURFACE_PRO_2), + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_TOUCH_COVER_2), + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, + USB_DEVICE_ID_MS_TYPE_COVER_2), + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0, + USB_DEVICE_ID_STM_HID_SENSOR_1), + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_TEXAS_INSTRUMENTS, + USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA), + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { 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, .probe = sensor_hub_probe, .remove = sensor_hub_remove, .raw_event = sensor_hub_raw_event, + .report_fixup = sensor_hub_report_fixup, #ifdef CONFIG_PM .suspend = sensor_hub_suspend, - .resume = sensor_hub_resume, - .reset_resume = sensor_hub_reset_resume, + .resume = sensor_hub_resume, + .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 42257acfeb7..37845eccddb 100644 --- a/drivers/hid/hid-sjoy.c +++ b/drivers/hid/hid-sjoy.c @@ -28,13 +28,11 @@ #include <linux/input.h> #include <linux/slab.h> -#include <linux/usb.h> #include <linux/hid.h> #include <linux/module.h> #include "hid-ids.h" #ifdef CONFIG_SMARTJOYPLUS_FF -#include "usbhid/usbhid.h" struct sjoyff_device { struct hid_report *report; @@ -57,7 +55,7 @@ static int hid_sjoyff_play(struct input_dev *dev, void *data, sjoyff->report->field[0]->value[1] = right; sjoyff->report->field[0]->value[2] = left; dev_dbg(&dev->dev, "running with 0x%02x 0x%02x\n", left, right); - usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT); + hid_hw_request(hid, sjoyff->report, HID_REQ_SET_REPORT); return 0; } @@ -115,7 +113,7 @@ static int sjoyff_init(struct hid_device *hid) sjoyff->report->field[0]->value[0] = 0x01; sjoyff->report->field[0]->value[1] = 0x00; sjoyff->report->field[0]->value[2] = 0x00; - usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT); + hid_hw_request(hid, sjoyff->report, HID_REQ_SET_REPORT); } hid_info(hid, "Force feedback for SmartJoy PLUS PS2/USB adapter\n"); @@ -177,19 +175,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 7f33ebf299c..2259eaa8b98 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1,11 +1,13 @@ /* - * HID driver for some sony "special" devices + * HID driver for Sony / PS2 / PS3 BD devices. * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc * Copyright (c) 2008 Jiri Slaby - * Copyright (c) 2006-2008 Jiri Kosina + * Copyright (c) 2012 David Dillow <dave@thedillows.org> + * Copyright (c) 2006-2013 Jiri Kosina + * Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com> */ /* @@ -15,17 +17,44 @@ * any later version. */ +/* + * NOTE: in order for the Sony PS3 BD Remote Control to be found by + * a Bluetooth host, the key combination Start+Enter has to be kept pressed + * for about 7 seconds with the Bluetooth Host Controller in discovering mode. + * + * There will be no PIN request from the device. + */ + #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/usb.h> +#include <linux/leds.h> +#include <linux/power_supply.h> +#include <linux/spinlock.h> +#include <linux/list.h> +#include <linux/idr.h> +#include <linux/input/mt.h> #include "hid-ids.h" -#define VAIO_RDESC_CONSTANT (1 << 0) -#define SIXAXIS_CONTROLLER_USB (1 << 1) -#define SIXAXIS_CONTROLLER_BT (1 << 2) +#define VAIO_RDESC_CONSTANT BIT(0) +#define SIXAXIS_CONTROLLER_USB BIT(1) +#define SIXAXIS_CONTROLLER_BT BIT(2) +#define BUZZ_CONTROLLER BIT(3) +#define PS3REMOTE BIT(4) +#define DUALSHOCK4_CONTROLLER_USB BIT(5) +#define DUALSHOCK4_CONTROLLER_BT BIT(6) + +#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) +#define DUALSHOCK4_CONTROLLER (DUALSHOCK4_CONTROLLER_USB |\ + DUALSHOCK4_CONTROLLER_BT) +#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\ + DUALSHOCK4_CONTROLLER) +#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER) +#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER) + +#define MAX_LEDS 4 static const u8 sixaxis_rdesc_fixup[] = { 0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C, @@ -33,22 +62,801 @@ 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, +}; + +/* + * The default descriptor doesn't provide mapping for the accelerometers + * or orientation sensors. This fixed descriptor maps the accelerometers + * to usage values 0x40, 0x41 and 0x42 and maps the orientation sensors + * to usage values 0x43, 0x44 and 0x45. + */ +static u8 dualshock4_usb_rdesc[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x05, /* Usage (Gamepad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x01, /* Report ID (1), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x09, 0x32, /* Usage (Z), */ + 0x09, 0x35, /* Usage (Rz), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x04, /* Report Count (4), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x39, /* Usage (Hat Switch), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 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, 0x42, /* Input (Variable, Null State), */ + 0x65, 0x00, /* Unit, */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x0E, /* Usage Maximum (0Eh), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x0E, /* Report Count (14), */ + 0x81, 0x02, /* Input (Variable), */ + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x09, 0x20, /* Usage (20h), */ + 0x75, 0x06, /* Report Size (6), */ + 0x95, 0x01, /* Report Count (1), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x7F, /* Logical Maximum (127), */ + 0x81, 0x02, /* Input (Variable), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x33, /* Usage (Rx), */ + 0x09, 0x34, /* Usage (Ry), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x02, /* Input (Variable), */ + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x09, 0x21, /* Usage (21h), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x19, 0x40, /* Usage Minimum (40h), */ + 0x29, 0x42, /* Usage Maximum (42h), */ + 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */ + 0x26, 0x00, 0x7F, /* Logical Maximum (32767), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x19, 0x43, /* Usage Minimum (43h), */ + 0x29, 0x45, /* Usage Maximum (45h), */ + 0x16, 0xFF, 0xBF, /* Logical Minimum (-16385), */ + 0x26, 0x00, 0x40, /* Logical Maximum (16384), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x09, 0x21, /* Usage (21h), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0xFF, /* Logical Maximum (255), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x27, /* Report Count (39), */ + 0x81, 0x02, /* Input (Variable), */ + 0x85, 0x05, /* Report ID (5), */ + 0x09, 0x22, /* Usage (22h), */ + 0x95, 0x1F, /* Report Count (31), */ + 0x91, 0x02, /* Output (Variable), */ + 0x85, 0x04, /* Report ID (4), */ + 0x09, 0x23, /* Usage (23h), */ + 0x95, 0x24, /* Report Count (36), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x02, /* Report ID (2), */ + 0x09, 0x24, /* Usage (24h), */ + 0x95, 0x24, /* Report Count (36), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x08, /* Report ID (8), */ + 0x09, 0x25, /* Usage (25h), */ + 0x95, 0x03, /* Report Count (3), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x10, /* Report ID (16), */ + 0x09, 0x26, /* Usage (26h), */ + 0x95, 0x04, /* Report Count (4), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x11, /* Report ID (17), */ + 0x09, 0x27, /* Usage (27h), */ + 0x95, 0x02, /* Report Count (2), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x12, /* Report ID (18), */ + 0x06, 0x02, 0xFF, /* Usage Page (FF02h), */ + 0x09, 0x21, /* Usage (21h), */ + 0x95, 0x0F, /* Report Count (15), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x13, /* Report ID (19), */ + 0x09, 0x22, /* Usage (22h), */ + 0x95, 0x16, /* Report Count (22), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x14, /* Report ID (20), */ + 0x06, 0x05, 0xFF, /* Usage Page (FF05h), */ + 0x09, 0x20, /* Usage (20h), */ + 0x95, 0x10, /* Report Count (16), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x15, /* Report ID (21), */ + 0x09, 0x21, /* Usage (21h), */ + 0x95, 0x2C, /* Report Count (44), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x06, 0x80, 0xFF, /* Usage Page (FF80h), */ + 0x85, 0x80, /* Report ID (128), */ + 0x09, 0x20, /* Usage (20h), */ + 0x95, 0x06, /* Report Count (6), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x81, /* Report ID (129), */ + 0x09, 0x21, /* Usage (21h), */ + 0x95, 0x06, /* Report Count (6), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x82, /* Report ID (130), */ + 0x09, 0x22, /* Usage (22h), */ + 0x95, 0x05, /* Report Count (5), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x83, /* Report ID (131), */ + 0x09, 0x23, /* Usage (23h), */ + 0x95, 0x01, /* Report Count (1), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x84, /* Report ID (132), */ + 0x09, 0x24, /* Usage (24h), */ + 0x95, 0x04, /* Report Count (4), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x85, /* Report ID (133), */ + 0x09, 0x25, /* Usage (25h), */ + 0x95, 0x06, /* Report Count (6), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x86, /* Report ID (134), */ + 0x09, 0x26, /* Usage (26h), */ + 0x95, 0x06, /* Report Count (6), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x87, /* Report ID (135), */ + 0x09, 0x27, /* Usage (27h), */ + 0x95, 0x23, /* Report Count (35), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x88, /* Report ID (136), */ + 0x09, 0x28, /* Usage (28h), */ + 0x95, 0x22, /* Report Count (34), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x89, /* Report ID (137), */ + 0x09, 0x29, /* Usage (29h), */ + 0x95, 0x02, /* Report Count (2), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x90, /* Report ID (144), */ + 0x09, 0x30, /* Usage (30h), */ + 0x95, 0x05, /* Report Count (5), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x91, /* Report ID (145), */ + 0x09, 0x31, /* Usage (31h), */ + 0x95, 0x03, /* Report Count (3), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x92, /* Report ID (146), */ + 0x09, 0x32, /* Usage (32h), */ + 0x95, 0x03, /* Report Count (3), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x93, /* Report ID (147), */ + 0x09, 0x33, /* Usage (33h), */ + 0x95, 0x0C, /* Report Count (12), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA0, /* Report ID (160), */ + 0x09, 0x40, /* Usage (40h), */ + 0x95, 0x06, /* Report Count (6), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA1, /* Report ID (161), */ + 0x09, 0x41, /* Usage (41h), */ + 0x95, 0x01, /* Report Count (1), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA2, /* Report ID (162), */ + 0x09, 0x42, /* Usage (42h), */ + 0x95, 0x01, /* Report Count (1), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA3, /* Report ID (163), */ + 0x09, 0x43, /* Usage (43h), */ + 0x95, 0x30, /* Report Count (48), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA4, /* Report ID (164), */ + 0x09, 0x44, /* Usage (44h), */ + 0x95, 0x0D, /* Report Count (13), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA5, /* Report ID (165), */ + 0x09, 0x45, /* Usage (45h), */ + 0x95, 0x15, /* Report Count (21), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA6, /* Report ID (166), */ + 0x09, 0x46, /* Usage (46h), */ + 0x95, 0x15, /* Report Count (21), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xF0, /* Report ID (240), */ + 0x09, 0x47, /* Usage (47h), */ + 0x95, 0x3F, /* Report Count (63), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xF1, /* Report ID (241), */ + 0x09, 0x48, /* Usage (48h), */ + 0x95, 0x3F, /* Report Count (63), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xF2, /* Report ID (242), */ + 0x09, 0x49, /* Usage (49h), */ + 0x95, 0x0F, /* Report Count (15), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA7, /* Report ID (167), */ + 0x09, 0x4A, /* Usage (4Ah), */ + 0x95, 0x01, /* Report Count (1), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA8, /* Report ID (168), */ + 0x09, 0x4B, /* Usage (4Bh), */ + 0x95, 0x01, /* Report Count (1), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA9, /* Report ID (169), */ + 0x09, 0x4C, /* Usage (4Ch), */ + 0x95, 0x08, /* Report Count (8), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xAA, /* Report ID (170), */ + 0x09, 0x4E, /* Usage (4Eh), */ + 0x95, 0x01, /* Report Count (1), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xAB, /* Report ID (171), */ + 0x09, 0x4F, /* Usage (4Fh), */ + 0x95, 0x39, /* Report Count (57), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xAC, /* Report ID (172), */ + 0x09, 0x50, /* Usage (50h), */ + 0x95, 0x39, /* Report Count (57), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xAD, /* Report ID (173), */ + 0x09, 0x51, /* Usage (51h), */ + 0x95, 0x0B, /* Report Count (11), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xAE, /* Report ID (174), */ + 0x09, 0x52, /* Usage (52h), */ + 0x95, 0x01, /* Report Count (1), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xAF, /* Report ID (175), */ + 0x09, 0x53, /* Usage (53h), */ + 0x95, 0x02, /* Report Count (2), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xB0, /* Report ID (176), */ + 0x09, 0x54, /* Usage (54h), */ + 0x95, 0x3F, /* Report Count (63), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0 /* End Collection */ +}; + +/* + * The default behavior of the Dualshock 4 is to send reports using report + * type 1 when running over Bluetooth. However, as soon as it receives a + * report of type 17 to set the LEDs or rumble it starts returning it's state + * in report 17 instead of 1. Since report 17 is undefined in the default HID + * descriptor the button and axis definitions must be moved to report 17 or + * the HID layer won't process the received input once a report is sent. + */ +static u8 dualshock4_bt_rdesc[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x05, /* Usage (Gamepad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x01, /* Report ID (1), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x0A, /* Report Count (9), */ + 0x81, 0x02, /* Input (Variable), */ + 0x06, 0x04, 0xFF, /* Usage Page (FF04h), */ + 0x85, 0x02, /* Report ID (2), */ + 0x09, 0x24, /* Usage (24h), */ + 0x95, 0x24, /* Report Count (36), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA3, /* Report ID (163), */ + 0x09, 0x25, /* Usage (25h), */ + 0x95, 0x30, /* Report Count (48), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x05, /* Report ID (5), */ + 0x09, 0x26, /* Usage (26h), */ + 0x95, 0x28, /* Report Count (40), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x06, /* Report ID (6), */ + 0x09, 0x27, /* Usage (27h), */ + 0x95, 0x34, /* Report Count (52), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x07, /* Report ID (7), */ + 0x09, 0x28, /* Usage (28h), */ + 0x95, 0x30, /* Report Count (48), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x08, /* Report ID (8), */ + 0x09, 0x29, /* Usage (29h), */ + 0x95, 0x2F, /* Report Count (47), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x06, 0x03, 0xFF, /* Usage Page (FF03h), */ + 0x85, 0x03, /* Report ID (3), */ + 0x09, 0x21, /* Usage (21h), */ + 0x95, 0x26, /* Report Count (38), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x04, /* Report ID (4), */ + 0x09, 0x22, /* Usage (22h), */ + 0x95, 0x2E, /* Report Count (46), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xF0, /* Report ID (240), */ + 0x09, 0x47, /* Usage (47h), */ + 0x95, 0x3F, /* Report Count (63), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xF1, /* Report ID (241), */ + 0x09, 0x48, /* Usage (48h), */ + 0x95, 0x3F, /* Report Count (63), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xF2, /* Report ID (242), */ + 0x09, 0x49, /* Usage (49h), */ + 0x95, 0x0F, /* Report Count (15), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x11, /* Report ID (17), */ + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x09, 0x20, /* Usage (20h), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x02, /* Input (Variable), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x09, 0x32, /* Usage (Z), */ + 0x09, 0x35, /* Usage (Rz), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x04, /* Report Count (4), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x39, /* Usage (Hat Switch), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x07, /* Logical Maximum (7), */ + 0x75, 0x04, /* Report Size (4), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x42, /* Input (Variable, Null State), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x0E, /* Usage Maximum (0Eh), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x0E, /* Report Count (14), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x06, /* Report Size (6), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x33, /* Usage (Rx), */ + 0x09, 0x34, /* Usage (Ry), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x02, /* Input (Variable), */ + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x09, 0x20, /* Usage (20h), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x19, 0x40, /* Usage Minimum (40h), */ + 0x29, 0x42, /* Usage Maximum (42h), */ + 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */ + 0x26, 0x00, 0x7F, /* Logical Maximum (32767), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x19, 0x43, /* Usage Minimum (43h), */ + 0x29, 0x45, /* Usage Maximum (45h), */ + 0x16, 0xFF, 0xBF, /* Logical Minimum (-16385), */ + 0x26, 0x00, 0x40, /* Logical Maximum (16384), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x09, 0x20, /* Usage (20h), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x31, /* Report Count (51), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x21, /* Usage (21h), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x4D, /* Report Count (77), */ + 0x91, 0x02, /* Output (Variable), */ + 0x85, 0x12, /* Report ID (18), */ + 0x09, 0x22, /* Usage (22h), */ + 0x95, 0x8D, /* Report Count (141), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x23, /* Usage (23h), */ + 0x91, 0x02, /* Output (Variable), */ + 0x85, 0x13, /* Report ID (19), */ + 0x09, 0x24, /* Usage (24h), */ + 0x95, 0xCD, /* Report Count (205), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x25, /* Usage (25h), */ + 0x91, 0x02, /* Output (Variable), */ + 0x85, 0x14, /* Report ID (20), */ + 0x09, 0x26, /* Usage (26h), */ + 0x96, 0x0D, 0x01, /* Report Count (269), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x27, /* Usage (27h), */ + 0x91, 0x02, /* Output (Variable), */ + 0x85, 0x15, /* Report ID (21), */ + 0x09, 0x28, /* Usage (28h), */ + 0x96, 0x4D, 0x01, /* Report Count (333), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x29, /* Usage (29h), */ + 0x91, 0x02, /* Output (Variable), */ + 0x85, 0x16, /* Report ID (22), */ + 0x09, 0x2A, /* Usage (2Ah), */ + 0x96, 0x8D, 0x01, /* Report Count (397), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x2B, /* Usage (2Bh), */ + 0x91, 0x02, /* Output (Variable), */ + 0x85, 0x17, /* Report ID (23), */ + 0x09, 0x2C, /* Usage (2Ch), */ + 0x96, 0xCD, 0x01, /* Report Count (461), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x2D, /* Usage (2Dh), */ + 0x91, 0x02, /* Output (Variable), */ + 0x85, 0x18, /* Report ID (24), */ + 0x09, 0x2E, /* Usage (2Eh), */ + 0x96, 0x0D, 0x02, /* Report Count (525), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x2F, /* Usage (2Fh), */ + 0x91, 0x02, /* Output (Variable), */ + 0x85, 0x19, /* Report ID (25), */ + 0x09, 0x30, /* Usage (30h), */ + 0x96, 0x22, 0x02, /* Report Count (546), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (31h), */ + 0x91, 0x02, /* Output (Variable), */ + 0x06, 0x80, 0xFF, /* Usage Page (FF80h), */ + 0x85, 0x82, /* Report ID (130), */ + 0x09, 0x22, /* Usage (22h), */ + 0x95, 0x3F, /* Report Count (63), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x83, /* Report ID (131), */ + 0x09, 0x23, /* Usage (23h), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x84, /* Report ID (132), */ + 0x09, 0x24, /* Usage (24h), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x90, /* Report ID (144), */ + 0x09, 0x30, /* Usage (30h), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x91, /* Report ID (145), */ + 0x09, 0x31, /* Usage (31h), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x92, /* Report ID (146), */ + 0x09, 0x32, /* Usage (32h), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0x93, /* Report ID (147), */ + 0x09, 0x33, /* Usage (33h), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA0, /* Report ID (160), */ + 0x09, 0x40, /* Usage (40h), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0x85, 0xA4, /* Report ID (164), */ + 0x09, 0x44, /* Usage (44h), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0 /* End Collection */ +}; + +static __u8 ps3remote_rdesc[] = { + 0x05, 0x01, /* GUsagePage Generic Desktop */ + 0x09, 0x05, /* LUsage 0x05 [Game Pad] */ + 0xA1, 0x01, /* MCollection Application (mouse, keyboard) */ + + /* Use collection 1 for joypad buttons */ + 0xA1, 0x02, /* MCollection Logical (interrelated data) */ + + /* Ignore the 1st byte, maybe it is used for a controller + * number but it's not needed for correct operation */ + 0x75, 0x08, /* GReportSize 0x08 [8] */ + 0x95, 0x01, /* GReportCount 0x01 [1] */ + 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */ + + /* Bytes from 2nd to 4th are a bitmap for joypad buttons, for these + * buttons multiple keypresses are allowed */ + 0x05, 0x09, /* GUsagePage Button */ + 0x19, 0x01, /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */ + 0x29, 0x18, /* LUsageMaximum 0x18 [Button 24] */ + 0x14, /* GLogicalMinimum [0] */ + 0x25, 0x01, /* GLogicalMaximum 0x01 [1] */ + 0x75, 0x01, /* GReportSize 0x01 [1] */ + 0x95, 0x18, /* GReportCount 0x18 [24] */ + 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */ + + 0xC0, /* MEndCollection */ + + /* Use collection 2 for remote control buttons */ + 0xA1, 0x02, /* MCollection Logical (interrelated data) */ + + /* 5th byte is used for remote control buttons */ + 0x05, 0x09, /* GUsagePage Button */ + 0x18, /* LUsageMinimum [No button pressed] */ + 0x29, 0xFE, /* LUsageMaximum 0xFE [Button 254] */ + 0x14, /* GLogicalMinimum [0] */ + 0x26, 0xFE, 0x00, /* GLogicalMaximum 0x00FE [254] */ + 0x75, 0x08, /* GReportSize 0x08 [8] */ + 0x95, 0x01, /* GReportCount 0x01 [1] */ + 0x80, /* MInput */ + + /* Ignore bytes from 6th to 11th, 6th to 10th are always constant at + * 0xff and 11th is for press indication */ + 0x75, 0x08, /* GReportSize 0x08 [8] */ + 0x95, 0x06, /* GReportCount 0x06 [6] */ + 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */ + + /* 12th byte is for battery strength */ + 0x05, 0x06, /* GUsagePage Generic Device Controls */ + 0x09, 0x20, /* LUsage 0x20 [Battery Strength] */ + 0x14, /* GLogicalMinimum [0] */ + 0x25, 0x05, /* GLogicalMaximum 0x05 [5] */ + 0x75, 0x08, /* GReportSize 0x08 [8] */ + 0x95, 0x01, /* GReportCount 0x01 [1] */ + 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */ + + 0xC0, /* MEndCollection */ + + 0xC0 /* MEndCollection [Game Pad] */ +}; + +static const unsigned int ps3remote_keymap_joypad_buttons[] = { + [0x01] = KEY_SELECT, + [0x02] = BTN_THUMBL, /* L3 */ + [0x03] = BTN_THUMBR, /* R3 */ + [0x04] = BTN_START, + [0x05] = KEY_UP, + [0x06] = KEY_RIGHT, + [0x07] = KEY_DOWN, + [0x08] = KEY_LEFT, + [0x09] = BTN_TL2, /* L2 */ + [0x0a] = BTN_TR2, /* R2 */ + [0x0b] = BTN_TL, /* L1 */ + [0x0c] = BTN_TR, /* R1 */ + [0x0d] = KEY_OPTION, /* options/triangle */ + [0x0e] = KEY_BACK, /* back/circle */ + [0x0f] = BTN_0, /* cross */ + [0x10] = KEY_SCREEN, /* view/square */ + [0x11] = KEY_HOMEPAGE, /* PS button */ + [0x14] = KEY_ENTER, +}; +static const unsigned int ps3remote_keymap_remote_buttons[] = { + [0x00] = KEY_1, + [0x01] = KEY_2, + [0x02] = KEY_3, + [0x03] = KEY_4, + [0x04] = KEY_5, + [0x05] = KEY_6, + [0x06] = KEY_7, + [0x07] = KEY_8, + [0x08] = KEY_9, + [0x09] = KEY_0, + [0x0e] = KEY_ESC, /* return */ + [0x0f] = KEY_CLEAR, + [0x16] = KEY_EJECTCD, + [0x1a] = KEY_MENU, /* top menu */ + [0x28] = KEY_TIME, + [0x30] = KEY_PREVIOUS, + [0x31] = KEY_NEXT, + [0x32] = KEY_PLAY, + [0x33] = KEY_REWIND, /* scan back */ + [0x34] = KEY_FORWARD, /* scan forward */ + [0x38] = KEY_STOP, + [0x39] = KEY_PAUSE, + [0x40] = KEY_CONTEXT_MENU, /* pop up/menu */ + [0x60] = KEY_FRAMEBACK, /* slow/step back */ + [0x61] = KEY_FRAMEFORWARD, /* slow/step forward */ + [0x63] = KEY_SUBTITLE, + [0x64] = KEY_AUDIO, + [0x65] = KEY_ANGLE, + [0x70] = KEY_INFO, /* display */ + [0x80] = KEY_BLUE, + [0x81] = KEY_RED, + [0x82] = KEY_GREEN, + [0x83] = KEY_YELLOW, +}; + +static const unsigned int buzz_keymap[] = { + /* + * The controller has 4 remote buzzers, each with one LED and 5 + * buttons. + * + * We use the mapping chosen by the controller, which is: + * + * Key Offset + * ------------------- + * Buzz 1 + * Blue 5 + * Orange 4 + * Green 3 + * Yellow 2 + * + * So, for example, the orange button on the third buzzer is mapped to + * BTN_TRIGGER_HAPPY14 + */ + [ 1] = BTN_TRIGGER_HAPPY1, + [ 2] = BTN_TRIGGER_HAPPY2, + [ 3] = BTN_TRIGGER_HAPPY3, + [ 4] = BTN_TRIGGER_HAPPY4, + [ 5] = BTN_TRIGGER_HAPPY5, + [ 6] = BTN_TRIGGER_HAPPY6, + [ 7] = BTN_TRIGGER_HAPPY7, + [ 8] = BTN_TRIGGER_HAPPY8, + [ 9] = BTN_TRIGGER_HAPPY9, + [10] = BTN_TRIGGER_HAPPY10, + [11] = BTN_TRIGGER_HAPPY11, + [12] = BTN_TRIGGER_HAPPY12, + [13] = BTN_TRIGGER_HAPPY13, + [14] = BTN_TRIGGER_HAPPY14, + [15] = BTN_TRIGGER_HAPPY15, + [16] = BTN_TRIGGER_HAPPY16, + [17] = BTN_TRIGGER_HAPPY17, + [18] = BTN_TRIGGER_HAPPY18, + [19] = BTN_TRIGGER_HAPPY19, + [20] = BTN_TRIGGER_HAPPY20, +}; + +static enum power_supply_property sony_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_STATUS, +}; + +struct sixaxis_led { + __u8 time_enabled; /* the total time the led is active (0xff means forever) */ + __u8 duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */ + __u8 enabled; + __u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */ + __u8 duty_on; /* % of duty_length the led is on (0xff mean 100%) */ +} __packed; + +struct sixaxis_rumble { + __u8 padding; + __u8 right_duration; /* Right motor duration (0xff means forever) */ + __u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */ + __u8 left_duration; /* Left motor duration (0xff means forever) */ + __u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */ +} __packed; + +struct sixaxis_output_report { + __u8 report_id; + struct sixaxis_rumble rumble; + __u8 padding[4]; + __u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */ + struct sixaxis_led led[4]; /* LEDx at (4 - x) */ + struct sixaxis_led _reserved; /* LED5, not actually soldered */ +} __packed; + +union sixaxis_output_report_01 { + struct sixaxis_output_report data; + __u8 buf[36]; +}; + +static spinlock_t sony_dev_list_lock; +static LIST_HEAD(sony_device_list); +static DEFINE_IDA(sony_device_id_allocator); + struct sony_sc { + spinlock_t lock; + struct list_head list_node; + struct hid_device *hdev; + struct led_classdev *leds[MAX_LEDS]; unsigned long quirks; + struct work_struct state_worker; + struct power_supply battery; + int device_id; + +#ifdef CONFIG_SONY_FF + __u8 left; + __u8 right; +#endif + + __u8 mac_address[6]; + __u8 worker_initialized; + __u8 cable_state; + __u8 battery_charging; + __u8 battery_capacity; + __u8 led_state[MAX_LEDS]; + __u8 led_delay_on[MAX_LEDS]; + __u8 led_delay_off[MAX_LEDS]; + __u8 led_count; }; +static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + *rsize = sizeof(ps3remote_rdesc); + return ps3remote_rdesc; +} + +static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + unsigned int key = usage->hid & HID_USAGE; + + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON) + return -1; + + switch (usage->collection_index) { + case 1: + if (key >= ARRAY_SIZE(ps3remote_keymap_joypad_buttons)) + return -1; + + key = ps3remote_keymap_joypad_buttons[key]; + if (!key) + return -1; + break; + case 2: + if (key >= ARRAY_SIZE(ps3remote_keymap_remote_buttons)) + return -1; + + key = ps3remote_keymap_remote_buttons[key]; + if (!key) + return -1; + break; + default: + return -1; + } + + hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); + return 1; +} + + /* Sony Vaio VGX has wrongly mouse pointer declared as constant */ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { 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; } + /* + * The default Dualshock 4 USB descriptor doesn't assign + * the gyroscope values to corresponding axes so we need a + * modified one. + */ + if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && *rsize == 467) { + hid_info(hdev, "Using modified Dualshock 4 report descriptor with gyroscope axes\n"); + rdesc = dualshock4_usb_rdesc; + *rsize = sizeof(dualshock4_usb_rdesc); + } else if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) && *rsize == 357) { + hid_info(hdev, "Using modified Dualshock 4 Bluetooth report descriptor\n"); + rdesc = dualshock4_bt_rdesc; + *rsize = sizeof(dualshock4_bt_rdesc); + } + /* The HID descriptor exposed over BT has a trailing zero byte */ if ((((sc->quirks & SIXAXIS_CONTROLLER_USB) && *rsize == 148) || ((sc->quirks & SIXAXIS_CONTROLLER_BT) && *rsize == 149)) && @@ -56,66 +864,180 @@ 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); } + + if (sc->quirks & PS3REMOTE) + return ps3remote_fixup(hdev, rdesc, rsize); + return rdesc; } +static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size) +{ + static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 }; + unsigned long flags; + __u8 cable_state, battery_capacity, battery_charging; + + /* + * The sixaxis is charging if the battery value is 0xee + * and it is fully charged if the value is 0xef. + * It does not report the actual level while charging so it + * is set to 100% while charging is in progress. + */ + if (rd[30] >= 0xee) { + battery_capacity = 100; + battery_charging = !(rd[30] & 0x01); + } else { + __u8 index = rd[30] <= 5 ? rd[30] : 5; + battery_capacity = sixaxis_battery_capacity[index]; + battery_charging = 0; + } + cable_state = !(rd[31] & 0x04); + + spin_lock_irqsave(&sc->lock, flags); + sc->cable_state = cable_state; + sc->battery_capacity = battery_capacity; + sc->battery_charging = battery_charging; + spin_unlock_irqrestore(&sc->lock, flags); +} + +static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size) +{ + struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, + struct hid_input, list); + struct input_dev *input_dev = hidinput->input; + unsigned long flags; + int n, offset; + __u8 cable_state, battery_capacity, battery_charging; + + /* + * Battery and touchpad data starts at byte 30 in the USB report and + * 32 in Bluetooth report. + */ + offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 30 : 32; + + /* + * The lower 4 bits of byte 30 contain the battery level + * and the 5th bit contains the USB cable state. + */ + cable_state = (rd[offset] >> 4) & 0x01; + battery_capacity = rd[offset] & 0x0F; + + /* + * When a USB power source is connected the battery level ranges from + * 0 to 10, and when running on battery power it ranges from 0 to 9. + * A battery level above 10 when plugged in means charge completed. + */ + if (!cable_state || battery_capacity > 10) + battery_charging = 0; + else + battery_charging = 1; + + if (!cable_state) + battery_capacity++; + if (battery_capacity > 10) + battery_capacity = 10; + + battery_capacity *= 10; + + spin_lock_irqsave(&sc->lock, flags); + sc->cable_state = cable_state; + sc->battery_capacity = battery_capacity; + sc->battery_charging = battery_charging; + spin_unlock_irqrestore(&sc->lock, flags); + + offset += 5; + + /* + * The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB + * and 37 on Bluetooth. + * The first 7 bits of the first byte is a counter and bit 8 is a touch + * indicator that is 0 when pressed and 1 when not pressed. + * The next 3 bytes are two 12 bit touch coordinates, X and Y. + * The data for the second touch is in the same format and immediatly + * follows the data for the first. + */ + for (n = 0; n < 2; n++) { + __u16 x, y; + + x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8); + y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4); + + input_mt_slot(input_dev, n); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + !(rd[offset] >> 7)); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + + offset += 4; + } +} + static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, __u8 *rd, int size) { struct sony_sc *sc = hid_get_drvdata(hdev); - /* Sixaxis HID report has acclerometers/gyro with MSByte first, this + /* + * Sixaxis HID report has acclerometers/gyro with MSByte first, this * has to be BYTE_SWAPPED before passing up to joystick interface */ - if ((sc->quirks & (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)) && - rd[0] == 0x01 && size == 49) { + if ((sc->quirks & SIXAXIS_CONTROLLER) && rd[0] == 0x01 && size == 49) { swap(rd[41], rd[42]); swap(rd[43], rd[44]); swap(rd[45], rd[46]); swap(rd[47], rd[48]); + + sixaxis_parse_report(sc, rd, size); + } else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 && + size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) + && rd[0] == 0x11 && size == 78)) { + dualshock4_parse_report(sc, rd, size); } return 0; } -/* - * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP - * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() - * so we need to override that forcing HID Output Reports on the Control EP. - * - * There is also another issue about HID Output Reports via USB, the Sixaxis - * does not want the report_id as part of the data packet, so we have to - * discard buf[0] when sending the actual control message, even for numbered - * reports, humpf! - */ -static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, - size_t count, unsigned char report_type) +static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) { - struct usb_interface *intf = to_usb_interface(hid->dev.parent); - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_host_interface *interface = intf->cur_altsetting; - int report_id = buf[0]; - int ret; + struct sony_sc *sc = hid_get_drvdata(hdev); - if (report_type == HID_OUTPUT_REPORT) { - /* Don't send the Report ID */ - buf++; - count--; - } + if (sc->quirks & BUZZ_CONTROLLER) { + unsigned int key = usage->hid & HID_USAGE; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - HID_REQ_SET_REPORT, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - ((report_type + 1) << 8) | report_id, - interface->desc.bInterfaceNumber, buf, count, - USB_CTRL_SET_TIMEOUT); + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON) + return -1; - /* Count also the Report ID, in case of an Output report. */ - if (ret > 0 && report_type == HID_OUTPUT_REPORT) - ret++; + switch (usage->collection_index) { + case 1: + if (key >= ARRAY_SIZE(buzz_keymap)) + return -1; - return ret; + key = buzz_keymap[key]; + if (!key) + return -1; + break; + default: + return -1; + } + + hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); + return 1; + } + + if (sc->quirks & PS3REMOTE) + return ps3remote_mapping(hdev, hi, field, usage, bit, max); + + /* Let hid-core decide for the others */ + return 0; } /* @@ -125,21 +1047,15 @@ static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, */ static int sixaxis_set_operational_usb(struct hid_device *hdev) { - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - struct usb_device *dev = interface_to_usbdev(intf); - __u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber; int ret; char *buf = kmalloc(18, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - HID_REQ_GET_REPORT, - USB_DIR_IN | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, - (3 << 8) | 0xf2, ifnum, buf, 17, - USB_CTRL_GET_TIMEOUT); + ret = hid_hw_raw_request(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); + if (ret < 0) hid_err(hdev, "can't set operational mode\n"); @@ -151,7 +1067,742 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) static int sixaxis_set_operational_bt(struct hid_device *hdev) { unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 }; - return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); + return hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); +} + +/* + * Requesting feature report 0x02 in Bluetooth mode changes the state of the + * controller so that it sends full input reports of type 0x11. + */ +static int dualshock4_set_operational_bt(struct hid_device *hdev) +{ + __u8 buf[37] = { 0 }; + + return hid_hw_raw_request(hdev, 0x02, buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); +} + +static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS]) +{ + static const __u8 sixaxis_leds[10][4] = { + { 0x01, 0x00, 0x00, 0x00 }, + { 0x00, 0x01, 0x00, 0x00 }, + { 0x00, 0x00, 0x01, 0x00 }, + { 0x00, 0x00, 0x00, 0x01 }, + { 0x01, 0x00, 0x00, 0x01 }, + { 0x00, 0x01, 0x00, 0x01 }, + { 0x00, 0x00, 0x01, 0x01 }, + { 0x01, 0x00, 0x01, 0x01 }, + { 0x00, 0x01, 0x01, 0x01 }, + { 0x01, 0x01, 0x01, 0x01 } + }; + + BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0])); + + if (id < 0) + return; + + id %= 10; + memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id])); +} + +static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS]) +{ + /* The first 4 color/index entries match what the PS4 assigns */ + static const __u8 color_code[7][3] = { + /* Blue */ { 0x00, 0x00, 0x01 }, + /* Red */ { 0x01, 0x00, 0x00 }, + /* Green */ { 0x00, 0x01, 0x00 }, + /* Pink */ { 0x02, 0x00, 0x01 }, + /* Orange */ { 0x02, 0x01, 0x00 }, + /* Teal */ { 0x00, 0x01, 0x01 }, + /* White */ { 0x01, 0x01, 0x01 } + }; + + BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0])); + + if (id < 0) + return; + + id %= 7; + memcpy(values, color_code[id], sizeof(color_code[id])); +} + +static void buzz_set_leds(struct hid_device *hdev, const __u8 *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] = 0x00; + value[1] = leds[0] ? 0xff : 0x00; + value[2] = leds[1] ? 0xff : 0x00; + value[3] = leds[2] ? 0xff : 0x00; + value[4] = leds[3] ? 0xff : 0x00; + value[5] = 0x00; + value[6] = 0x00; + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); +} + +static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count) +{ + int n; + + BUG_ON(count > MAX_LEDS); + + if (sc->quirks & BUZZ_CONTROLLER && count == 4) { + buzz_set_leds(sc->hdev, leds); + } else { + for (n = 0; n < count; n++) + sc->led_state[n] = leds[n]; + schedule_work(&sc->state_worker); + } +} + +static void sony_led_set_brightness(struct led_classdev *led, + enum led_brightness value) +{ + struct device *dev = led->dev->parent; + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct sony_sc *drv_data; + + int n; + int force_update; + + drv_data = hid_get_drvdata(hdev); + if (!drv_data) { + hid_err(hdev, "No device data\n"); + return; + } + + /* + * The Sixaxis on USB will override any LED settings sent to it + * and keep flashing all of the LEDs until the PS button is pressed. + * Updates, even if redundant, must be always be sent to the + * controller to avoid having to toggle the state of an LED just to + * stop the flashing later on. + */ + force_update = !!(drv_data->quirks & SIXAXIS_CONTROLLER_USB); + + for (n = 0; n < drv_data->led_count; n++) { + if (led == drv_data->leds[n] && (force_update || + (value != drv_data->led_state[n] || + drv_data->led_delay_on[n] || + drv_data->led_delay_off[n]))) { + + drv_data->led_state[n] = value; + + /* Setting the brightness stops the blinking */ + drv_data->led_delay_on[n] = 0; + drv_data->led_delay_off[n] = 0; + + sony_set_leds(drv_data, drv_data->led_state, + drv_data->led_count); + break; + } + } +} + +static enum led_brightness sony_led_get_brightness(struct led_classdev *led) +{ + struct device *dev = led->dev->parent; + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct sony_sc *drv_data; + + int n; + + drv_data = hid_get_drvdata(hdev); + if (!drv_data) { + hid_err(hdev, "No device data\n"); + return LED_OFF; + } + + for (n = 0; n < drv_data->led_count; n++) { + if (led == drv_data->leds[n]) + return drv_data->led_state[n]; + } + + return LED_OFF; +} + +static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on, + unsigned long *delay_off) +{ + struct device *dev = led->dev->parent; + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct sony_sc *drv_data = hid_get_drvdata(hdev); + int n; + __u8 new_on, new_off; + + if (!drv_data) { + hid_err(hdev, "No device data\n"); + return -EINVAL; + } + + /* Max delay is 255 deciseconds or 2550 milliseconds */ + if (*delay_on > 2550) + *delay_on = 2550; + if (*delay_off > 2550) + *delay_off = 2550; + + /* Blink at 1 Hz if both values are zero */ + if (!*delay_on && !*delay_off) + *delay_on = *delay_off = 500; + + new_on = *delay_on / 10; + new_off = *delay_off / 10; + + for (n = 0; n < drv_data->led_count; n++) { + if (led == drv_data->leds[n]) + break; + } + + /* This LED is not registered on this device */ + if (n >= drv_data->led_count) + return -EINVAL; + + /* Don't schedule work if the values didn't change */ + if (new_on != drv_data->led_delay_on[n] || + new_off != drv_data->led_delay_off[n]) { + drv_data->led_delay_on[n] = new_on; + drv_data->led_delay_off[n] = new_off; + schedule_work(&drv_data->state_worker); + } + + return 0; +} + +static void sony_leds_remove(struct sony_sc *sc) +{ + struct led_classdev *led; + int n; + + BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); + + for (n = 0; n < sc->led_count; n++) { + led = sc->leds[n]; + sc->leds[n] = NULL; + if (!led) + continue; + led_classdev_unregister(led); + kfree(led); + } + + sc->led_count = 0; +} + +static int sony_leds_init(struct sony_sc *sc) +{ + struct hid_device *hdev = sc->hdev; + int n, ret = 0; + int use_ds4_names; + struct led_classdev *led; + size_t name_sz; + char *name; + size_t name_len; + const char *name_fmt; + static const char * const ds4_name_str[] = { "red", "green", "blue", + "global" }; + __u8 initial_values[MAX_LEDS] = { 0 }; + __u8 max_brightness[MAX_LEDS] = { 1 }; + __u8 use_hw_blink[MAX_LEDS] = { 0 }; + + BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); + + if (sc->quirks & BUZZ_CONTROLLER) { + sc->led_count = 4; + use_ds4_names = 0; + name_len = strlen("::buzz#"); + name_fmt = "%s::buzz%d"; + /* Validate expected report characteristics. */ + if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7)) + return -ENODEV; + } else if (sc->quirks & DUALSHOCK4_CONTROLLER) { + dualshock4_set_leds_from_id(sc->device_id, initial_values); + initial_values[3] = 1; + sc->led_count = 4; + memset(max_brightness, 255, 3); + use_hw_blink[3] = 1; + use_ds4_names = 1; + name_len = 0; + name_fmt = "%s:%s"; + } else { + sixaxis_set_leds_from_id(sc->device_id, initial_values); + sc->led_count = 4; + memset(use_hw_blink, 1, 4); + use_ds4_names = 0; + name_len = strlen("::sony#"); + name_fmt = "%s::sony%d"; + } + + /* + * Clear LEDs as we have no way of reading their initial state. This is + * only relevant if the driver is loaded after somebody actively set the + * LEDs to on + */ + sony_set_leds(sc, initial_values, sc->led_count); + + name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1; + + for (n = 0; n < sc->led_count; n++) { + + if (use_ds4_names) + name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2; + + led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL); + if (!led) { + hid_err(hdev, "Couldn't allocate memory for LED %d\n", n); + ret = -ENOMEM; + goto error_leds; + } + + name = (void *)(&led[1]); + if (use_ds4_names) + snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), + ds4_name_str[n]); + else + snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1); + led->name = name; + led->brightness = initial_values[n]; + led->max_brightness = max_brightness[n]; + led->brightness_get = sony_led_get_brightness; + led->brightness_set = sony_led_set_brightness; + + if (use_hw_blink[n]) + led->blink_set = sony_led_blink_set; + + sc->leds[n] = led; + + ret = led_classdev_register(&hdev->dev, led); + if (ret) { + hid_err(hdev, "Failed to register LED %d\n", n); + sc->leds[n] = NULL; + kfree(led); + goto error_leds; + } + } + + return ret; + +error_leds: + sony_leds_remove(sc); + + return ret; +} + +static void sixaxis_state_worker(struct work_struct *work) +{ + struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); + int n; + union sixaxis_output_report_01 report = { + .buf = { + 0x01, + 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00 + } + }; + +#ifdef CONFIG_SONY_FF + report.data.rumble.right_motor_on = sc->right ? 1 : 0; + report.data.rumble.left_motor_force = sc->left; +#endif + + report.data.leds_bitmap |= sc->led_state[0] << 1; + report.data.leds_bitmap |= sc->led_state[1] << 2; + report.data.leds_bitmap |= sc->led_state[2] << 3; + report.data.leds_bitmap |= sc->led_state[3] << 4; + + /* Set flag for all leds off, required for 3rd party INTEC controller */ + if ((report.data.leds_bitmap & 0x1E) == 0) + report.data.leds_bitmap |= 0x20; + + /* + * The LEDs in the report are indexed in reverse order to their + * corresponding light on the controller. + * Index 0 = LED 4, index 1 = LED 3, etc... + * + * In the case of both delay values being zero (blinking disabled) the + * default report values should be used or the controller LED will be + * always off. + */ + for (n = 0; n < 4; n++) { + if (sc->led_delay_on[n] || sc->led_delay_off[n]) { + report.data.led[3 - n].duty_off = sc->led_delay_off[n]; + report.data.led[3 - n].duty_on = sc->led_delay_on[n]; + } + } + + hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf, + sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); +} + +static void dualshock4_state_worker(struct work_struct *work) +{ + struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); + struct hid_device *hdev = sc->hdev; + int offset; + + __u8 buf[78] = { 0 }; + + if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { + buf[0] = 0x05; + buf[1] = 0xFF; + offset = 4; + } else { + buf[0] = 0x11; + buf[1] = 0xB0; + buf[3] = 0x0F; + offset = 6; + } + +#ifdef CONFIG_SONY_FF + buf[offset++] = sc->right; + buf[offset++] = sc->left; +#else + offset += 2; +#endif + + /* LED 3 is the global control */ + if (sc->led_state[3]) { + buf[offset++] = sc->led_state[0]; + buf[offset++] = sc->led_state[1]; + buf[offset++] = sc->led_state[2]; + } else { + offset += 3; + } + + /* If both delay values are zero the DualShock 4 disables blinking. */ + buf[offset++] = sc->led_delay_on[3]; + buf[offset++] = sc->led_delay_off[3]; + + if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) + hid_hw_output_report(hdev, buf, 32); + else + hid_hw_raw_request(hdev, 0x11, buf, 78, + HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); +} + +#ifdef CONFIG_SONY_FF +static int sony_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct sony_sc *sc = hid_get_drvdata(hid); + + if (effect->type != FF_RUMBLE) + return 0; + + sc->left = effect->u.rumble.strong_magnitude / 256; + sc->right = effect->u.rumble.weak_magnitude / 256; + + schedule_work(&sc->state_worker); + return 0; +} + +static int sony_init_ff(struct sony_sc *sc) +{ + struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, + struct hid_input, list); + struct input_dev *input_dev = hidinput->input; + + input_set_capability(input_dev, EV_FF, FF_RUMBLE); + return input_ff_create_memless(input_dev, NULL, sony_play_effect); +} + +#else +static int sony_init_ff(struct sony_sc *sc) +{ + return 0; +} + +#endif + +static int sony_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sony_sc *sc = container_of(psy, struct sony_sc, battery); + unsigned long flags; + int ret = 0; + u8 battery_charging, battery_capacity, cable_state; + + spin_lock_irqsave(&sc->lock, flags); + battery_charging = sc->battery_charging; + battery_capacity = sc->battery_capacity; + cable_state = sc->cable_state; + spin_unlock_irqrestore(&sc->lock, flags); + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = battery_capacity; + break; + case POWER_SUPPLY_PROP_STATUS: + if (battery_charging) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + if (battery_capacity == 100 && cable_state) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int sony_battery_probe(struct sony_sc *sc) +{ + struct hid_device *hdev = sc->hdev; + int ret; + + /* + * Set the default battery level to 100% to avoid low battery warnings + * if the battery is polled before the first device report is received. + */ + sc->battery_capacity = 100; + + sc->battery.properties = sony_battery_props; + sc->battery.num_properties = ARRAY_SIZE(sony_battery_props); + sc->battery.get_property = sony_battery_get_property; + sc->battery.type = POWER_SUPPLY_TYPE_BATTERY; + sc->battery.use_for_apm = 0; + sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR", + sc->mac_address); + if (!sc->battery.name) + return -ENOMEM; + + ret = power_supply_register(&hdev->dev, &sc->battery); + if (ret) { + hid_err(hdev, "Unable to register battery device\n"); + goto err_free; + } + + power_supply_powers(&sc->battery, &hdev->dev); + return 0; + +err_free: + kfree(sc->battery.name); + sc->battery.name = NULL; + return ret; +} + +static void sony_battery_remove(struct sony_sc *sc) +{ + if (!sc->battery.name) + return; + + power_supply_unregister(&sc->battery); + kfree(sc->battery.name); + sc->battery.name = NULL; +} + +static int sony_register_touchpad(struct sony_sc *sc, int touch_count, + int w, int h) +{ + struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, + struct hid_input, list); + struct input_dev *input_dev = hidinput->input; + int ret; + + ret = input_mt_init_slots(input_dev, touch_count, 0); + if (ret < 0) { + hid_err(sc->hdev, "Unable to initialize multi-touch slots\n"); + return ret; + } + + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0); + + return 0; +} + +/* + * If a controller is plugged in via USB while already connected via Bluetooth + * it will show up as two devices. A global list of connected controllers and + * their MAC addresses is maintained to ensure that a device is only connected + * once. + */ +static int sony_check_add_dev_list(struct sony_sc *sc) +{ + struct sony_sc *entry; + unsigned long flags; + int ret; + + spin_lock_irqsave(&sony_dev_list_lock, flags); + + list_for_each_entry(entry, &sony_device_list, list_node) { + ret = memcmp(sc->mac_address, entry->mac_address, + sizeof(sc->mac_address)); + if (!ret) { + ret = -EEXIST; + hid_info(sc->hdev, "controller with MAC address %pMR already connected\n", + sc->mac_address); + goto unlock; + } + } + + ret = 0; + list_add(&(sc->list_node), &sony_device_list); + +unlock: + spin_unlock_irqrestore(&sony_dev_list_lock, flags); + return ret; +} + +static void sony_remove_dev_list(struct sony_sc *sc) +{ + unsigned long flags; + + if (sc->list_node.next) { + spin_lock_irqsave(&sony_dev_list_lock, flags); + list_del(&(sc->list_node)); + spin_unlock_irqrestore(&sony_dev_list_lock, flags); + } +} + +static int sony_get_bt_devaddr(struct sony_sc *sc) +{ + int ret; + + /* HIDP stores the device MAC address as a string in the uniq field. */ + ret = strlen(sc->hdev->uniq); + if (ret != 17) + return -EINVAL; + + ret = sscanf(sc->hdev->uniq, + "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &sc->mac_address[5], &sc->mac_address[4], &sc->mac_address[3], + &sc->mac_address[2], &sc->mac_address[1], &sc->mac_address[0]); + + if (ret != 6) + return -EINVAL; + + return 0; +} + +static int sony_check_add(struct sony_sc *sc) +{ + int n, ret; + + if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) || + (sc->quirks & SIXAXIS_CONTROLLER_BT)) { + /* + * sony_get_bt_devaddr() attempts to parse the Bluetooth MAC + * address from the uniq string where HIDP stores it. + * As uniq cannot be guaranteed to be a MAC address in all cases + * a failure of this function should not prevent the connection. + */ + if (sony_get_bt_devaddr(sc) < 0) { + hid_warn(sc->hdev, "UNIQ does not contain a MAC address; duplicate check skipped\n"); + return 0; + } + } else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { + __u8 buf[7]; + + /* + * The MAC address of a DS4 controller connected via USB can be + * retrieved with feature report 0x81. The address begins at + * offset 1. + */ + ret = hid_hw_raw_request(sc->hdev, 0x81, buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + + if (ret != 7) { + hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n"); + return ret < 0 ? ret : -EINVAL; + } + + memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address)); + } else if (sc->quirks & SIXAXIS_CONTROLLER_USB) { + __u8 buf[18]; + + /* + * The MAC address of a Sixaxis controller connected via USB can + * be retrieved with feature report 0xf2. The address begins at + * offset 4. + */ + ret = hid_hw_raw_request(sc->hdev, 0xf2, buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + + if (ret != 18) { + hid_err(sc->hdev, "failed to retrieve feature report 0xf2 with the Sixaxis MAC address\n"); + return ret < 0 ? ret : -EINVAL; + } + + /* + * The Sixaxis device MAC in the report is big-endian and must + * be byte-swapped. + */ + for (n = 0; n < 6; n++) + sc->mac_address[5-n] = buf[4+n]; + } else { + return 0; + } + + return sony_check_add_dev_list(sc); +} + +static int sony_set_device_id(struct sony_sc *sc) +{ + int ret; + + /* + * Only DualShock 4 or Sixaxis controllers get an id. + * All others are set to -1. + */ + if ((sc->quirks & SIXAXIS_CONTROLLER) || + (sc->quirks & DUALSHOCK4_CONTROLLER)) { + ret = ida_simple_get(&sony_device_id_allocator, 0, 0, + GFP_KERNEL); + if (ret < 0) { + sc->device_id = -1; + return ret; + } + sc->device_id = ret; + } else { + sc->device_id = -1; + } + + return 0; +} + +static void sony_release_device_id(struct sony_sc *sc) +{ + if (sc->device_id >= 0) { + ida_simple_remove(&sony_device_id_allocator, sc->device_id); + sc->device_id = -1; + } +} + +static inline void sony_init_work(struct sony_sc *sc, + void (*worker)(struct work_struct *)) +{ + if (!sc->worker_initialized) + INIT_WORK(&sc->state_worker, worker); + + sc->worker_initialized = 1; +} + +static inline void sony_cancel_work_sync(struct sony_sc *sc) +{ + if (sc->worker_initialized) + cancel_work_sync(&sc->state_worker); } static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) @@ -159,8 +1810,9 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) int ret; unsigned long quirks = id->driver_data; struct sony_sc *sc; + unsigned int connect_mask = HID_CONNECT_DEFAULT; - sc = kzalloc(sizeof(*sc), GFP_KERNEL); + sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); if (sc == NULL) { hid_err(hdev, "can't alloc sony descriptor\n"); return -ENOMEM; @@ -168,44 +1820,148 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) sc->quirks = quirks; hid_set_drvdata(hdev, sc); + sc->hdev = hdev; ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err_free; + return ret; } - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | - HID_CONNECT_HIDDEV_FORCE); + if (sc->quirks & VAIO_RDESC_CONSTANT) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + else if (sc->quirks & SIXAXIS_CONTROLLER_USB) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + else if (sc->quirks & SIXAXIS_CONTROLLER_BT) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + + ret = hid_hw_start(hdev, connect_mask); if (ret) { hid_err(hdev, "hw start failed\n"); - goto err_free; + return ret; + } + + ret = sony_set_device_id(sc); + if (ret < 0) { + hid_err(hdev, "failed to allocate the device id\n"); + goto err_stop; } if (sc->quirks & SIXAXIS_CONTROLLER_USB) { - hdev->hid_output_raw_report = sixaxis_usb_output_raw_report; + /* + * The Sony Sixaxis does not handle HID Output Reports on the + * Interrupt EP like it could, so we need to force HID Output + * Reports to use HID_REQ_SET_REPORT on the Control EP. + * + * There is also another issue about HID Output Reports via USB, + * the Sixaxis does not want the report_id as part of the data + * packet, so we have to discard buf[0] when sending the actual + * control message, even for numbered reports, humpf! + */ + hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; + hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID; ret = sixaxis_set_operational_usb(hdev); - } - else if (sc->quirks & SIXAXIS_CONTROLLER_BT) + sony_init_work(sc, sixaxis_state_worker); + } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) { + /* + * The Sixaxis wants output reports sent on the ctrl endpoint + * when connected via Bluetooth. + */ + hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; ret = sixaxis_set_operational_bt(hdev); - else + sony_init_work(sc, sixaxis_state_worker); + } else if (sc->quirks & DUALSHOCK4_CONTROLLER) { + if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) { + /* + * The DualShock 4 wants output reports sent on the ctrl + * endpoint when connected via Bluetooth. + */ + hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; + ret = dualshock4_set_operational_bt(hdev); + if (ret < 0) { + hid_err(hdev, "failed to set the Dualshock 4 operational mode\n"); + goto err_stop; + } + } + /* + * The Dualshock 4 touchpad supports 2 touches and has a + * resolution of 1920x940. + */ + ret = sony_register_touchpad(sc, 2, 1920, 940); + if (ret < 0) + goto err_stop; + + sony_init_work(sc, dualshock4_state_worker); + } else { ret = 0; + } if (ret < 0) goto err_stop; + ret = sony_check_add(sc); + if (ret < 0) + goto err_stop; + + if (sc->quirks & SONY_LED_SUPPORT) { + ret = sony_leds_init(sc); + if (ret < 0) + goto err_stop; + } + + if (sc->quirks & SONY_BATTERY_SUPPORT) { + ret = sony_battery_probe(sc); + if (ret < 0) + goto err_stop; + + /* Open the device to receive reports with battery info */ + ret = hid_hw_open(hdev); + if (ret < 0) { + hid_err(hdev, "hw open failed\n"); + goto err_stop; + } + } + + if (sc->quirks & SONY_FF_SUPPORT) { + ret = sony_init_ff(sc); + if (ret < 0) + goto err_close; + } + return 0; +err_close: + hid_hw_close(hdev); err_stop: + if (sc->quirks & SONY_LED_SUPPORT) + sony_leds_remove(sc); + if (sc->quirks & SONY_BATTERY_SUPPORT) + sony_battery_remove(sc); + sony_cancel_work_sync(sc); + sony_remove_dev_list(sc); + sony_release_device_id(sc); hid_hw_stop(hdev); -err_free: - kfree(sc); return ret; } static void sony_remove(struct hid_device *hdev) { + struct sony_sc *sc = hid_get_drvdata(hdev); + + if (sc->quirks & SONY_LED_SUPPORT) + sony_leds_remove(sc); + + if (sc->quirks & SONY_BATTERY_SUPPORT) { + hid_hw_close(hdev); + sony_battery_remove(sc); + } + + sony_cancel_work_sync(sc); + + sony_remove_dev_list(sc); + + sony_release_device_id(sc); + hid_hw_stop(hdev); - kfree(hid_get_drvdata(hdev)); } static const struct hid_device_id sony_devices[] = { @@ -217,29 +1973,54 @@ 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 }, + /* Wired Buzz Controller. Reported as Sony Hub from its USB ID and as + * Logitech joystick from the device descriptor. */ + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER), + .driver_data = BUZZ_CONTROLLER }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER), + .driver_data = BUZZ_CONTROLLER }, + /* PS3 BD Remote Control */ + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE), + .driver_data = PS3REMOTE }, + /* Logitech Harmony Adapter for PS3 */ + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3), + .driver_data = PS3REMOTE }, + /* Sony Dualshock 4 controllers for PS4 */ + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), + .driver_data = DUALSHOCK4_CONTROLLER_USB }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), + .driver_data = DUALSHOCK4_CONTROLLER_BT }, { } }; MODULE_DEVICE_TABLE(hid, sony_devices); static struct hid_driver sony_driver = { - .name = "sony", - .id_table = sony_devices, - .probe = sony_probe, - .remove = sony_remove, - .report_fixup = sony_report_fixup, - .raw_event = sony_raw_event + .name = "sony", + .id_table = sony_devices, + .input_mapping = sony_mapping, + .probe = sony_probe, + .remove = sony_remove, + .report_fixup = sony_report_fixup, + .raw_event = sony_raw_event }; static int __init sony_init(void) { + dbg_hid("Sony:%s\n", __func__); + return hid_register_driver(&sony_driver); } static void __exit sony_exit(void) { + dbg_hid("Sony:%s\n", __func__); + + ida_destroy(&sony_device_id_allocator); 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 60201374171..7112f3e832e 100644 --- a/drivers/hid/hid-speedlink.c +++ b/drivers/hid/hid-speedlink.c @@ -3,7 +3,7 @@ * Fixes "jumpy" cursor and removes nonexistent keyboard LEDS from * the HID descriptor. * - * Copyright (c) 2011 Stefan Kriwanek <mail@stefankriwanek.de> + * Copyright (c) 2011, 2013 Stefan Kriwanek <dev@stefankriwanek.de> */ /* @@ -16,10 +16,8 @@ #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h> -#include <linux/usb.h> #include "hid-ids.h" -#include "usbhid/usbhid.h" static const struct hid_device_id speedlink_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE)}, @@ -48,8 +46,13 @@ static int speedlink_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { /* No other conditions due to usage_table. */ - /* Fix "jumpy" cursor (invalid events sent by device). */ - if (value == 256) + + /* This fixes the "jumpy" cursor occuring due to invalid events sent + * by the device. Some devices only send them with value==+256, others + * don't. However, catching abs(value)>=256 is restrictive enough not + * to interfere with devices that were bug-free (has been tested). + */ + if (abs(value) >= 256) return 1; /* Drop useless distance 0 events (on button clicks etc.) as well */ if (value == 0) @@ -73,17 +76,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 00000000000..29f328f411f --- /dev/null +++ b/drivers/hid/hid-steelseries.c @@ -0,0 +1,389 @@ +/* + * 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 "hid-ids.h" + +#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ + (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) +#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 IS_BUILTIN(CONFIG_LEDS_CLASS) || \ + (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) +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; + + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); + + /* 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; + } + + if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) { + ret = -ENODEV; + 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 IS_BUILTIN(CONFIG_LEDS_CLASS) || \ + (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) + .probe = steelseries_srws1_probe, + .remove = steelseries_srws1_remove, +#endif + .report_fixup = steelseries_srws1_report_fixup +}; + +module_hid_driver(steelseries_srws1_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c index 45b4b066a26..87fc91e1c8d 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 00000000000..134be89b15e --- /dev/null +++ b/drivers/hid/hid-thingm.c @@ -0,0 +1,312 @@ +/* + * ThingM blink(1) USB RGB LED driver + * + * Copyright 2013-2014 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/hidraw.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> + +#include "hid-ids.h" + +#define REPORT_ID 1 +#define REPORT_SIZE 9 + +/* Firmware major number of supported devices */ +#define THINGM_MAJOR_MK1 '1' +#define THINGM_MAJOR_MK2 '2' + +struct thingm_fwinfo { + char major; + unsigned numrgb; + unsigned first; +}; + +static const struct thingm_fwinfo thingm_fwinfo[] = { + { + .major = THINGM_MAJOR_MK1, + .numrgb = 1, + .first = 0, + }, { + .major = THINGM_MAJOR_MK2, + .numrgb = 2, + .first = 1, + } +}; + +/* A red, green or blue channel, part of an RGB chip */ +struct thingm_led { + struct thingm_rgb *rgb; + struct led_classdev ldev; + char name[32]; +}; + +/* Basically a WS2812 5050 RGB LED chip */ +struct thingm_rgb { + struct thingm_device *tdev; + struct thingm_led red; + struct thingm_led green; + struct thingm_led blue; + struct work_struct work; + u8 num; +}; + +struct thingm_device { + struct hid_device *hdev; + struct { + char major; + char minor; + } version; + const struct thingm_fwinfo *fwinfo; + struct mutex lock; + struct thingm_rgb *rgb; +}; + +static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE]) +{ + int ret; + + hid_dbg(tdev->hdev, "-> %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n", + buf[0], buf[1], buf[2], buf[3], buf[4], + buf[5], buf[6], buf[7], buf[8]); + + ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + + return ret < 0 ? ret : 0; +} + +static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE]) +{ + int ret; + + ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret < 0) + return ret; + + hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n", + buf[0], buf[1], buf[2], buf[3], buf[4], + buf[5], buf[6], buf[7], buf[8]); + + return 0; +} + +static int thingm_version(struct thingm_device *tdev) +{ + u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 }; + int err; + + err = thingm_send(tdev, buf); + if (err) + return err; + + err = thingm_recv(tdev, buf); + if (err) + return err; + + tdev->version.major = buf[3]; + tdev->version.minor = buf[4]; + + return 0; +} + +static int thingm_write_color(struct thingm_rgb *rgb) +{ + u8 buf[REPORT_SIZE] = { REPORT_ID, 'c', 0, 0, 0, 0, 0, rgb->num, 0 }; + + buf[2] = rgb->red.ldev.brightness; + buf[3] = rgb->green.ldev.brightness; + buf[4] = rgb->blue.ldev.brightness; + + return thingm_send(rgb->tdev, buf); +} + +static void thingm_work(struct work_struct *work) +{ + struct thingm_rgb *rgb = container_of(work, struct thingm_rgb, work); + + mutex_lock(&rgb->tdev->lock); + + if (thingm_write_color(rgb)) + hid_err(rgb->tdev->hdev, "failed to write color\n"); + + mutex_unlock(&rgb->tdev->lock); +} + +static void thingm_led_set(struct led_classdev *ldev, + enum led_brightness brightness) +{ + struct thingm_led *led = container_of(ldev, struct thingm_led, ldev); + + /* the ledclass has already stored the brightness value */ + schedule_work(&led->rgb->work); +} + +static int thingm_init_rgb(struct thingm_rgb *rgb) +{ + const int minor = ((struct hidraw *) rgb->tdev->hdev->hidraw)->minor; + int err; + + /* Register the red diode */ + snprintf(rgb->red.name, sizeof(rgb->red.name), + "thingm%d:red:led%d", minor, rgb->num); + rgb->red.ldev.name = rgb->red.name; + rgb->red.ldev.max_brightness = 255; + rgb->red.ldev.brightness_set = thingm_led_set; + rgb->red.rgb = rgb; + + err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->red.ldev); + if (err) + return err; + + /* Register the green diode */ + snprintf(rgb->green.name, sizeof(rgb->green.name), + "thingm%d:green:led%d", minor, rgb->num); + rgb->green.ldev.name = rgb->green.name; + rgb->green.ldev.max_brightness = 255; + rgb->green.ldev.brightness_set = thingm_led_set; + rgb->green.rgb = rgb; + + err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->green.ldev); + if (err) + goto unregister_red; + + /* Register the blue diode */ + snprintf(rgb->blue.name, sizeof(rgb->blue.name), + "thingm%d:blue:led%d", minor, rgb->num); + rgb->blue.ldev.name = rgb->blue.name; + rgb->blue.ldev.max_brightness = 255; + rgb->blue.ldev.brightness_set = thingm_led_set; + rgb->blue.rgb = rgb; + + err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->blue.ldev); + if (err) + goto unregister_green; + + INIT_WORK(&rgb->work, thingm_work); + + return 0; + +unregister_green: + led_classdev_unregister(&rgb->green.ldev); + +unregister_red: + led_classdev_unregister(&rgb->red.ldev); + + return err; +} + +static void thingm_remove_rgb(struct thingm_rgb *rgb) +{ + flush_work(&rgb->work); + led_classdev_unregister(&rgb->red.ldev); + led_classdev_unregister(&rgb->green.ldev); + led_classdev_unregister(&rgb->blue.ldev); +} + +static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct thingm_device *tdev; + int i, err; + + tdev = devm_kzalloc(&hdev->dev, sizeof(struct thingm_device), + GFP_KERNEL); + if (!tdev) + return -ENOMEM; + + tdev->hdev = hdev; + hid_set_drvdata(hdev, tdev); + + err = hid_parse(hdev); + if (err) + goto error; + + err = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (err) + goto error; + + mutex_init(&tdev->lock); + + err = thingm_version(tdev); + if (err) + goto stop; + + hid_dbg(hdev, "firmware version: %c.%c\n", + tdev->version.major, tdev->version.minor); + + for (i = 0; i < ARRAY_SIZE(thingm_fwinfo) && !tdev->fwinfo; ++i) + if (thingm_fwinfo[i].major == tdev->version.major) + tdev->fwinfo = &thingm_fwinfo[i]; + + if (!tdev->fwinfo) { + hid_err(hdev, "unsupported firmware %c\n", tdev->version.major); + goto stop; + } + + tdev->rgb = devm_kzalloc(&hdev->dev, + sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb, + GFP_KERNEL); + if (!tdev->rgb) { + err = -ENOMEM; + goto stop; + } + + for (i = 0; i < tdev->fwinfo->numrgb; ++i) { + struct thingm_rgb *rgb = tdev->rgb + i; + + rgb->tdev = tdev; + rgb->num = tdev->fwinfo->first + i; + err = thingm_init_rgb(rgb); + if (err) { + while (--i >= 0) + thingm_remove_rgb(tdev->rgb + i); + goto stop; + } + } + + return 0; +stop: + hid_hw_stop(hdev); +error: + return err; +} + +static void thingm_remove(struct hid_device *hdev) +{ + struct thingm_device *tdev = hid_get_drvdata(hdev); + int i; + + for (i = 0; i < tdev->fwinfo->numrgb; ++i) + thingm_remove_rgb(tdev->rgb + i); + + 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 9f85f827607..d790d8d71f7 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 83a933b9c2e..b83376077d7 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -30,7 +30,6 @@ #include <linux/hid.h> #include <linux/input.h> #include <linux/slab.h> -#include <linux/usb.h> #include <linux/module.h> #include "hid-ids.h" @@ -46,7 +45,6 @@ static const signed short ff_joystick[] = { }; #ifdef CONFIG_THRUSTMASTER_FF -#include "usbhid/usbhid.h" /* Usages for thrustmaster devices I know about */ #define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb) @@ -103,7 +101,7 @@ static int tmff_play(struct input_dev *dev, void *data, dbg_hid("(x, y)=(%04x, %04x)\n", x, y); ff_field->value[0] = x; ff_field->value[1] = y; - usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); + hid_hw_request(hid, tmff->report, HID_REQ_SET_REPORT); break; case FF_RUMBLE: @@ -117,7 +115,7 @@ static int tmff_play(struct input_dev *dev, void *data, dbg_hid("(left,right)=(%08x, %08x)\n", left, right); ff_field->value[0] = left; ff_field->value[1] = right; - usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); + hid_hw_request(hid, tmff->report, HID_REQ_SET_REPORT); break; } return 0; @@ -261,17 +259,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 613ff7b1d74..8a5b843e9dd 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 f23456b1fd4..c08c36443f8 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 2e56a1fd237..fb8b516ff0e 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 2f60da9ed06..902013ec041 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -46,6 +46,7 @@ struct wacom_data { __u8 battery_capacity; __u8 power_raw; __u8 ps_connected; + __u8 bat_charging; struct power_supply battery; struct power_supply ac; __u8 led_selector; @@ -62,6 +63,7 @@ static enum power_supply_property wacom_battery_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_STATUS, }; static enum power_supply_property wacom_ac_props[] = { @@ -126,8 +128,8 @@ static void wacom_set_image(struct hid_device *hdev, const char *image, rep_data[0] = WAC_CMD_ICON_START_STOP; rep_data[1] = 0; - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); if (ret < 0) goto err; @@ -141,15 +143,15 @@ static void wacom_set_image(struct hid_device *hdev, const char *image, rep_data[j + 3] = p[(i << 6) + j]; rep_data[2] = i; - ret = hdev->hid_output_raw_report(hdev, rep_data, 67, - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 67, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); } rep_data[0] = WAC_CMD_ICON_START_STOP; rep_data[1] = 0; - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); err: return; @@ -181,7 +183,8 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev, buf[3] = value; /* use fixed brightness for OLEDs */ buf[4] = 0x08; - hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); + hid_hw_raw_request(hdev, buf[0], buf, 9, HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); kfree(buf); } @@ -287,6 +290,15 @@ static int wacom_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: val->intval = wdata->battery_capacity; break; + case POWER_SUPPLY_PROP_STATUS: + if (wdata->bat_charging) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + if (wdata->battery_capacity == 100 && wdata->ps_connected) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; default: ret = -EINVAL; break; @@ -328,8 +340,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[0] = 0x03 ; rep_data[1] = 0x00; limit = 3; do { - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); } while (ret < 0 && limit-- > 0); if (ret >= 0) { @@ -341,8 +353,9 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[1] = 0x00; limit = 3; do { - ret = hdev->hid_output_raw_report(hdev, - rep_data, 2, HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], + rep_data, 2, HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); } while (ret < 0 && limit-- > 0); if (ret >= 0) { @@ -367,8 +380,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[0] = 0x03; rep_data[1] = wdata->features; - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); if (ret >= 0) wdata->high_speed = speed; break; @@ -727,7 +740,8 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, if (power_raw != wdata->power_raw) { wdata->power_raw = power_raw; wdata->battery_capacity = batcap_i4[power_raw & 0x07]; - wdata->ps_connected = power_raw & 0x08; + wdata->bat_charging = (power_raw & 0x08) ? 1 : 0; + wdata->ps_connected = (power_raw & 0x10) ? 1 : 0; } break; @@ -953,23 +967,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 bb536ab5941..059931d7b39 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 84e2fbec5fb..d00391418d1 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -1,6 +1,6 @@ /* - * HID driver for Nintendo Wiimote devices - * Copyright (c) 2011 David Herrmann + * HID driver for Nintendo Wii / Wii U peripherals + * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com> */ /* @@ -14,85 +14,60 @@ #include <linux/device.h> #include <linux/hid.h> #include <linux/input.h> -#include <linux/leds.h> #include <linux/module.h> #include <linux/mutex.h> -#include <linux/power_supply.h> #include <linux/spinlock.h> #include "hid-ids.h" #include "hid-wiimote.h" -enum wiiproto_keys { - WIIPROTO_KEY_LEFT, - WIIPROTO_KEY_RIGHT, - WIIPROTO_KEY_UP, - WIIPROTO_KEY_DOWN, - WIIPROTO_KEY_PLUS, - WIIPROTO_KEY_MINUS, - WIIPROTO_KEY_ONE, - WIIPROTO_KEY_TWO, - WIIPROTO_KEY_A, - WIIPROTO_KEY_B, - WIIPROTO_KEY_HOME, - WIIPROTO_KEY_COUNT -}; - -static __u16 wiiproto_keymap[] = { - KEY_LEFT, /* WIIPROTO_KEY_LEFT */ - KEY_RIGHT, /* WIIPROTO_KEY_RIGHT */ - KEY_UP, /* WIIPROTO_KEY_UP */ - KEY_DOWN, /* WIIPROTO_KEY_DOWN */ - KEY_NEXT, /* WIIPROTO_KEY_PLUS */ - KEY_PREVIOUS, /* WIIPROTO_KEY_MINUS */ - BTN_1, /* WIIPROTO_KEY_ONE */ - BTN_2, /* WIIPROTO_KEY_TWO */ - BTN_A, /* WIIPROTO_KEY_A */ - BTN_B, /* WIIPROTO_KEY_B */ - BTN_MODE, /* WIIPROTO_KEY_HOME */ -}; +/* output queue handling */ -static enum power_supply_property wiimote_battery_props[] = { - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_SCOPE, -}; - -static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, - size_t count) +static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, + size_t count) { __u8 *buf; - ssize_t ret; + int ret; - if (!hdev->hid_output_raw_report) + if (!hdev->ll_driver->output_report) return -ENODEV; buf = kmemdup(buffer, count, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT); + ret = hid_hw_output_report(hdev, buf, count); kfree(buf); return ret; } -static void wiimote_worker(struct work_struct *work) +static void wiimote_queue_worker(struct work_struct *work) { - struct wiimote_data *wdata = container_of(work, struct wiimote_data, - worker); + struct wiimote_queue *queue = container_of(work, struct wiimote_queue, + worker); + struct wiimote_data *wdata = container_of(queue, struct wiimote_data, + queue); unsigned long flags; + int ret; - spin_lock_irqsave(&wdata->qlock, flags); + spin_lock_irqsave(&wdata->queue.lock, flags); - while (wdata->head != wdata->tail) { - spin_unlock_irqrestore(&wdata->qlock, flags); - wiimote_hid_send(wdata->hdev, wdata->outq[wdata->tail].data, - wdata->outq[wdata->tail].size); - spin_lock_irqsave(&wdata->qlock, flags); + while (wdata->queue.head != wdata->queue.tail) { + spin_unlock_irqrestore(&wdata->queue.lock, flags); + ret = wiimote_hid_send(wdata->hdev, + wdata->queue.outq[wdata->queue.tail].data, + wdata->queue.outq[wdata->queue.tail].size); + if (ret < 0) { + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_abort(wdata); + spin_unlock_irqrestore(&wdata->state.lock, flags); + } + spin_lock_irqsave(&wdata->queue.lock, flags); - wdata->tail = (wdata->tail + 1) % WIIMOTE_BUFSIZE; + wdata->queue.tail = (wdata->queue.tail + 1) % WIIMOTE_BUFSIZE; } - spin_unlock_irqrestore(&wdata->qlock, flags); + spin_unlock_irqrestore(&wdata->queue.lock, flags); } static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer, @@ -103,7 +78,9 @@ static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer, if (count > HID_MAX_BUFFER_SIZE) { hid_warn(wdata->hdev, "Sending too large output report\n"); - return; + + spin_lock_irqsave(&wdata->queue.lock, flags); + goto out_error; } /* @@ -116,22 +93,28 @@ static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer, * will reschedule itself until the queue is empty. */ - spin_lock_irqsave(&wdata->qlock, flags); + spin_lock_irqsave(&wdata->queue.lock, flags); - memcpy(wdata->outq[wdata->head].data, buffer, count); - wdata->outq[wdata->head].size = count; - newhead = (wdata->head + 1) % WIIMOTE_BUFSIZE; + memcpy(wdata->queue.outq[wdata->queue.head].data, buffer, count); + wdata->queue.outq[wdata->queue.head].size = count; + newhead = (wdata->queue.head + 1) % WIIMOTE_BUFSIZE; - if (wdata->head == wdata->tail) { - wdata->head = newhead; - schedule_work(&wdata->worker); - } else if (newhead != wdata->tail) { - wdata->head = newhead; + if (wdata->queue.head == wdata->queue.tail) { + wdata->queue.head = newhead; + schedule_work(&wdata->queue.worker); + } else if (newhead != wdata->queue.tail) { + wdata->queue.head = newhead; } else { hid_warn(wdata->hdev, "Output queue is full"); + goto out_error; } - spin_unlock_irqrestore(&wdata->qlock, flags); + goto out_unlock; + +out_error: + wiimote_cmd_abort(wdata); +out_unlock: + spin_unlock_irqrestore(&wdata->queue.lock, flags); } /* @@ -147,7 +130,7 @@ static inline void wiiproto_keep_rumble(struct wiimote_data *wdata, __u8 *cmd1) *cmd1 |= 0x01; } -static void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble) +void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble) { __u8 cmd[2]; @@ -167,7 +150,7 @@ static void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble) wiimote_queue(wdata, cmd, sizeof(cmd)); } -static void wiiproto_req_leds(struct wiimote_data *wdata, int leds) +void wiiproto_req_leds(struct wiimote_data *wdata, int leds) { __u8 cmd[2]; @@ -196,17 +179,48 @@ static void wiiproto_req_leds(struct wiimote_data *wdata, int leds) * Check what peripherals of the wiimote are currently * active and select a proper DRM that supports all of * the requested data inputs. + * + * Not all combinations are actually supported. The following + * combinations work only with limitations: + * - IR cam in extended or full mode disables any data transmission + * of extension controllers. There is no DRM mode that supports + * extension bytes plus extended/full IR. + * - IR cam with accelerometer and extension *_EXT8 is not supported. + * However, all extensions that need *_EXT8 are devices that don't + * support IR cameras. Hence, this shouldn't happen under normal + * operation. + * - *_EXT16 is only supported in combination with buttons and + * accelerometer. No IR or similar can be active simultaneously. As + * above, all modules that require it are mutually exclusive with + * IR/etc. so this doesn't matter. */ static __u8 select_drm(struct wiimote_data *wdata) { __u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR; - bool ext = wiiext_active(wdata); + bool ext; + + ext = (wdata->state.flags & WIIPROTO_FLAG_EXT_USED) || + (wdata->state.flags & WIIPROTO_FLAG_MP_USED); + + /* some 3rd-party balance-boards are hard-coded to KEE, *sigh* */ + if (wdata->state.devtype == WIIMOTE_DEV_BALANCE_BOARD) { + if (ext) + return WIIPROTO_REQ_DRM_KEE; + else + return WIIPROTO_REQ_DRM_K; + } if (ir == WIIPROTO_FLAG_IR_BASIC) { - if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) + if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) { + /* GEN10 and ealier devices bind IR formats to DRMs. + * Hence, we cannot use DRM_KAI here as it might be + * bound to IR_EXT. Use DRM_KAIE unconditionally so we + * work with all devices and our parsers can use the + * fixed formats, too. */ return WIIPROTO_REQ_DRM_KAIE; - else + } else { return WIIPROTO_REQ_DRM_KIE; + } } else if (ir == WIIPROTO_FLAG_IR_EXT) { return WIIPROTO_REQ_DRM_KAI; } else if (ir == WIIPROTO_FLAG_IR_FULL) { @@ -219,7 +233,7 @@ static __u8 select_drm(struct wiimote_data *wdata) return WIIPROTO_REQ_DRM_KA; } else { if (ext) - return WIIPROTO_REQ_DRM_KE; + return WIIPROTO_REQ_DRM_KEE; else return WIIPROTO_REQ_DRM_K; } @@ -230,7 +244,9 @@ void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm) { __u8 cmd[3]; - if (drm == WIIPROTO_REQ_NULL) + if (wdata->state.flags & WIIPROTO_FLAG_DRM_LOCKED) + drm = wdata->state.drm; + else if (drm == WIIPROTO_REQ_NULL) drm = select_drm(wdata); cmd[0] = WIIPROTO_REQ_DRM; @@ -242,7 +258,7 @@ void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm) wiimote_queue(wdata, cmd, sizeof(cmd)); } -static void wiiproto_req_status(struct wiimote_data *wdata) +void wiiproto_req_status(struct wiimote_data *wdata) { __u8 cmd[2]; @@ -253,7 +269,7 @@ static void wiiproto_req_status(struct wiimote_data *wdata) wiimote_queue(wdata, cmd, sizeof(cmd)); } -static void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel) +void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel) { accel = !!accel; if (accel == !!(wdata->state.flags & WIIPROTO_FLAG_ACCEL)) @@ -267,7 +283,7 @@ static void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel) wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); } -static void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags) +void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags) { __u8 cmd[2]; @@ -278,7 +294,7 @@ static void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags) wiimote_queue(wdata, cmd, sizeof(cmd)); } -static void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags) +void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags) { __u8 cmd[2]; @@ -394,414 +410,1037 @@ ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, __u8 *rmem, return ret; } -static int wiimote_battery_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) +/* requires the cmd-mutex to be held */ +static int wiimote_cmd_init_ext(struct wiimote_data *wdata) { - struct wiimote_data *wdata = container_of(psy, - struct wiimote_data, battery); - int ret = 0, state; - unsigned long flags; + __u8 wmem; + int ret; - if (psp == POWER_SUPPLY_PROP_SCOPE) { - val->intval = POWER_SUPPLY_SCOPE_DEVICE; - return 0; - } + /* initialize extension */ + wmem = 0x55; + ret = wiimote_cmd_write(wdata, 0xa400f0, &wmem, sizeof(wmem)); + if (ret) + return ret; - ret = wiimote_cmd_acquire(wdata); + /* disable default encryption */ + wmem = 0x0; + ret = wiimote_cmd_write(wdata, 0xa400fb, &wmem, sizeof(wmem)); if (ret) return ret; - spin_lock_irqsave(&wdata->state.lock, flags); - wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0); - wiiproto_req_status(wdata); - spin_unlock_irqrestore(&wdata->state.lock, flags); + return 0; +} - ret = wiimote_cmd_wait(wdata); - state = wdata->state.cmd_battery; - wiimote_cmd_release(wdata); +/* requires the cmd-mutex to be held */ +static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem) +{ + int ret; + /* read extension ID */ + ret = wiimote_cmd_read(wdata, 0xa400fa, rmem, 6); + if (ret != 6) + return WIIMOTE_EXT_NONE; + + hid_dbg(wdata->hdev, "extension ID: %6phC\n", rmem); + + if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff && + rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff) + return WIIMOTE_EXT_NONE; + + if (rmem[4] == 0x00 && rmem[5] == 0x00) + return WIIMOTE_EXT_NUNCHUK; + if (rmem[4] == 0x01 && rmem[5] == 0x01) + return WIIMOTE_EXT_CLASSIC_CONTROLLER; + if (rmem[4] == 0x04 && rmem[5] == 0x02) + return WIIMOTE_EXT_BALANCE_BOARD; + if (rmem[4] == 0x01 && rmem[5] == 0x20) + return WIIMOTE_EXT_PRO_CONTROLLER; + + return WIIMOTE_EXT_UNKNOWN; +} + +/* requires the cmd-mutex to be held */ +static int wiimote_cmd_init_mp(struct wiimote_data *wdata) +{ + __u8 wmem; + int ret; + + /* initialize MP */ + wmem = 0x55; + ret = wiimote_cmd_write(wdata, 0xa600f0, &wmem, sizeof(wmem)); if (ret) return ret; - switch (psp) { - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = state * 100 / 255; - break; - default: - ret = -EINVAL; - break; + /* disable default encryption */ + wmem = 0x0; + ret = wiimote_cmd_write(wdata, 0xa600fb, &wmem, sizeof(wmem)); + if (ret) + return ret; + + return 0; +} + +/* requires the cmd-mutex to be held */ +static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype) +{ + __u8 wmem; + + /* map MP with correct pass-through mode */ + switch (exttype) { + case WIIMOTE_EXT_CLASSIC_CONTROLLER: + wmem = 0x07; + break; + case WIIMOTE_EXT_NUNCHUK: + wmem = 0x05; + break; + default: + wmem = 0x04; + break; } - return ret; + return wiimote_cmd_write(wdata, 0xa600fe, &wmem, sizeof(wmem)); } -static int wiimote_init_ir(struct wiimote_data *wdata, __u16 mode) +/* requires the cmd-mutex to be held */ +static bool wiimote_cmd_read_mp(struct wiimote_data *wdata, __u8 *rmem) { int ret; - unsigned long flags; - __u8 format = 0; - static const __u8 data_enable[] = { 0x01 }; - static const __u8 data_sens1[] = { 0x02, 0x00, 0x00, 0x71, 0x01, - 0x00, 0xaa, 0x00, 0x64 }; - static const __u8 data_sens2[] = { 0x63, 0x03 }; - static const __u8 data_fin[] = { 0x08 }; - spin_lock_irqsave(&wdata->state.lock, flags); + /* read motion plus ID */ + ret = wiimote_cmd_read(wdata, 0xa600fa, rmem, 6); + if (ret != 6) + return false; - if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) { - spin_unlock_irqrestore(&wdata->state.lock, flags); - return 0; - } + hid_dbg(wdata->hdev, "motion plus ID: %6phC\n", rmem); - if (mode == 0) { - wdata->state.flags &= ~WIIPROTO_FLAGS_IR; - wiiproto_req_ir1(wdata, 0); - wiiproto_req_ir2(wdata, 0); - wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); - spin_unlock_irqrestore(&wdata->state.lock, flags); - return 0; - } + if (rmem[5] == 0x05) + return true; - spin_unlock_irqrestore(&wdata->state.lock, flags); + hid_info(wdata->hdev, "unknown motion plus ID: %6phC\n", rmem); - ret = wiimote_cmd_acquire(wdata); - if (ret) - return ret; + return false; +} - /* send PIXEL CLOCK ENABLE cmd first */ - spin_lock_irqsave(&wdata->state.lock, flags); - wiimote_cmd_set(wdata, WIIPROTO_REQ_IR1, 0); - wiiproto_req_ir1(wdata, 0x06); - spin_unlock_irqrestore(&wdata->state.lock, flags); +/* requires the cmd-mutex to be held */ +static __u8 wiimote_cmd_read_mp_mapped(struct wiimote_data *wdata) +{ + int ret; + __u8 rmem[6]; - ret = wiimote_cmd_wait(wdata); - if (ret) - goto unlock; - if (wdata->state.cmd_err) { - ret = -EIO; - goto unlock; + /* read motion plus ID */ + ret = wiimote_cmd_read(wdata, 0xa400fa, rmem, 6); + if (ret != 6) + return WIIMOTE_MP_NONE; + + hid_dbg(wdata->hdev, "mapped motion plus ID: %6phC\n", rmem); + + if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff && + rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff) + return WIIMOTE_MP_NONE; + + if (rmem[4] == 0x04 && rmem[5] == 0x05) + return WIIMOTE_MP_SINGLE; + else if (rmem[4] == 0x05 && rmem[5] == 0x05) + return WIIMOTE_MP_PASSTHROUGH_NUNCHUK; + else if (rmem[4] == 0x07 && rmem[5] == 0x05) + return WIIMOTE_MP_PASSTHROUGH_CLASSIC; + + return WIIMOTE_MP_UNKNOWN; +} + +/* device module handling */ + +static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = { + [WIIMOTE_DEV_PENDING] = (const __u8[]){ + WIIMOD_NULL, + }, + [WIIMOTE_DEV_UNKNOWN] = (const __u8[]){ + WIIMOD_NO_MP, + WIIMOD_NULL, + }, + [WIIMOTE_DEV_GENERIC] = (const __u8[]){ + WIIMOD_KEYS, + WIIMOD_RUMBLE, + WIIMOD_BATTERY, + WIIMOD_LED1, + WIIMOD_LED2, + WIIMOD_LED3, + WIIMOD_LED4, + WIIMOD_ACCEL, + WIIMOD_IR, + WIIMOD_NULL, + }, + [WIIMOTE_DEV_GEN10] = (const __u8[]){ + WIIMOD_KEYS, + WIIMOD_RUMBLE, + WIIMOD_BATTERY, + WIIMOD_LED1, + WIIMOD_LED2, + WIIMOD_LED3, + WIIMOD_LED4, + WIIMOD_ACCEL, + WIIMOD_IR, + WIIMOD_NULL, + }, + [WIIMOTE_DEV_GEN20] = (const __u8[]){ + WIIMOD_KEYS, + WIIMOD_RUMBLE, + WIIMOD_BATTERY, + WIIMOD_LED1, + WIIMOD_LED2, + WIIMOD_LED3, + WIIMOD_LED4, + WIIMOD_ACCEL, + WIIMOD_IR, + WIIMOD_BUILTIN_MP, + WIIMOD_NULL, + }, + [WIIMOTE_DEV_BALANCE_BOARD] = (const __u8[]) { + WIIMOD_BATTERY, + WIIMOD_LED1, + WIIMOD_NO_MP, + WIIMOD_NULL, + }, + [WIIMOTE_DEV_PRO_CONTROLLER] = (const __u8[]) { + WIIMOD_BATTERY, + WIIMOD_LED1, + WIIMOD_LED2, + WIIMOD_LED3, + WIIMOD_LED4, + WIIMOD_NO_MP, + WIIMOD_NULL, + }, +}; + +static void wiimote_modules_load(struct wiimote_data *wdata, + unsigned int devtype) +{ + bool need_input = false; + const __u8 *mods, *iter; + const struct wiimod_ops *ops; + int ret; + + mods = wiimote_devtype_mods[devtype]; + + for (iter = mods; *iter != WIIMOD_NULL; ++iter) { + if (wiimod_table[*iter]->flags & WIIMOD_FLAG_INPUT) { + need_input = true; + break; + } } - /* enable IR LOGIC */ - spin_lock_irqsave(&wdata->state.lock, flags); - wiimote_cmd_set(wdata, WIIPROTO_REQ_IR2, 0); - wiiproto_req_ir2(wdata, 0x06); - spin_unlock_irqrestore(&wdata->state.lock, flags); + if (need_input) { + wdata->input = input_allocate_device(); + if (!wdata->input) + return; + + input_set_drvdata(wdata->input, wdata); + wdata->input->dev.parent = &wdata->hdev->dev; + wdata->input->id.bustype = wdata->hdev->bus; + wdata->input->id.vendor = wdata->hdev->vendor; + wdata->input->id.product = wdata->hdev->product; + wdata->input->id.version = wdata->hdev->version; + wdata->input->name = WIIMOTE_NAME; + } - ret = wiimote_cmd_wait(wdata); - if (ret) - goto unlock; - if (wdata->state.cmd_err) { - ret = -EIO; - goto unlock; + for (iter = mods; *iter != WIIMOD_NULL; ++iter) { + ops = wiimod_table[*iter]; + if (!ops->probe) + continue; + + ret = ops->probe(ops, wdata); + if (ret) + goto error; } - /* enable IR cam but do not make it send data, yet */ - ret = wiimote_cmd_write(wdata, 0xb00030, data_enable, - sizeof(data_enable)); - if (ret) - goto unlock; + if (wdata->input) { + ret = input_register_device(wdata->input); + if (ret) + goto error; + } - /* write first sensitivity block */ - ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1, - sizeof(data_sens1)); - if (ret) - goto unlock; + spin_lock_irq(&wdata->state.lock); + wdata->state.devtype = devtype; + spin_unlock_irq(&wdata->state.lock); + return; - /* write second sensitivity block */ - ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2, - sizeof(data_sens2)); - if (ret) - goto unlock; +error: + for ( ; iter-- != mods; ) { + ops = wiimod_table[*iter]; + if (ops->remove) + ops->remove(ops, wdata); + } - /* put IR cam into desired state */ - switch (mode) { - case WIIPROTO_FLAG_IR_FULL: - format = 5; - break; - case WIIPROTO_FLAG_IR_EXT: - format = 3; - break; - case WIIPROTO_FLAG_IR_BASIC: - format = 1; - break; + if (wdata->input) { + input_free_device(wdata->input); + wdata->input = NULL; } - ret = wiimote_cmd_write(wdata, 0xb00033, &format, sizeof(format)); - if (ret) - goto unlock; +} - /* make IR cam send data */ - ret = wiimote_cmd_write(wdata, 0xb00030, data_fin, sizeof(data_fin)); - if (ret) - goto unlock; +static void wiimote_modules_unload(struct wiimote_data *wdata) +{ + const __u8 *mods, *iter; + const struct wiimod_ops *ops; + unsigned long flags; + + mods = wiimote_devtype_mods[wdata->state.devtype]; - /* request new DRM mode compatible to IR mode */ spin_lock_irqsave(&wdata->state.lock, flags); - wdata->state.flags &= ~WIIPROTO_FLAGS_IR; - wdata->state.flags |= mode & WIIPROTO_FLAGS_IR; - wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + wdata->state.devtype = WIIMOTE_DEV_UNKNOWN; spin_unlock_irqrestore(&wdata->state.lock, flags); -unlock: - wiimote_cmd_release(wdata); - return ret; + /* find end of list */ + for (iter = mods; *iter != WIIMOD_NULL; ++iter) + /* empty */ ; + + if (wdata->input) { + input_get_device(wdata->input); + input_unregister_device(wdata->input); + } + + for ( ; iter-- != mods; ) { + ops = wiimod_table[*iter]; + if (ops->remove) + ops->remove(ops, wdata); + } + + if (wdata->input) { + input_put_device(wdata->input); + wdata->input = NULL; + } } -static enum led_brightness wiimote_leds_get(struct led_classdev *led_dev) +/* device extension handling */ + +static void wiimote_ext_load(struct wiimote_data *wdata, unsigned int ext) { - struct wiimote_data *wdata; - struct device *dev = led_dev->dev->parent; - int i; unsigned long flags; - bool value = false; + const struct wiimod_ops *ops; + int ret; - wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); + ops = wiimod_ext_table[ext]; - for (i = 0; i < 4; ++i) { - if (wdata->leds[i] == led_dev) { - spin_lock_irqsave(&wdata->state.lock, flags); - value = wdata->state.flags & WIIPROTO_FLAG_LED(i + 1); - spin_unlock_irqrestore(&wdata->state.lock, flags); - break; - } + if (ops->probe) { + ret = ops->probe(ops, wdata); + if (ret) + ext = WIIMOTE_EXT_UNKNOWN; } - return value ? LED_FULL : LED_OFF; + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.exttype = ext; + spin_unlock_irqrestore(&wdata->state.lock, flags); } -static void wiimote_leds_set(struct led_classdev *led_dev, - enum led_brightness value) +static void wiimote_ext_unload(struct wiimote_data *wdata) { - struct wiimote_data *wdata; - struct device *dev = led_dev->dev->parent; - int i; unsigned long flags; - __u8 state, flag; + const struct wiimod_ops *ops; - wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); + ops = wiimod_ext_table[wdata->state.exttype]; - for (i = 0; i < 4; ++i) { - if (wdata->leds[i] == led_dev) { - flag = WIIPROTO_FLAG_LED(i + 1); - spin_lock_irqsave(&wdata->state.lock, flags); - state = wdata->state.flags; - if (value == LED_OFF) - wiiproto_req_leds(wdata, state & ~flag); - else - wiiproto_req_leds(wdata, state | flag); - spin_unlock_irqrestore(&wdata->state.lock, flags); - break; - } + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.exttype = WIIMOTE_EXT_UNKNOWN; + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + if (ops->remove) + ops->remove(ops, wdata); +} + +static void wiimote_mp_load(struct wiimote_data *wdata) +{ + unsigned long flags; + const struct wiimod_ops *ops; + int ret; + __u8 mode = 2; + + ops = &wiimod_mp; + if (ops->probe) { + ret = ops->probe(ops, wdata); + if (ret) + mode = 1; } + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.mp = mode; + spin_unlock_irqrestore(&wdata->state.lock, flags); } -static int wiimote_ff_play(struct input_dev *dev, void *data, - struct ff_effect *eff) +static void wiimote_mp_unload(struct wiimote_data *wdata) { - struct wiimote_data *wdata = input_get_drvdata(dev); - __u8 value; unsigned long flags; + const struct wiimod_ops *ops; - /* - * The wiimote supports only a single rumble motor so if any magnitude - * is set to non-zero then we start the rumble motor. If both are set to - * zero, we stop the rumble motor. - */ + if (wdata->state.mp < 2) + return; - if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude) - value = 1; - else - value = 0; + ops = &wiimod_mp; spin_lock_irqsave(&wdata->state.lock, flags); - wiiproto_req_rumble(wdata, value); + wdata->state.mp = 0; + wdata->state.flags &= ~WIIPROTO_FLAG_MP_USED; spin_unlock_irqrestore(&wdata->state.lock, flags); - return 0; + if (ops->remove) + ops->remove(ops, wdata); } -static int wiimote_input_open(struct input_dev *dev) -{ - struct wiimote_data *wdata = input_get_drvdata(dev); +/* device (re-)initialization and detection */ - return hid_hw_open(wdata->hdev); -} +static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = { + [WIIMOTE_DEV_PENDING] = "Pending", + [WIIMOTE_DEV_UNKNOWN] = "Unknown", + [WIIMOTE_DEV_GENERIC] = "Generic", + [WIIMOTE_DEV_GEN10] = "Nintendo Wii Remote (Gen 1)", + [WIIMOTE_DEV_GEN20] = "Nintendo Wii Remote Plus (Gen 2)", + [WIIMOTE_DEV_BALANCE_BOARD] = "Nintendo Wii Balance Board", + [WIIMOTE_DEV_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller", +}; -static void wiimote_input_close(struct input_dev *dev) +/* Try to guess the device type based on all collected information. We + * first try to detect by static extension types, then VID/PID and the + * device name. If we cannot detect the device, we use + * WIIMOTE_DEV_GENERIC so all modules will get probed on the device. */ +static void wiimote_init_set_type(struct wiimote_data *wdata, + __u8 exttype) { - struct wiimote_data *wdata = input_get_drvdata(dev); + __u8 devtype = WIIMOTE_DEV_GENERIC; + __u16 vendor, product; + const char *name; + + vendor = wdata->hdev->vendor; + product = wdata->hdev->product; + name = wdata->hdev->name; + + if (exttype == WIIMOTE_EXT_BALANCE_BOARD) { + devtype = WIIMOTE_DEV_BALANCE_BOARD; + goto done; + } else if (exttype == WIIMOTE_EXT_PRO_CONTROLLER) { + devtype = WIIMOTE_DEV_PRO_CONTROLLER; + goto done; + } - hid_hw_close(wdata->hdev); + if (!strcmp(name, "Nintendo RVL-CNT-01")) { + devtype = WIIMOTE_DEV_GEN10; + goto done; + } else if (!strcmp(name, "Nintendo RVL-CNT-01-TR")) { + devtype = WIIMOTE_DEV_GEN20; + goto done; + } else if (!strcmp(name, "Nintendo RVL-WBC-01")) { + devtype = WIIMOTE_DEV_BALANCE_BOARD; + goto done; + } else if (!strcmp(name, "Nintendo RVL-CNT-01-UC")) { + devtype = WIIMOTE_DEV_PRO_CONTROLLER; + goto done; + } + + if (vendor == USB_VENDOR_ID_NINTENDO) { + if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE) { + devtype = WIIMOTE_DEV_GEN10; + goto done; + } else if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE2) { + devtype = WIIMOTE_DEV_GEN20; + goto done; + } + } + +done: + if (devtype == WIIMOTE_DEV_GENERIC) + hid_info(wdata->hdev, "cannot detect device; NAME: %s VID: %04x PID: %04x EXT: %04x\n", + name, vendor, product, exttype); + else + hid_info(wdata->hdev, "detected device: %s\n", + wiimote_devtype_names[devtype]); + + wiimote_modules_load(wdata, devtype); } -static int wiimote_accel_open(struct input_dev *dev) +static void wiimote_init_detect(struct wiimote_data *wdata) { - struct wiimote_data *wdata = input_get_drvdata(dev); + __u8 exttype = WIIMOTE_EXT_NONE, extdata[6]; + bool ext; int ret; - unsigned long flags; - ret = hid_hw_open(wdata->hdev); + wiimote_cmd_acquire_noint(wdata); + + spin_lock_irq(&wdata->state.lock); + wdata->state.devtype = WIIMOTE_DEV_UNKNOWN; + wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0); + wiiproto_req_status(wdata); + spin_unlock_irq(&wdata->state.lock); + + ret = wiimote_cmd_wait_noint(wdata); if (ret) - return ret; + goto out_release; - spin_lock_irqsave(&wdata->state.lock, flags); - wiiproto_req_accel(wdata, true); - spin_unlock_irqrestore(&wdata->state.lock, flags); + spin_lock_irq(&wdata->state.lock); + ext = wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED; + spin_unlock_irq(&wdata->state.lock); - return 0; + if (!ext) + goto out_release; + + wiimote_cmd_init_ext(wdata); + exttype = wiimote_cmd_read_ext(wdata, extdata); + +out_release: + wiimote_cmd_release(wdata); + wiimote_init_set_type(wdata, exttype); + + /* schedule MP timer */ + spin_lock_irq(&wdata->state.lock); + if (!(wdata->state.flags & WIIPROTO_FLAG_BUILTIN_MP) && + !(wdata->state.flags & WIIPROTO_FLAG_NO_MP)) + mod_timer(&wdata->timer, jiffies + HZ * 4); + spin_unlock_irq(&wdata->state.lock); } -static void wiimote_accel_close(struct input_dev *dev) +/* + * MP hotplug events are not generated by the wiimote. Therefore, we need + * polling to detect it. We use a 4s interval for polling MP registers. This + * seems reasonable considering applications can trigger it manually via + * sysfs requests. + */ +static void wiimote_init_poll_mp(struct wiimote_data *wdata) { - struct wiimote_data *wdata = input_get_drvdata(dev); - unsigned long flags; + bool mp; + __u8 mpdata[6]; - spin_lock_irqsave(&wdata->state.lock, flags); - wiiproto_req_accel(wdata, false); - spin_unlock_irqrestore(&wdata->state.lock, flags); + wiimote_cmd_acquire_noint(wdata); + wiimote_cmd_init_mp(wdata); + mp = wiimote_cmd_read_mp(wdata, mpdata); + wiimote_cmd_release(wdata); - hid_hw_close(wdata->hdev); + /* load/unload MP module if it changed */ + if (mp) { + if (!wdata->state.mp) { + hid_info(wdata->hdev, "detected extension: Nintendo Wii Motion Plus\n"); + wiimote_mp_load(wdata); + } + } else if (wdata->state.mp) { + wiimote_mp_unload(wdata); + } + + mod_timer(&wdata->timer, jiffies + HZ * 4); } -static int wiimote_ir_open(struct input_dev *dev) +/* + * Check whether the wiimote is in the expected state. The extension registers + * may change during hotplug and initialization so we might get hotplug events + * that we caused by remapping some memory. + * We use some heuristics here to check known states. If the wiimote is in the + * expected state, we can ignore the hotplug event. + * + * Returns "true" if the device is in expected state, "false" if we should + * redo hotplug handling and extension initialization. + */ +static bool wiimote_init_check(struct wiimote_data *wdata) { - struct wiimote_data *wdata = input_get_drvdata(dev); - int ret; + __u32 flags; + __u8 type, data[6]; + bool ret, poll_mp; - ret = hid_hw_open(wdata->hdev); - if (ret) - return ret; + spin_lock_irq(&wdata->state.lock); + flags = wdata->state.flags; + spin_unlock_irq(&wdata->state.lock); - ret = wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC); - if (ret) { - hid_hw_close(wdata->hdev); - return ret; + wiimote_cmd_acquire_noint(wdata); + + /* If MP is used and active, but the extension is not, we expect: + * read_mp_mapped() == WIIMOTE_MP_SINGLE + * state.flags == !EXT_ACTIVE && !MP_PLUGGED && MP_ACTIVE + * We do not check EXT_PLUGGED because it might change during + * initialization of MP without extensions. + * - If MP is unplugged/replugged, read_mp_mapped() fails + * - If EXT is plugged, MP_PLUGGED will get set */ + if (wdata->state.exttype == WIIMOTE_EXT_NONE && + wdata->state.mp > 0 && (flags & WIIPROTO_FLAG_MP_USED)) { + type = wiimote_cmd_read_mp_mapped(wdata); + ret = type == WIIMOTE_MP_SINGLE; + + spin_lock_irq(&wdata->state.lock); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED); + ret = ret && (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE); + spin_unlock_irq(&wdata->state.lock); + + if (!ret) + hid_dbg(wdata->hdev, "state left: !EXT && MP\n"); + + /* while MP is mapped, we get EXT_PLUGGED events */ + poll_mp = false; + + goto out_release; } - return 0; + /* If MP is unused, but the extension port is used, we expect: + * read_ext == state.exttype + * state.flags == !MP_ACTIVE && EXT_ACTIVE + * - If MP is plugged/unplugged, our timer detects it + * - If EXT is unplugged/replugged, EXT_ACTIVE will become unset */ + if (!(flags & WIIPROTO_FLAG_MP_USED) && + wdata->state.exttype != WIIMOTE_EXT_NONE) { + type = wiimote_cmd_read_ext(wdata, data); + ret = type == wdata->state.exttype; + + spin_lock_irq(&wdata->state.lock); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE); + ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE); + spin_unlock_irq(&wdata->state.lock); + + if (!ret) + hid_dbg(wdata->hdev, "state left: EXT && !MP\n"); + + /* poll MP for hotplug events */ + poll_mp = true; + + goto out_release; + } + + /* If neither MP nor an extension are used, we expect: + * read_ext() == WIIMOTE_EXT_NONE + * state.flags == !MP_ACTIVE && !EXT_ACTIVE && !EXT_PLUGGED + * No need to perform any action in this case as everything is + * disabled already. + * - If MP is plugged/unplugged, our timer detects it + * - If EXT is plugged, EXT_PLUGGED will be set */ + if (!(flags & WIIPROTO_FLAG_MP_USED) && + wdata->state.exttype == WIIMOTE_EXT_NONE) { + type = wiimote_cmd_read_ext(wdata, data); + ret = type == wdata->state.exttype; + + spin_lock_irq(&wdata->state.lock); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE); + ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED); + spin_unlock_irq(&wdata->state.lock); + + if (!ret) + hid_dbg(wdata->hdev, "state left: !EXT && !MP\n"); + + /* poll MP for hotplug events */ + poll_mp = true; + + goto out_release; + } + + /* The trickiest part is if both EXT and MP are active. We cannot read + * the EXT ID, anymore, because MP is mapped over it. However, we use + * a handy trick here: + * - EXT_ACTIVE is unset whenever !MP_PLUGGED is sent + * MP_PLUGGED might be re-sent again before we are scheduled, but + * EXT_ACTIVE will stay unset. + * So it is enough to check for mp_mapped() and MP_ACTIVE and + * EXT_ACTIVE. EXT_PLUGGED is a sanity check. */ + if (wdata->state.exttype != WIIMOTE_EXT_NONE && + wdata->state.mp > 0 && (flags & WIIPROTO_FLAG_MP_USED)) { + type = wiimote_cmd_read_mp_mapped(wdata); + ret = type != WIIMOTE_MP_NONE; + ret = ret && type != WIIMOTE_MP_UNKNOWN; + ret = ret && type != WIIMOTE_MP_SINGLE; + + spin_lock_irq(&wdata->state.lock); + ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED); + ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE); + ret = ret && (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE); + spin_unlock_irq(&wdata->state.lock); + + if (!ret) + hid_dbg(wdata->hdev, "state left: EXT && MP\n"); + + /* while MP is mapped, we get EXT_PLUGGED events */ + poll_mp = false; + + goto out_release; + } + + /* unknown state */ + ret = false; + +out_release: + wiimote_cmd_release(wdata); + + /* only poll for MP if requested and if state didn't change */ + if (ret && poll_mp && !(flags & WIIPROTO_FLAG_BUILTIN_MP) && + !(flags & WIIPROTO_FLAG_NO_MP)) + wiimote_init_poll_mp(wdata); + + return ret; } -static void wiimote_ir_close(struct input_dev *dev) +static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = { + [WIIMOTE_EXT_NONE] = "None", + [WIIMOTE_EXT_UNKNOWN] = "Unknown", + [WIIMOTE_EXT_NUNCHUK] = "Nintendo Wii Nunchuk", + [WIIMOTE_EXT_CLASSIC_CONTROLLER] = "Nintendo Wii Classic Controller", + [WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board", + [WIIMOTE_EXT_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller", +}; + +/* + * Handle hotplug events + * If we receive an hotplug event and the device-check failed, we deinitialize + * the extension ports, re-read all extension IDs and set the device into + * the desired state. This involves mapping MP into the main extension + * registers, setting up extension passthrough modes and initializing the + * requested extensions. + */ +static void wiimote_init_hotplug(struct wiimote_data *wdata) { - struct wiimote_data *wdata = input_get_drvdata(dev); + __u8 exttype, extdata[6], mpdata[6]; + __u32 flags; + bool mp; - wiimote_init_ir(wdata, 0); - hid_hw_close(wdata->hdev); + hid_dbg(wdata->hdev, "detect extensions..\n"); + + wiimote_cmd_acquire_noint(wdata); + + spin_lock_irq(&wdata->state.lock); + + /* get state snapshot that we will then work on */ + flags = wdata->state.flags; + + /* disable event forwarding temporarily */ + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE; + wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE; + + spin_unlock_irq(&wdata->state.lock); + + /* init extension and MP (deactivates current extension or MP) */ + wiimote_cmd_init_ext(wdata); + if (flags & WIIPROTO_FLAG_NO_MP) { + mp = false; + } else { + wiimote_cmd_init_mp(wdata); + mp = wiimote_cmd_read_mp(wdata, mpdata); + } + exttype = wiimote_cmd_read_ext(wdata, extdata); + + wiimote_cmd_release(wdata); + + /* load/unload extension module if it changed */ + if (exttype != wdata->state.exttype) { + /* unload previous extension */ + wiimote_ext_unload(wdata); + + if (exttype == WIIMOTE_EXT_UNKNOWN) { + hid_info(wdata->hdev, "cannot detect extension; %6phC\n", + extdata); + } else if (exttype == WIIMOTE_EXT_NONE) { + spin_lock_irq(&wdata->state.lock); + wdata->state.exttype = WIIMOTE_EXT_NONE; + spin_unlock_irq(&wdata->state.lock); + } else { + hid_info(wdata->hdev, "detected extension: %s\n", + wiimote_exttype_names[exttype]); + /* try loading new extension */ + wiimote_ext_load(wdata, exttype); + } + } + + /* load/unload MP module if it changed */ + if (mp) { + if (!wdata->state.mp) { + hid_info(wdata->hdev, "detected extension: Nintendo Wii Motion Plus\n"); + wiimote_mp_load(wdata); + } + } else if (wdata->state.mp) { + wiimote_mp_unload(wdata); + } + + /* if MP is not used, do not map or activate it */ + if (!(flags & WIIPROTO_FLAG_MP_USED)) + mp = false; + + /* map MP into main extension registers if used */ + if (mp) { + wiimote_cmd_acquire_noint(wdata); + wiimote_cmd_map_mp(wdata, exttype); + wiimote_cmd_release(wdata); + + /* delete MP hotplug timer */ + del_timer_sync(&wdata->timer); + } else { + /* reschedule MP hotplug timer */ + if (!(flags & WIIPROTO_FLAG_BUILTIN_MP) && + !(flags & WIIPROTO_FLAG_NO_MP)) + mod_timer(&wdata->timer, jiffies + HZ * 4); + } + + spin_lock_irq(&wdata->state.lock); + + /* enable data forwarding again and set expected hotplug state */ + if (mp) { + wdata->state.flags |= WIIPROTO_FLAG_MP_ACTIVE; + if (wdata->state.exttype == WIIMOTE_EXT_NONE) { + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED; + wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED; + } else { + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED; + wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED; + wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE; + } + } else if (wdata->state.exttype != WIIMOTE_EXT_NONE) { + wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE; + } + + /* request status report for hotplug state updates */ + wiiproto_req_status(wdata); + + spin_unlock_irq(&wdata->state.lock); + + hid_dbg(wdata->hdev, "detected extensions: MP: %d EXT: %d\n", + wdata->state.mp, wdata->state.exttype); } -static void handler_keys(struct wiimote_data *wdata, const __u8 *payload) +static void wiimote_init_worker(struct work_struct *work) { - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT], - !!(payload[0] & 0x01)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_RIGHT], - !!(payload[0] & 0x02)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_DOWN], - !!(payload[0] & 0x04)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_UP], - !!(payload[0] & 0x08)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_PLUS], - !!(payload[0] & 0x10)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_TWO], - !!(payload[1] & 0x01)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_ONE], - !!(payload[1] & 0x02)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_B], - !!(payload[1] & 0x04)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_A], - !!(payload[1] & 0x08)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_MINUS], - !!(payload[1] & 0x10)); - input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_HOME], - !!(payload[1] & 0x80)); - input_sync(wdata->input); + struct wiimote_data *wdata = container_of(work, struct wiimote_data, + init_worker); + bool changed = false; + + if (wdata->state.devtype == WIIMOTE_DEV_PENDING) { + wiimote_init_detect(wdata); + changed = true; + } + + if (changed || !wiimote_init_check(wdata)) + wiimote_init_hotplug(wdata); + + if (changed) + kobject_uevent(&wdata->hdev->dev.kobj, KOBJ_CHANGE); } -static void handler_accel(struct wiimote_data *wdata, const __u8 *payload) +void __wiimote_schedule(struct wiimote_data *wdata) +{ + if (!(wdata->state.flags & WIIPROTO_FLAG_EXITING)) + schedule_work(&wdata->init_worker); +} + +static void wiimote_schedule(struct wiimote_data *wdata) { - __u16 x, y, z; + unsigned long flags; - if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL)) - return; + spin_lock_irqsave(&wdata->state.lock, flags); + __wiimote_schedule(wdata); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} - /* - * payload is: BB BB XX YY ZZ - * Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ - * contain the upper 8 bits of each value. The lower 2 bits are - * contained in the buttons data BB BB. - * Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the - * X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y - * accel value and bit 6 is the second bit of the Z value. - * The first bit of Y and Z values is not available and always set to 0. - * 0x200 is returned on no movement. - */ +static void wiimote_init_timeout(unsigned long arg) +{ + struct wiimote_data *wdata = (void*)arg; - x = payload[2] << 2; - y = payload[3] << 2; - z = payload[4] << 2; + wiimote_schedule(wdata); +} - x |= (payload[0] >> 5) & 0x3; - y |= (payload[1] >> 4) & 0x2; - z |= (payload[1] >> 5) & 0x2; +/* protocol handlers */ - input_report_abs(wdata->accel, ABS_RX, x - 0x200); - input_report_abs(wdata->accel, ABS_RY, y - 0x200); - input_report_abs(wdata->accel, ABS_RZ, z - 0x200); - input_sync(wdata->accel); +static void handler_keys(struct wiimote_data *wdata, const __u8 *payload) +{ + const __u8 *iter, *mods; + const struct wiimod_ops *ops; + + ops = wiimod_ext_table[wdata->state.exttype]; + if (ops->in_keys) { + ops->in_keys(wdata, payload); + return; + } + + mods = wiimote_devtype_mods[wdata->state.devtype]; + for (iter = mods; *iter != WIIMOD_NULL; ++iter) { + ops = wiimod_table[*iter]; + if (ops->in_keys) { + ops->in_keys(wdata, payload); + break; + } + } } -#define ir_to_input0(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ - ABS_HAT0X, ABS_HAT0Y) -#define ir_to_input1(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ - ABS_HAT1X, ABS_HAT1Y) -#define ir_to_input2(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ - ABS_HAT2X, ABS_HAT2Y) -#define ir_to_input3(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ - ABS_HAT3X, ABS_HAT3Y) +static void handler_accel(struct wiimote_data *wdata, const __u8 *payload) +{ + const __u8 *iter, *mods; + const struct wiimod_ops *ops; + + ops = wiimod_ext_table[wdata->state.exttype]; + if (ops->in_accel) { + ops->in_accel(wdata, payload); + return; + } -static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir, - bool packed, __u8 xid, __u8 yid) + mods = wiimote_devtype_mods[wdata->state.devtype]; + for (iter = mods; *iter != WIIMOD_NULL; ++iter) { + ops = wiimod_table[*iter]; + if (ops->in_accel) { + ops->in_accel(wdata, payload); + break; + } + } +} + +static bool valid_ext_handler(const struct wiimod_ops *ops, size_t len) { - __u16 x, y; + if (!ops->in_ext) + return false; + if ((ops->flags & WIIMOD_FLAG_EXT8) && len < 8) + return false; + if ((ops->flags & WIIMOD_FLAG_EXT16) && len < 16) + return false; + + return true; +} - if (!(wdata->state.flags & WIIPROTO_FLAGS_IR)) +static void handler_ext(struct wiimote_data *wdata, const __u8 *payload, + size_t len) +{ + static const __u8 invalid[21] = { 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff }; + const __u8 *iter, *mods; + const struct wiimod_ops *ops; + bool is_mp; + + if (len > 21) + len = 21; + if (len < 6 || !memcmp(payload, invalid, len)) return; - /* - * Basic IR data is encoded into 3 bytes. The first two bytes are the - * lower 8 bit of the X/Y data, the 3rd byte contains the upper 2 bits - * of both. - * If data is packed, then the 3rd byte is put first and slightly - * reordered. This allows to interleave packed and non-packed data to - * have two IR sets in 5 bytes instead of 6. - * The resulting 10bit X/Y values are passed to the ABS_HATXY input dev. - */ + /* if MP is active, track MP slot hotplugging */ + if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) { + /* this bit is set for invalid events (eg. during hotplug) */ + if (payload[5] & 0x01) + return; + + if (payload[4] & 0x01) { + if (!(wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED)) { + hid_dbg(wdata->hdev, "MP hotplug: 1\n"); + wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED; + __wiimote_schedule(wdata); + } + } else { + if (wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED) { + hid_dbg(wdata->hdev, "MP hotplug: 0\n"); + wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED; + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE; + __wiimote_schedule(wdata); + } + } - if (packed) { - x = ir[1] | ((ir[0] & 0x03) << 8); - y = ir[2] | ((ir[0] & 0x0c) << 6); + /* detect MP data that is sent interleaved with EXT data */ + is_mp = payload[5] & 0x02; } else { - x = ir[0] | ((ir[2] & 0x30) << 4); - y = ir[1] | ((ir[2] & 0xc0) << 2); + is_mp = false; } - input_report_abs(wdata->ir, xid, x); - input_report_abs(wdata->ir, yid, y); + /* ignore EXT events if no extension is active */ + if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE) && !is_mp) + return; + + /* try forwarding to extension handler, first */ + ops = wiimod_ext_table[wdata->state.exttype]; + if (is_mp && ops->in_mp) { + ops->in_mp(wdata, payload); + return; + } else if (!is_mp && valid_ext_handler(ops, len)) { + ops->in_ext(wdata, payload); + return; + } + + /* try forwarding to MP handler */ + ops = &wiimod_mp; + if (is_mp && ops->in_mp) { + ops->in_mp(wdata, payload); + return; + } else if (!is_mp && valid_ext_handler(ops, len)) { + ops->in_ext(wdata, payload); + return; + } + + /* try forwarding to loaded modules */ + mods = wiimote_devtype_mods[wdata->state.devtype]; + for (iter = mods; *iter != WIIMOD_NULL; ++iter) { + ops = wiimod_table[*iter]; + if (is_mp && ops->in_mp) { + ops->in_mp(wdata, payload); + return; + } else if (!is_mp && valid_ext_handler(ops, len)) { + ops->in_ext(wdata, payload); + return; + } + } } -static void handler_status(struct wiimote_data *wdata, const __u8 *payload) +#define ir_to_input0(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 0) +#define ir_to_input1(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 1) +#define ir_to_input2(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 2) +#define ir_to_input3(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 3) + +static void handler_ir(struct wiimote_data *wdata, const __u8 *payload, + bool packed, unsigned int id) +{ + const __u8 *iter, *mods; + const struct wiimod_ops *ops; + + ops = wiimod_ext_table[wdata->state.exttype]; + if (ops->in_ir) { + ops->in_ir(wdata, payload, packed, id); + return; + } + + mods = wiimote_devtype_mods[wdata->state.devtype]; + for (iter = mods; *iter != WIIMOD_NULL; ++iter) { + ops = wiimod_table[*iter]; + if (ops->in_ir) { + ops->in_ir(wdata, payload, packed, id); + break; + } + } +} + +/* reduced status report with "BB BB" key data only */ +static void handler_status_K(struct wiimote_data *wdata, + const __u8 *payload) { handler_keys(wdata, payload); /* on status reports the drm is reset so we need to resend the drm */ wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); +} - wiiext_event(wdata, payload[2] & 0x02); +/* extended status report with "BB BB LF 00 00 VV" data */ +static void handler_status(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_status_K(wdata, payload); + + /* update extension status */ + if (payload[2] & 0x02) { + if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED)) { + hid_dbg(wdata->hdev, "EXT hotplug: 1\n"); + wdata->state.flags |= WIIPROTO_FLAG_EXT_PLUGGED; + __wiimote_schedule(wdata); + } + } else { + if (wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED) { + hid_dbg(wdata->hdev, "EXT hotplug: 0\n"); + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED; + wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED; + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE; + wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE; + __wiimote_schedule(wdata); + } + } - if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) { - wdata->state.cmd_battery = payload[5]; + wdata->state.cmd_battery = payload[5]; + if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) wiimote_cmd_complete(wdata); - } +} + +/* reduced generic report with "BB BB" key data only */ +static void handler_generic_K(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); } static void handler_data(struct wiimote_data *wdata, const __u8 *payload) @@ -850,7 +1489,7 @@ static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload) static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload) { handler_keys(wdata, payload); - wiiext_handle(wdata, &payload[2]); + handler_ext(wdata, &payload[2], 8); } static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload) @@ -861,13 +1500,12 @@ static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload) ir_to_input1(wdata, &payload[8], false); ir_to_input2(wdata, &payload[11], false); ir_to_input3(wdata, &payload[14], false); - input_sync(wdata->ir); } static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload) { handler_keys(wdata, payload); - wiiext_handle(wdata, &payload[2]); + handler_ext(wdata, &payload[2], 19); } static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload) @@ -877,15 +1515,14 @@ static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload) ir_to_input1(wdata, &payload[4], true); ir_to_input2(wdata, &payload[7], false); ir_to_input3(wdata, &payload[9], true); - input_sync(wdata->ir); - wiiext_handle(wdata, &payload[12]); + handler_ext(wdata, &payload[12], 9); } static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload) { handler_keys(wdata, payload); handler_accel(wdata, payload); - wiiext_handle(wdata, &payload[5]); + handler_ext(wdata, &payload[5], 16); } static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload) @@ -896,13 +1533,12 @@ static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload) ir_to_input1(wdata, &payload[7], true); ir_to_input2(wdata, &payload[10], false); ir_to_input3(wdata, &payload[12], true); - input_sync(wdata->ir); - wiiext_handle(wdata, &payload[15]); + handler_ext(wdata, &payload[15], 6); } static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload) { - wiiext_handle(wdata, payload); + handler_ext(wdata, payload, 21); } static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload) @@ -915,7 +1551,6 @@ static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload) ir_to_input0(wdata, &payload[3], false); ir_to_input1(wdata, &payload[12], false); - input_sync(wdata->ir); } static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload) @@ -936,7 +1571,6 @@ static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload) ir_to_input2(wdata, &payload[3], false); ir_to_input3(wdata, &payload[12], false); - input_sync(wdata->ir); } struct wiiproto_handler { @@ -947,16 +1581,26 @@ struct wiiproto_handler { static struct wiiproto_handler handlers[] = { { .id = WIIPROTO_REQ_STATUS, .size = 6, .func = handler_status }, + { .id = WIIPROTO_REQ_STATUS, .size = 2, .func = handler_status_K }, { .id = WIIPROTO_REQ_DATA, .size = 21, .func = handler_data }, + { .id = WIIPROTO_REQ_DATA, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_RETURN, .size = 4, .func = handler_return }, + { .id = WIIPROTO_REQ_RETURN, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys }, { .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA }, + { .id = WIIPROTO_REQ_DRM_KA, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KE, .size = 10, .func = handler_drm_KE }, + { .id = WIIPROTO_REQ_DRM_KE, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI }, + { .id = WIIPROTO_REQ_DRM_KAI, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KEE, .size = 21, .func = handler_drm_KEE }, + { .id = WIIPROTO_REQ_DRM_KEE, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE }, + { .id = WIIPROTO_REQ_DRM_KAE, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE }, + { .id = WIIPROTO_REQ_DRM_KIE, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE }, + { .id = WIIPROTO_REQ_DRM_KAIE, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_E, .size = 21, .func = handler_drm_E }, { .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 }, { .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 }, @@ -970,7 +1614,6 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report, struct wiiproto_handler *h; int i; unsigned long flags; - bool handled = false; if (size < 1) return -EINVAL; @@ -981,11 +1624,11 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report, h = &handlers[i]; if (h->id == raw_data[0] && h->size < size) { h->func(wdata, &raw_data[1]); - handled = true; + break; } } - if (!handled) + if (!handlers[i].id) hid_warn(hdev, "Unhandled report %hhu size %d\n", raw_data[0], size); @@ -994,176 +1637,136 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report, return 0; } -static void wiimote_leds_destroy(struct wiimote_data *wdata) +static ssize_t wiimote_ext_show(struct device *dev, + struct device_attribute *attr, + char *buf) { - int i; - struct led_classdev *led; - - for (i = 0; i < 4; ++i) { - if (wdata->leds[i]) { - led = wdata->leds[i]; - wdata->leds[i] = NULL; - led_classdev_unregister(led); - kfree(led); - } + struct wiimote_data *wdata = dev_to_wii(dev); + __u8 type; + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + type = wdata->state.exttype; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + switch (type) { + case WIIMOTE_EXT_NONE: + return sprintf(buf, "none\n"); + case WIIMOTE_EXT_NUNCHUK: + return sprintf(buf, "nunchuk\n"); + case WIIMOTE_EXT_CLASSIC_CONTROLLER: + return sprintf(buf, "classic\n"); + case WIIMOTE_EXT_BALANCE_BOARD: + return sprintf(buf, "balanceboard\n"); + case WIIMOTE_EXT_PRO_CONTROLLER: + return sprintf(buf, "procontroller\n"); + case WIIMOTE_EXT_UNKNOWN: + /* fallthrough */ + default: + return sprintf(buf, "unknown\n"); } } -static int wiimote_leds_create(struct wiimote_data *wdata) +static ssize_t wiimote_ext_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - int i, ret; - struct device *dev = &wdata->hdev->dev; - size_t namesz = strlen(dev_name(dev)) + 9; - struct led_classdev *led; - char *name; + struct wiimote_data *wdata = dev_to_wii(dev); - for (i = 0; i < 4; ++i) { - led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL); - if (!led) { - ret = -ENOMEM; - goto err; - } - name = (void*)&led[1]; - snprintf(name, namesz, "%s:blue:p%d", dev_name(dev), i); - led->name = name; - led->brightness = 0; - led->max_brightness = 1; - led->brightness_get = wiimote_leds_get; - led->brightness_set = wiimote_leds_set; - - ret = led_classdev_register(dev, led); - if (ret) { - kfree(led); - goto err; - } - wdata->leds[i] = led; + if (!strcmp(buf, "scan")) { + wiimote_schedule(wdata); + } else { + return -EINVAL; } - return 0; + return strnlen(buf, PAGE_SIZE); +} -err: - wiimote_leds_destroy(wdata); - return ret; +static DEVICE_ATTR(extension, S_IRUGO | S_IWUSR | S_IWGRP, wiimote_ext_show, + wiimote_ext_store); + +static ssize_t wiimote_dev_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wiimote_data *wdata = dev_to_wii(dev); + __u8 type; + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + type = wdata->state.devtype; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + switch (type) { + case WIIMOTE_DEV_GENERIC: + return sprintf(buf, "generic\n"); + case WIIMOTE_DEV_GEN10: + return sprintf(buf, "gen10\n"); + case WIIMOTE_DEV_GEN20: + return sprintf(buf, "gen20\n"); + case WIIMOTE_DEV_BALANCE_BOARD: + return sprintf(buf, "balanceboard\n"); + case WIIMOTE_DEV_PRO_CONTROLLER: + return sprintf(buf, "procontroller\n"); + case WIIMOTE_DEV_PENDING: + return sprintf(buf, "pending\n"); + case WIIMOTE_DEV_UNKNOWN: + /* fallthrough */ + default: + return sprintf(buf, "unknown\n"); + } } +static DEVICE_ATTR(devtype, S_IRUGO, wiimote_dev_show, NULL); + static struct wiimote_data *wiimote_create(struct hid_device *hdev) { struct wiimote_data *wdata; - int i; wdata = kzalloc(sizeof(*wdata), GFP_KERNEL); if (!wdata) return NULL; - wdata->input = input_allocate_device(); - if (!wdata->input) - goto err; - wdata->hdev = hdev; hid_set_drvdata(hdev, wdata); - input_set_drvdata(wdata->input, wdata); - wdata->input->open = wiimote_input_open; - wdata->input->close = wiimote_input_close; - wdata->input->dev.parent = &wdata->hdev->dev; - wdata->input->id.bustype = wdata->hdev->bus; - wdata->input->id.vendor = wdata->hdev->vendor; - wdata->input->id.product = wdata->hdev->product; - wdata->input->id.version = wdata->hdev->version; - wdata->input->name = WIIMOTE_NAME; - - set_bit(EV_KEY, wdata->input->evbit); - for (i = 0; i < WIIPROTO_KEY_COUNT; ++i) - set_bit(wiiproto_keymap[i], wdata->input->keybit); - - set_bit(FF_RUMBLE, wdata->input->ffbit); - if (input_ff_create_memless(wdata->input, NULL, wiimote_ff_play)) - goto err_input; - - wdata->accel = input_allocate_device(); - if (!wdata->accel) - goto err_input; - - input_set_drvdata(wdata->accel, wdata); - wdata->accel->open = wiimote_accel_open; - wdata->accel->close = wiimote_accel_close; - wdata->accel->dev.parent = &wdata->hdev->dev; - wdata->accel->id.bustype = wdata->hdev->bus; - wdata->accel->id.vendor = wdata->hdev->vendor; - wdata->accel->id.product = wdata->hdev->product; - wdata->accel->id.version = wdata->hdev->version; - wdata->accel->name = WIIMOTE_NAME " Accelerometer"; - - set_bit(EV_ABS, wdata->accel->evbit); - set_bit(ABS_RX, wdata->accel->absbit); - set_bit(ABS_RY, wdata->accel->absbit); - set_bit(ABS_RZ, wdata->accel->absbit); - input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4); - input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4); - input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4); - - wdata->ir = input_allocate_device(); - if (!wdata->ir) - goto err_ir; - - input_set_drvdata(wdata->ir, wdata); - wdata->ir->open = wiimote_ir_open; - wdata->ir->close = wiimote_ir_close; - wdata->ir->dev.parent = &wdata->hdev->dev; - wdata->ir->id.bustype = wdata->hdev->bus; - wdata->ir->id.vendor = wdata->hdev->vendor; - wdata->ir->id.product = wdata->hdev->product; - wdata->ir->id.version = wdata->hdev->version; - wdata->ir->name = WIIMOTE_NAME " IR"; - - set_bit(EV_ABS, wdata->ir->evbit); - set_bit(ABS_HAT0X, wdata->ir->absbit); - set_bit(ABS_HAT0Y, wdata->ir->absbit); - set_bit(ABS_HAT1X, wdata->ir->absbit); - set_bit(ABS_HAT1Y, wdata->ir->absbit); - set_bit(ABS_HAT2X, wdata->ir->absbit); - set_bit(ABS_HAT2Y, wdata->ir->absbit); - set_bit(ABS_HAT3X, wdata->ir->absbit); - set_bit(ABS_HAT3Y, wdata->ir->absbit); - input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4); - input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4); - - spin_lock_init(&wdata->qlock); - INIT_WORK(&wdata->worker, wiimote_worker); + spin_lock_init(&wdata->queue.lock); + INIT_WORK(&wdata->queue.worker, wiimote_queue_worker); spin_lock_init(&wdata->state.lock); init_completion(&wdata->state.ready); mutex_init(&wdata->state.sync); wdata->state.drm = WIIPROTO_REQ_DRM_K; + wdata->state.cmd_battery = 0xff; - return wdata; + INIT_WORK(&wdata->init_worker, wiimote_init_worker); + setup_timer(&wdata->timer, wiimote_init_timeout, (long)wdata); -err_ir: - input_free_device(wdata->accel); -err_input: - input_free_device(wdata->input); -err: - kfree(wdata); - return NULL; + return wdata; } static void wiimote_destroy(struct wiimote_data *wdata) { + unsigned long flags; + wiidebug_deinit(wdata); - wiiext_deinit(wdata); - wiimote_leds_destroy(wdata); - - power_supply_unregister(&wdata->battery); - input_unregister_device(wdata->accel); - input_unregister_device(wdata->ir); - input_unregister_device(wdata->input); - cancel_work_sync(&wdata->worker); + + /* prevent init_worker from being scheduled again */ + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_EXITING; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + cancel_work_sync(&wdata->init_worker); + del_timer_sync(&wdata->timer); + + device_remove_file(&wdata->hdev->dev, &dev_attr_devtype); + device_remove_file(&wdata->hdev->dev, &dev_attr_extension); + + wiimote_mp_unload(wdata); + wiimote_ext_unload(wdata); + wiimote_modules_unload(wdata); + cancel_work_sync(&wdata->queue.worker); + hid_hw_close(wdata->hdev); hid_hw_stop(wdata->hdev); kfree(wdata); @@ -1195,57 +1798,32 @@ static int wiimote_hid_probe(struct hid_device *hdev, goto err; } - ret = input_register_device(wdata->accel); + ret = hid_hw_open(hdev); if (ret) { - hid_err(hdev, "Cannot register input device\n"); + hid_err(hdev, "cannot start hardware I/O\n"); goto err_stop; } - ret = input_register_device(wdata->ir); + ret = device_create_file(&hdev->dev, &dev_attr_extension); if (ret) { - hid_err(hdev, "Cannot register input device\n"); - goto err_ir; + hid_err(hdev, "cannot create sysfs attribute\n"); + goto err_close; } - ret = input_register_device(wdata->input); + ret = device_create_file(&hdev->dev, &dev_attr_devtype); if (ret) { - hid_err(hdev, "Cannot register input device\n"); - goto err_input; + hid_err(hdev, "cannot create sysfs attribute\n"); + goto err_ext; } - wdata->battery.properties = wiimote_battery_props; - wdata->battery.num_properties = ARRAY_SIZE(wiimote_battery_props); - wdata->battery.get_property = wiimote_battery_get_property; - wdata->battery.name = "wiimote_battery"; - wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; - wdata->battery.use_for_apm = 0; - - ret = power_supply_register(&wdata->hdev->dev, &wdata->battery); - if (ret) { - hid_err(hdev, "Cannot register battery device\n"); - goto err_battery; - } - - power_supply_powers(&wdata->battery, &hdev->dev); - - ret = wiimote_leds_create(wdata); - if (ret) - goto err_free; - - ret = wiiext_init(wdata); - if (ret) - goto err_free; - ret = wiidebug_init(wdata); if (ret) goto err_free; hid_info(hdev, "New device registered\n"); - /* by default set led1 after device initialization */ - spin_lock_irq(&wdata->state.lock); - wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1); - spin_unlock_irq(&wdata->state.lock); + /* schedule device detection */ + wiimote_schedule(wdata); return 0; @@ -1253,21 +1831,15 @@ err_free: wiimote_destroy(wdata); return ret; -err_battery: - input_unregister_device(wdata->input); - wdata->input = NULL; -err_input: - input_unregister_device(wdata->ir); - wdata->ir = NULL; -err_ir: - input_unregister_device(wdata->accel); - wdata->accel = NULL; +err_ext: + device_remove_file(&wdata->hdev->dev, &dev_attr_extension); +err_close: + hid_hw_close(hdev); err_stop: hid_hw_stop(hdev); err: input_free_device(wdata->ir); input_free_device(wdata->accel); - input_free_device(wdata->input); kfree(wdata); return ret; } @@ -1283,6 +1855,8 @@ static void wiimote_hid_remove(struct hid_device *hdev) static const struct hid_device_id wiimote_hid_devices[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_WIIMOTE2) }, { } }; MODULE_DEVICE_TABLE(hid, wiimote_hid_devices); @@ -1294,25 +1868,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"); +MODULE_DESCRIPTION("Driver for Nintendo Wii / Wii U peripherals"); diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c index eec329197c1..c13fb5bd79e 100644 --- a/drivers/hid/hid-wiimote-debug.c +++ b/drivers/hid/hid-wiimote-debug.c @@ -1,6 +1,6 @@ /* - * Debug support for HID Nintendo Wiimote devices - * Copyright (c) 2011 David Herrmann + * Debug support for HID Nintendo Wii / Wii U peripherals + * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com> */ /* @@ -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; @@ -127,7 +127,8 @@ static int wiidebug_drm_open(struct inode *i, struct file *f) static ssize_t wiidebug_drm_write(struct file *f, const char __user *u, size_t s, loff_t *off) { - struct wiimote_debug *dbg = f->private_data; + struct seq_file *sf = f->private_data; + struct wiimote_debug *dbg = sf->private; unsigned long flags; char buf[16]; ssize_t len; @@ -140,7 +141,7 @@ static ssize_t wiidebug_drm_write(struct file *f, const char __user *u, if (copy_from_user(buf, u, len)) return -EFAULT; - buf[15] = 0; + buf[len] = 0; for (i = 0; i < WIIPROTO_REQ_MAX; ++i) { if (!wiidebug_drmmap[i]) @@ -150,10 +151,13 @@ static ssize_t wiidebug_drm_write(struct file *f, const char __user *u, } if (i == WIIPROTO_REQ_MAX) - i = simple_strtoul(buf, NULL, 10); + i = simple_strtoul(buf, NULL, 16); spin_lock_irqsave(&dbg->wdata->state.lock, flags); + dbg->wdata->state.flags &= ~WIIPROTO_FLAG_DRM_LOCKED; wiiproto_req_drm(dbg->wdata, (__u8) i); + if (i != WIIPROTO_REQ_NULL) + dbg->wdata->state.flags |= WIIPROTO_FLAG_DRM_LOCKED; spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); return len; diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c deleted file mode 100644 index 38ae87772e9..00000000000 --- a/drivers/hid/hid-wiimote-ext.c +++ /dev/null @@ -1,849 +0,0 @@ -/* - * HID driver for Nintendo Wiimote extension devices - * Copyright (c) 2011 David Herrmann - */ - -/* - * 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/atomic.h> -#include <linux/module.h> -#include <linux/spinlock.h> -#include <linux/workqueue.h> -#include "hid-wiimote.h" - -struct wiimote_ext { - struct wiimote_data *wdata; - struct work_struct worker; - struct input_dev *input; - struct input_dev *mp_input; - - atomic_t opened; - atomic_t mp_opened; - bool plugged; - bool mp_plugged; - bool motionp; - __u8 ext_type; - __u16 calib[4][3]; -}; - -enum wiiext_type { - WIIEXT_NONE, /* placeholder */ - WIIEXT_CLASSIC, /* Nintendo classic controller */ - WIIEXT_NUNCHUCK, /* Nintendo nunchuck controller */ - WIIEXT_BALANCE_BOARD, /* Nintendo balance board controller */ -}; - -enum wiiext_keys { - WIIEXT_KEY_C, - WIIEXT_KEY_Z, - WIIEXT_KEY_A, - WIIEXT_KEY_B, - WIIEXT_KEY_X, - WIIEXT_KEY_Y, - WIIEXT_KEY_ZL, - WIIEXT_KEY_ZR, - WIIEXT_KEY_PLUS, - WIIEXT_KEY_MINUS, - WIIEXT_KEY_HOME, - WIIEXT_KEY_LEFT, - WIIEXT_KEY_RIGHT, - WIIEXT_KEY_UP, - WIIEXT_KEY_DOWN, - WIIEXT_KEY_LT, - WIIEXT_KEY_RT, - WIIEXT_KEY_COUNT -}; - -static __u16 wiiext_keymap[] = { - BTN_C, /* WIIEXT_KEY_C */ - BTN_Z, /* WIIEXT_KEY_Z */ - BTN_A, /* WIIEXT_KEY_A */ - BTN_B, /* WIIEXT_KEY_B */ - BTN_X, /* WIIEXT_KEY_X */ - BTN_Y, /* WIIEXT_KEY_Y */ - BTN_TL2, /* WIIEXT_KEY_ZL */ - BTN_TR2, /* WIIEXT_KEY_ZR */ - KEY_NEXT, /* WIIEXT_KEY_PLUS */ - KEY_PREVIOUS, /* WIIEXT_KEY_MINUS */ - BTN_MODE, /* WIIEXT_KEY_HOME */ - KEY_LEFT, /* WIIEXT_KEY_LEFT */ - KEY_RIGHT, /* WIIEXT_KEY_RIGHT */ - KEY_UP, /* WIIEXT_KEY_UP */ - KEY_DOWN, /* WIIEXT_KEY_DOWN */ - BTN_TL, /* WIIEXT_KEY_LT */ - BTN_TR, /* WIIEXT_KEY_RT */ -}; - -/* disable all extensions */ -static void ext_disable(struct wiimote_ext *ext) -{ - unsigned long flags; - __u8 wmem = 0x55; - - if (!wiimote_cmd_acquire(ext->wdata)) { - wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem)); - wiimote_cmd_release(ext->wdata); - } - - spin_lock_irqsave(&ext->wdata->state.lock, flags); - ext->motionp = false; - ext->ext_type = WIIEXT_NONE; - wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL); - spin_unlock_irqrestore(&ext->wdata->state.lock, flags); -} - -static bool motionp_read(struct wiimote_ext *ext) -{ - __u8 rmem[2], wmem; - ssize_t ret; - bool avail = false; - - if (!atomic_read(&ext->mp_opened)) - return false; - - if (wiimote_cmd_acquire(ext->wdata)) - return false; - - /* initialize motion plus */ - wmem = 0x55; - ret = wiimote_cmd_write(ext->wdata, 0xa600f0, &wmem, sizeof(wmem)); - if (ret) - goto error; - - /* read motion plus ID */ - ret = wiimote_cmd_read(ext->wdata, 0xa600fe, rmem, 2); - if (ret == 2 || rmem[1] == 0x5) - avail = true; - -error: - wiimote_cmd_release(ext->wdata); - return avail; -} - -static __u8 ext_read(struct wiimote_ext *ext) -{ - ssize_t ret; - __u8 buf[24], i, j, offs = 0; - __u8 rmem[2], wmem; - __u8 type = WIIEXT_NONE; - - if (!ext->plugged || !atomic_read(&ext->opened)) - return WIIEXT_NONE; - - if (wiimote_cmd_acquire(ext->wdata)) - return WIIEXT_NONE; - - /* initialize extension */ - wmem = 0x55; - ret = wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem)); - if (!ret) { - /* disable encryption */ - wmem = 0x0; - wiimote_cmd_write(ext->wdata, 0xa400fb, &wmem, sizeof(wmem)); - } - - /* read extension ID */ - ret = wiimote_cmd_read(ext->wdata, 0xa400fe, rmem, 2); - if (ret == 2) { - if (rmem[0] == 0 && rmem[1] == 0) - type = WIIEXT_NUNCHUCK; - else if (rmem[0] == 0x01 && rmem[1] == 0x01) - type = WIIEXT_CLASSIC; - else if (rmem[0] == 0x04 && rmem[1] == 0x02) - type = WIIEXT_BALANCE_BOARD; - } - - /* get balance board calibration data */ - if (type == WIIEXT_BALANCE_BOARD) { - ret = wiimote_cmd_read(ext->wdata, 0xa40024, buf, 12); - ret += wiimote_cmd_read(ext->wdata, 0xa40024 + 12, - buf + 12, 12); - - if (ret != 24) { - type = WIIEXT_NONE; - } else { - for (i = 0; i < 3; i++) { - for (j = 0; j < 4; j++) { - ext->calib[j][i] = buf[offs]; - ext->calib[j][i] <<= 8; - ext->calib[j][i] |= buf[offs + 1]; - offs += 2; - } - } - } - } - - wiimote_cmd_release(ext->wdata); - - return type; -} - -static void ext_enable(struct wiimote_ext *ext, bool motionp, __u8 ext_type) -{ - unsigned long flags; - __u8 wmem; - int ret; - - if (motionp) { - if (wiimote_cmd_acquire(ext->wdata)) - return; - - if (ext_type == WIIEXT_CLASSIC) - wmem = 0x07; - else if (ext_type == WIIEXT_NUNCHUCK) - wmem = 0x05; - else - wmem = 0x04; - - ret = wiimote_cmd_write(ext->wdata, 0xa600fe, &wmem, sizeof(wmem)); - wiimote_cmd_release(ext->wdata); - if (ret) - return; - } - - spin_lock_irqsave(&ext->wdata->state.lock, flags); - ext->motionp = motionp; - ext->ext_type = ext_type; - wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL); - spin_unlock_irqrestore(&ext->wdata->state.lock, flags); -} - -static void wiiext_worker(struct work_struct *work) -{ - struct wiimote_ext *ext = container_of(work, struct wiimote_ext, - worker); - bool motionp; - __u8 ext_type; - - ext_disable(ext); - motionp = motionp_read(ext); - ext_type = ext_read(ext); - ext_enable(ext, motionp, ext_type); -} - -/* schedule work only once, otherwise mark for reschedule */ -static void wiiext_schedule(struct wiimote_ext *ext) -{ - schedule_work(&ext->worker); -} - -/* - * Reacts on extension port events - * Whenever the driver gets an event from the wiimote that an extension has been - * plugged or unplugged, this funtion shall be called. It checks what extensions - * are connected and initializes and activates them. - * This can be called in atomic context. The initialization is done in a - * separate worker thread. The state.lock spinlock must be held by the caller. - */ -void wiiext_event(struct wiimote_data *wdata, bool plugged) -{ - if (!wdata->ext) - return; - - if (wdata->ext->plugged == plugged) - return; - - wdata->ext->plugged = plugged; - - if (!plugged) - wdata->ext->mp_plugged = false; - - /* - * We need to call wiiext_schedule(wdata->ext) here, however, the - * extension initialization logic is not fully understood and so - * automatic initialization is not supported, yet. - */ -} - -/* - * Returns true if the current DRM mode should contain extension data and false - * if there is no interest in extension data. - * All supported extensions send 6 byte extension data so any DRM that contains - * extension bytes is fine. - * The caller must hold the state.lock spinlock. - */ -bool wiiext_active(struct wiimote_data *wdata) -{ - if (!wdata->ext) - return false; - - return wdata->ext->motionp || wdata->ext->ext_type; -} - -static void handler_motionp(struct wiimote_ext *ext, const __u8 *payload) -{ - __s32 x, y, z; - bool plugged; - - /* | 8 7 6 5 4 3 | 2 | 1 | - * -----+------------------------------+-----+-----+ - * 1 | Yaw Speed <7:0> | - * 2 | Roll Speed <7:0> | - * 3 | Pitch Speed <7:0> | - * -----+------------------------------+-----+-----+ - * 4 | Yaw Speed <13:8> | Yaw |Pitch| - * -----+------------------------------+-----+-----+ - * 5 | Roll Speed <13:8> |Roll | Ext | - * -----+------------------------------+-----+-----+ - * 6 | Pitch Speed <13:8> | 1 | 0 | - * -----+------------------------------+-----+-----+ - * The single bits Yaw, Roll, Pitch in the lower right corner specify - * whether the wiimote is rotating fast (0) or slow (1). Speed for slow - * roation is 440 deg/s and for fast rotation 2000 deg/s. To get a - * linear scale we multiply by 2000/440 = ~4.5454 which is 18 for fast - * and 9 for slow. - * If the wiimote is not rotating the sensor reports 2^13 = 8192. - * Ext specifies whether an extension is connected to the motionp. - */ - - x = payload[0]; - y = payload[1]; - z = payload[2]; - - x |= (((__u16)payload[3]) << 6) & 0xff00; - y |= (((__u16)payload[4]) << 6) & 0xff00; - z |= (((__u16)payload[5]) << 6) & 0xff00; - - x -= 8192; - y -= 8192; - z -= 8192; - - if (!(payload[3] & 0x02)) - x *= 18; - else - x *= 9; - if (!(payload[4] & 0x02)) - y *= 18; - else - y *= 9; - if (!(payload[3] & 0x01)) - z *= 18; - else - z *= 9; - - input_report_abs(ext->mp_input, ABS_RX, x); - input_report_abs(ext->mp_input, ABS_RY, y); - input_report_abs(ext->mp_input, ABS_RZ, z); - input_sync(ext->mp_input); - - plugged = payload[5] & 0x01; - if (plugged != ext->mp_plugged) - ext->mp_plugged = plugged; -} - -static void handler_nunchuck(struct wiimote_ext *ext, const __u8 *payload) -{ - __s16 x, y, z, bx, by; - - /* Byte | 8 7 | 6 5 | 4 3 | 2 | 1 | - * -----+----------+---------+---------+----+-----+ - * 1 | Button X <7:0> | - * 2 | Button Y <7:0> | - * -----+----------+---------+---------+----+-----+ - * 3 | Speed X <9:2> | - * 4 | Speed Y <9:2> | - * 5 | Speed Z <9:2> | - * -----+----------+---------+---------+----+-----+ - * 6 | Z <1:0> | Y <1:0> | X <1:0> | BC | BZ | - * -----+----------+---------+---------+----+-----+ - * Button X/Y is the analog stick. Speed X, Y and Z are the - * accelerometer data in the same format as the wiimote's accelerometer. - * The 6th byte contains the LSBs of the accelerometer data. - * BC and BZ are the C and Z buttons: 0 means pressed - * - * If reported interleaved with motionp, then the layout changes. The - * 5th and 6th byte changes to: - * -----+-----------------------------------+-----+ - * 5 | Speed Z <9:3> | EXT | - * -----+--------+-----+-----+----+----+----+-----+ - * 6 |Z <2:1> |Y <1>|X <1>| BC | BZ | 0 | 0 | - * -----+--------+-----+-----+----+----+----+-----+ - * All three accelerometer values lose their LSB. The other data is - * still available but slightly moved. - * - * Center data for button values is 128. Center value for accelerometer - * values it 512 / 0x200 - */ - - bx = payload[0]; - by = payload[1]; - bx -= 128; - by -= 128; - - x = payload[2] << 2; - y = payload[3] << 2; - z = payload[4] << 2; - - if (ext->motionp) { - x |= (payload[5] >> 3) & 0x02; - y |= (payload[5] >> 4) & 0x02; - z &= ~0x4; - z |= (payload[5] >> 5) & 0x06; - } else { - x |= (payload[5] >> 2) & 0x03; - y |= (payload[5] >> 4) & 0x03; - z |= (payload[5] >> 6) & 0x03; - } - - x -= 0x200; - y -= 0x200; - z -= 0x200; - - input_report_abs(ext->input, ABS_HAT0X, bx); - input_report_abs(ext->input, ABS_HAT0Y, by); - - input_report_abs(ext->input, ABS_RX, x); - input_report_abs(ext->input, ABS_RY, y); - input_report_abs(ext->input, ABS_RZ, z); - - if (ext->motionp) { - input_report_key(ext->input, - wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x04)); - input_report_key(ext->input, - wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x08)); - } else { - input_report_key(ext->input, - wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x01)); - input_report_key(ext->input, - wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x02)); - } - - input_sync(ext->input); -} - -static void handler_classic(struct wiimote_ext *ext, const __u8 *payload) -{ - __s8 rx, ry, lx, ly, lt, rt; - - /* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | - * -----+-----+-----+-----+-----+-----+-----+-----+-----+ - * 1 | RX <5:4> | LX <5:0> | - * 2 | RX <3:2> | LY <5:0> | - * -----+-----+-----+-----+-----------------------------+ - * 3 |RX<1>| LT <5:4> | RY <5:1> | - * -----+-----+-----------+-----------------------------+ - * 4 | LT <3:1> | RT <5:1> | - * -----+-----+-----+-----+-----+-----+-----+-----+-----+ - * 5 | BDR | BDD | BLT | B- | BH | B+ | BRT | 1 | - * -----+-----+-----+-----+-----+-----+-----+-----+-----+ - * 6 | BZL | BB | BY | BA | BX | BZR | BDL | BDU | - * -----+-----+-----+-----+-----+-----+-----+-----+-----+ - * All buttons are 0 if pressed - * RX and RY are right analog stick - * LX and LY are left analog stick - * LT is left trigger, RT is right trigger - * BLT is 0 if left trigger is fully pressed - * BRT is 0 if right trigger is fully pressed - * BDR, BDD, BDL, BDU form the D-Pad with right, down, left, up buttons - * BZL is left Z button and BZR is right Z button - * B-, BH, B+ are +, HOME and - buttons - * BB, BY, BA, BX are A, B, X, Y buttons - * LSB of RX, RY, LT, and RT are not transmitted and always 0. - * - * With motionp enabled it changes slightly to this: - * Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | - * -----+-----+-----+-----+-----+-----+-----+-----+-----+ - * 1 | RX <4:3> | LX <5:1> | BDU | - * 2 | RX <2:1> | LY <5:1> | BDL | - * -----+-----+-----+-----+-----------------------+-----+ - * 3 |RX<0>| LT <4:3> | RY <4:0> | - * -----+-----+-----------+-----------------------------+ - * 4 | LT <2:0> | RT <4:0> | - * -----+-----+-----+-----+-----+-----+-----+-----+-----+ - * 5 | BDR | BDD | BLT | B- | BH | B+ | BRT | EXT | - * -----+-----+-----+-----+-----+-----+-----+-----+-----+ - * 6 | BZL | BB | BY | BA | BX | BZR | 0 | 0 | - * -----+-----+-----+-----+-----+-----+-----+-----+-----+ - * Only the LSBs of LX and LY are lost. BDU and BDL are moved, the rest - * is the same as before. - */ - - if (ext->motionp) { - lx = payload[0] & 0x3e; - ly = payload[0] & 0x3e; - } else { - lx = payload[0] & 0x3f; - ly = payload[0] & 0x3f; - } - - rx = (payload[0] >> 3) & 0x14; - rx |= (payload[1] >> 5) & 0x06; - rx |= (payload[2] >> 7) & 0x01; - ry = payload[2] & 0x1f; - - rt = payload[3] & 0x1f; - lt = (payload[2] >> 2) & 0x18; - lt |= (payload[3] >> 5) & 0x07; - - rx <<= 1; - ry <<= 1; - rt <<= 1; - lt <<= 1; - - input_report_abs(ext->input, ABS_HAT1X, lx - 0x20); - input_report_abs(ext->input, ABS_HAT1Y, ly - 0x20); - input_report_abs(ext->input, ABS_HAT2X, rx - 0x20); - input_report_abs(ext->input, ABS_HAT2Y, ry - 0x20); - input_report_abs(ext->input, ABS_HAT3X, rt - 0x20); - input_report_abs(ext->input, ABS_HAT3Y, lt - 0x20); - - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RIGHT], - !!(payload[4] & 0x80)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_DOWN], - !!(payload[4] & 0x40)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LT], - !!(payload[4] & 0x20)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_MINUS], - !!(payload[4] & 0x10)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_HOME], - !!(payload[4] & 0x08)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_PLUS], - !!(payload[4] & 0x04)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RT], - !!(payload[4] & 0x02)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZL], - !!(payload[5] & 0x80)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_B], - !!(payload[5] & 0x40)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_Y], - !!(payload[5] & 0x20)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_A], - !!(payload[5] & 0x10)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_X], - !!(payload[5] & 0x08)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZR], - !!(payload[5] & 0x04)); - - if (ext->motionp) { - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP], - !!(payload[0] & 0x01)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT], - !!(payload[1] & 0x01)); - } else { - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP], - !!(payload[5] & 0x01)); - input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT], - !!(payload[5] & 0x02)); - } - - input_sync(ext->input); -} - -static void handler_balance_board(struct wiimote_ext *ext, const __u8 *payload) -{ - __s32 val[4], tmp; - unsigned int i; - - /* Byte | 8 7 6 5 4 3 2 1 | - * -----+--------------------------+ - * 1 | Top Right <15:8> | - * 2 | Top Right <7:0> | - * -----+--------------------------+ - * 3 | Bottom Right <15:8> | - * 4 | Bottom Right <7:0> | - * -----+--------------------------+ - * 5 | Top Left <15:8> | - * 6 | Top Left <7:0> | - * -----+--------------------------+ - * 7 | Bottom Left <15:8> | - * 8 | Bottom Left <7:0> | - * -----+--------------------------+ - * - * These values represent the weight-measurements of the Wii-balance - * board with 16bit precision. - * - * The balance-board is never reported interleaved with motionp. - */ - - val[0] = payload[0]; - val[0] <<= 8; - val[0] |= payload[1]; - - val[1] = payload[2]; - val[1] <<= 8; - val[1] |= payload[3]; - - val[2] = payload[4]; - val[2] <<= 8; - val[2] |= payload[5]; - - val[3] = payload[6]; - val[3] <<= 8; - val[3] |= payload[7]; - - /* apply calibration data */ - for (i = 0; i < 4; i++) { - if (val[i] < ext->calib[i][1]) { - tmp = val[i] - ext->calib[i][0]; - tmp *= 1700; - tmp /= ext->calib[i][1] - ext->calib[i][0]; - } else { - tmp = val[i] - ext->calib[i][1]; - tmp *= 1700; - tmp /= ext->calib[i][2] - ext->calib[i][1]; - tmp += 1700; - } - val[i] = tmp; - } - - input_report_abs(ext->input, ABS_HAT0X, val[0]); - input_report_abs(ext->input, ABS_HAT0Y, val[1]); - input_report_abs(ext->input, ABS_HAT1X, val[2]); - input_report_abs(ext->input, ABS_HAT1Y, val[3]); - - input_sync(ext->input); -} - -/* call this with state.lock spinlock held */ -void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload) -{ - struct wiimote_ext *ext = wdata->ext; - - if (!ext) - return; - - if (ext->motionp && (payload[5] & 0x02)) { - handler_motionp(ext, payload); - } else if (ext->ext_type == WIIEXT_NUNCHUCK) { - handler_nunchuck(ext, payload); - } else if (ext->ext_type == WIIEXT_CLASSIC) { - handler_classic(ext, payload); - } else if (ext->ext_type == WIIEXT_BALANCE_BOARD) { - handler_balance_board(ext, payload); - } -} - -static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct wiimote_data *wdata = dev_to_wii(dev); - __u8 type = WIIEXT_NONE; - bool motionp = false; - unsigned long flags; - - spin_lock_irqsave(&wdata->state.lock, flags); - if (wdata->ext) { - motionp = wdata->ext->motionp; - type = wdata->ext->ext_type; - } - spin_unlock_irqrestore(&wdata->state.lock, flags); - - if (type == WIIEXT_NUNCHUCK) { - if (motionp) - return sprintf(buf, "motionp+nunchuck\n"); - else - return sprintf(buf, "nunchuck\n"); - } else if (type == WIIEXT_CLASSIC) { - if (motionp) - return sprintf(buf, "motionp+classic\n"); - else - return sprintf(buf, "classic\n"); - } else if (type == WIIEXT_BALANCE_BOARD) { - if (motionp) - return sprintf(buf, "motionp+balanceboard\n"); - else - return sprintf(buf, "balanceboard\n"); - } else { - if (motionp) - return sprintf(buf, "motionp\n"); - else - return sprintf(buf, "none\n"); - } -} - -static DEVICE_ATTR(extension, S_IRUGO, wiiext_show, NULL); - -static int wiiext_input_open(struct input_dev *dev) -{ - struct wiimote_ext *ext = input_get_drvdata(dev); - int ret; - - ret = hid_hw_open(ext->wdata->hdev); - if (ret) - return ret; - - atomic_inc(&ext->opened); - wiiext_schedule(ext); - - return 0; -} - -static void wiiext_input_close(struct input_dev *dev) -{ - struct wiimote_ext *ext = input_get_drvdata(dev); - - atomic_dec(&ext->opened); - wiiext_schedule(ext); - hid_hw_close(ext->wdata->hdev); -} - -static int wiiext_mp_open(struct input_dev *dev) -{ - struct wiimote_ext *ext = input_get_drvdata(dev); - int ret; - - ret = hid_hw_open(ext->wdata->hdev); - if (ret) - return ret; - - atomic_inc(&ext->mp_opened); - wiiext_schedule(ext); - - return 0; -} - -static void wiiext_mp_close(struct input_dev *dev) -{ - struct wiimote_ext *ext = input_get_drvdata(dev); - - atomic_dec(&ext->mp_opened); - wiiext_schedule(ext); - hid_hw_close(ext->wdata->hdev); -} - -/* Initializes the extension driver of a wiimote */ -int wiiext_init(struct wiimote_data *wdata) -{ - struct wiimote_ext *ext; - unsigned long flags; - int ret, i; - - ext = kzalloc(sizeof(*ext), GFP_KERNEL); - if (!ext) - return -ENOMEM; - - ext->wdata = wdata; - INIT_WORK(&ext->worker, wiiext_worker); - - ext->input = input_allocate_device(); - if (!ext->input) { - ret = -ENOMEM; - goto err_input; - } - - input_set_drvdata(ext->input, ext); - ext->input->open = wiiext_input_open; - ext->input->close = wiiext_input_close; - ext->input->dev.parent = &wdata->hdev->dev; - ext->input->id.bustype = wdata->hdev->bus; - ext->input->id.vendor = wdata->hdev->vendor; - ext->input->id.product = wdata->hdev->product; - ext->input->id.version = wdata->hdev->version; - ext->input->name = WIIMOTE_NAME " Extension"; - - set_bit(EV_KEY, ext->input->evbit); - for (i = 0; i < WIIEXT_KEY_COUNT; ++i) - set_bit(wiiext_keymap[i], ext->input->keybit); - - set_bit(EV_ABS, ext->input->evbit); - set_bit(ABS_HAT0X, ext->input->absbit); - set_bit(ABS_HAT0Y, ext->input->absbit); - set_bit(ABS_HAT1X, ext->input->absbit); - set_bit(ABS_HAT1Y, ext->input->absbit); - set_bit(ABS_HAT2X, ext->input->absbit); - set_bit(ABS_HAT2Y, ext->input->absbit); - set_bit(ABS_HAT3X, ext->input->absbit); - set_bit(ABS_HAT3Y, ext->input->absbit); - input_set_abs_params(ext->input, ABS_HAT0X, -120, 120, 2, 4); - input_set_abs_params(ext->input, ABS_HAT0Y, -120, 120, 2, 4); - input_set_abs_params(ext->input, ABS_HAT1X, -30, 30, 1, 1); - input_set_abs_params(ext->input, ABS_HAT1Y, -30, 30, 1, 1); - input_set_abs_params(ext->input, ABS_HAT2X, -30, 30, 1, 1); - input_set_abs_params(ext->input, ABS_HAT2Y, -30, 30, 1, 1); - input_set_abs_params(ext->input, ABS_HAT3X, -30, 30, 1, 1); - input_set_abs_params(ext->input, ABS_HAT3Y, -30, 30, 1, 1); - set_bit(ABS_RX, ext->input->absbit); - set_bit(ABS_RY, ext->input->absbit); - set_bit(ABS_RZ, ext->input->absbit); - input_set_abs_params(ext->input, ABS_RX, -500, 500, 2, 4); - input_set_abs_params(ext->input, ABS_RY, -500, 500, 2, 4); - input_set_abs_params(ext->input, ABS_RZ, -500, 500, 2, 4); - - ret = input_register_device(ext->input); - if (ret) { - input_free_device(ext->input); - goto err_input; - } - - ext->mp_input = input_allocate_device(); - if (!ext->mp_input) { - ret = -ENOMEM; - goto err_mp; - } - - input_set_drvdata(ext->mp_input, ext); - ext->mp_input->open = wiiext_mp_open; - ext->mp_input->close = wiiext_mp_close; - ext->mp_input->dev.parent = &wdata->hdev->dev; - ext->mp_input->id.bustype = wdata->hdev->bus; - ext->mp_input->id.vendor = wdata->hdev->vendor; - ext->mp_input->id.product = wdata->hdev->product; - ext->mp_input->id.version = wdata->hdev->version; - ext->mp_input->name = WIIMOTE_NAME " Motion+"; - - set_bit(EV_ABS, ext->mp_input->evbit); - set_bit(ABS_RX, ext->mp_input->absbit); - set_bit(ABS_RY, ext->mp_input->absbit); - set_bit(ABS_RZ, ext->mp_input->absbit); - input_set_abs_params(ext->mp_input, ABS_RX, -160000, 160000, 4, 8); - input_set_abs_params(ext->mp_input, ABS_RY, -160000, 160000, 4, 8); - input_set_abs_params(ext->mp_input, ABS_RZ, -160000, 160000, 4, 8); - - ret = input_register_device(ext->mp_input); - if (ret) { - input_free_device(ext->mp_input); - goto err_mp; - } - - ret = device_create_file(&wdata->hdev->dev, &dev_attr_extension); - if (ret) - goto err_dev; - - spin_lock_irqsave(&wdata->state.lock, flags); - wdata->ext = ext; - spin_unlock_irqrestore(&wdata->state.lock, flags); - - return 0; - -err_dev: - input_unregister_device(ext->mp_input); -err_mp: - input_unregister_device(ext->input); -err_input: - kfree(ext); - return ret; -} - -/* Deinitializes the extension driver of a wiimote */ -void wiiext_deinit(struct wiimote_data *wdata) -{ - struct wiimote_ext *ext = wdata->ext; - unsigned long flags; - - if (!ext) - return; - - /* - * We first unset wdata->ext to avoid further input from the wiimote - * core. The worker thread does not access this pointer so it is not - * affected by this. - * We kill the worker after this so it does not get respawned during - * deinitialization. - */ - - spin_lock_irqsave(&wdata->state.lock, flags); - wdata->ext = NULL; - spin_unlock_irqrestore(&wdata->state.lock, flags); - - device_remove_file(&wdata->hdev->dev, &dev_attr_extension); - input_unregister_device(ext->mp_input); - input_unregister_device(ext->input); - - cancel_work_sync(&ext->worker); - kfree(ext); -} diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c new file mode 100644 index 00000000000..6b61f01e01e --- /dev/null +++ b/drivers/hid/hid-wiimote-modules.c @@ -0,0 +1,2203 @@ +/* + * Device Modules for Nintendo Wii / Wii U HID Driver + * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com> + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +/* + * Wiimote Modules + * Nintendo devices provide different peripherals and many new devices lack + * initial features like the IR camera. Therefore, each peripheral device is + * implemented as an independent module and we probe on each device only the + * modules for the hardware that really is available. + * + * Module registration is sequential. Unregistration is done in reverse order. + * After device detection, the needed modules are loaded. Users can trigger + * re-detection which causes all modules to be unloaded and then reload the + * modules for the new detected device. + * + * wdata->input is a shared input device. It is always initialized prior to + * module registration. If at least one registered module is marked as + * WIIMOD_FLAG_INPUT, then the input device will get registered after all + * modules were registered. + * Please note that it is unregistered _before_ the "remove" callbacks are + * called. This guarantees that no input interaction is done, anymore. However, + * the wiimote core keeps a reference to the input device so it is freed only + * after all modules were removed. It is safe to send events to unregistered + * input devices. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/input.h> +#include <linux/spinlock.h> +#include "hid-wiimote.h" + +/* + * Keys + * The initial Wii Remote provided a bunch of buttons that are reported as + * part of the core protocol. Many later devices dropped these and report + * invalid data in the core button reports. Load this only on devices which + * correctly send button reports. + * It uses the shared input device. + */ + +static const __u16 wiimod_keys_map[] = { + KEY_LEFT, /* WIIPROTO_KEY_LEFT */ + KEY_RIGHT, /* WIIPROTO_KEY_RIGHT */ + KEY_UP, /* WIIPROTO_KEY_UP */ + KEY_DOWN, /* WIIPROTO_KEY_DOWN */ + KEY_NEXT, /* WIIPROTO_KEY_PLUS */ + KEY_PREVIOUS, /* WIIPROTO_KEY_MINUS */ + BTN_1, /* WIIPROTO_KEY_ONE */ + BTN_2, /* WIIPROTO_KEY_TWO */ + BTN_A, /* WIIPROTO_KEY_A */ + BTN_B, /* WIIPROTO_KEY_B */ + BTN_MODE, /* WIIPROTO_KEY_HOME */ +}; + +static void wiimod_keys_in_keys(struct wiimote_data *wdata, const __u8 *keys) +{ + input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_LEFT], + !!(keys[0] & 0x01)); + input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_RIGHT], + !!(keys[0] & 0x02)); + input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_DOWN], + !!(keys[0] & 0x04)); + input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_UP], + !!(keys[0] & 0x08)); + input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_PLUS], + !!(keys[0] & 0x10)); + input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_TWO], + !!(keys[1] & 0x01)); + input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_ONE], + !!(keys[1] & 0x02)); + input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_B], + !!(keys[1] & 0x04)); + input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_A], + !!(keys[1] & 0x08)); + input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_MINUS], + !!(keys[1] & 0x10)); + input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_HOME], + !!(keys[1] & 0x80)); + input_sync(wdata->input); +} + +static int wiimod_keys_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + unsigned int i; + + set_bit(EV_KEY, wdata->input->evbit); + for (i = 0; i < WIIPROTO_KEY_COUNT; ++i) + set_bit(wiimod_keys_map[i], wdata->input->keybit); + + return 0; +} + +static const struct wiimod_ops wiimod_keys = { + .flags = WIIMOD_FLAG_INPUT, + .arg = 0, + .probe = wiimod_keys_probe, + .remove = NULL, + .in_keys = wiimod_keys_in_keys, +}; + +/* + * Rumble + * Nearly all devices provide a rumble feature. A small motor for + * force-feedback effects. We provide an FF_RUMBLE memless ff device on the + * shared input device if this module is loaded. + * The rumble motor is controlled via a flag on almost every output report so + * the wiimote core handles the rumble flag. But if a device doesn't provide + * the rumble motor, this flag shouldn't be set. + */ + +/* used by wiimod_rumble and wiipro_rumble */ +static void wiimod_rumble_worker(struct work_struct *work) +{ + struct wiimote_data *wdata = container_of(work, struct wiimote_data, + rumble_worker); + + spin_lock_irq(&wdata->state.lock); + wiiproto_req_rumble(wdata, wdata->state.cache_rumble); + spin_unlock_irq(&wdata->state.lock); +} + +static int wiimod_rumble_play(struct input_dev *dev, void *data, + struct ff_effect *eff) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + __u8 value; + + /* + * The wiimote supports only a single rumble motor so if any magnitude + * is set to non-zero then we start the rumble motor. If both are set to + * zero, we stop the rumble motor. + */ + + if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude) + value = 1; + else + value = 0; + + /* Locking state.lock here might deadlock with input_event() calls. + * schedule_work acts as barrier. Merging multiple changes is fine. */ + wdata->state.cache_rumble = value; + schedule_work(&wdata->rumble_worker); + + return 0; +} + +static int wiimod_rumble_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker); + + set_bit(FF_RUMBLE, wdata->input->ffbit); + if (input_ff_create_memless(wdata->input, NULL, wiimod_rumble_play)) + return -ENOMEM; + + return 0; +} + +static void wiimod_rumble_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + unsigned long flags; + + cancel_work_sync(&wdata->rumble_worker); + + spin_lock_irqsave(&wdata->state.lock, flags); + wiiproto_req_rumble(wdata, 0); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static const struct wiimod_ops wiimod_rumble = { + .flags = WIIMOD_FLAG_INPUT, + .arg = 0, + .probe = wiimod_rumble_probe, + .remove = wiimod_rumble_remove, +}; + +/* + * Battery + * 1 byte of battery capacity information is sent along every protocol status + * report. The wiimote core caches it but we try to update it on every + * user-space request. + * This is supported by nearly every device so it's almost always enabled. + */ + +static enum power_supply_property wiimod_battery_props[] = { + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_SCOPE, +}; + +static int wiimod_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wiimote_data *wdata = container_of(psy, struct wiimote_data, + battery); + int ret = 0, state; + unsigned long flags; + + if (psp == POWER_SUPPLY_PROP_SCOPE) { + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + return 0; + } else if (psp != POWER_SUPPLY_PROP_CAPACITY) { + return -EINVAL; + } + + ret = wiimote_cmd_acquire(wdata); + if (ret) + return ret; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0); + wiiproto_req_status(wdata); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + wiimote_cmd_wait(wdata); + wiimote_cmd_release(wdata); + + spin_lock_irqsave(&wdata->state.lock, flags); + state = wdata->state.cmd_battery; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + val->intval = state * 100 / 255; + return ret; +} + +static int wiimod_battery_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret; + + wdata->battery.properties = wiimod_battery_props; + wdata->battery.num_properties = ARRAY_SIZE(wiimod_battery_props); + wdata->battery.get_property = wiimod_battery_get_property; + wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; + wdata->battery.use_for_apm = 0; + wdata->battery.name = kasprintf(GFP_KERNEL, "wiimote_battery_%s", + wdata->hdev->uniq); + if (!wdata->battery.name) + return -ENOMEM; + + ret = power_supply_register(&wdata->hdev->dev, &wdata->battery); + if (ret) { + hid_err(wdata->hdev, "cannot register battery device\n"); + goto err_free; + } + + power_supply_powers(&wdata->battery, &wdata->hdev->dev); + return 0; + +err_free: + kfree(wdata->battery.name); + wdata->battery.name = NULL; + return ret; +} + +static void wiimod_battery_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->battery.name) + return; + + power_supply_unregister(&wdata->battery); + kfree(wdata->battery.name); + wdata->battery.name = NULL; +} + +static const struct wiimod_ops wiimod_battery = { + .flags = 0, + .arg = 0, + .probe = wiimod_battery_probe, + .remove = wiimod_battery_remove, +}; + +/* + * LED + * 0 to 4 player LEDs are supported by devices. The "arg" field of the + * wiimod_ops structure specifies which LED this module controls. This allows + * to register a limited number of LEDs. + * State is managed by wiimote core. + */ + +static enum led_brightness wiimod_led_get(struct led_classdev *led_dev) +{ + struct wiimote_data *wdata; + struct device *dev = led_dev->dev->parent; + int i; + unsigned long flags; + bool value = false; + + wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); + + for (i = 0; i < 4; ++i) { + if (wdata->leds[i] == led_dev) { + spin_lock_irqsave(&wdata->state.lock, flags); + value = wdata->state.flags & WIIPROTO_FLAG_LED(i + 1); + spin_unlock_irqrestore(&wdata->state.lock, flags); + break; + } + } + + return value ? LED_FULL : LED_OFF; +} + +static void wiimod_led_set(struct led_classdev *led_dev, + enum led_brightness value) +{ + struct wiimote_data *wdata; + struct device *dev = led_dev->dev->parent; + int i; + unsigned long flags; + __u8 state, flag; + + wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); + + for (i = 0; i < 4; ++i) { + if (wdata->leds[i] == led_dev) { + flag = WIIPROTO_FLAG_LED(i + 1); + spin_lock_irqsave(&wdata->state.lock, flags); + state = wdata->state.flags; + if (value == LED_OFF) + wiiproto_req_leds(wdata, state & ~flag); + else + wiiproto_req_leds(wdata, state | flag); + spin_unlock_irqrestore(&wdata->state.lock, flags); + break; + } + } +} + +static int wiimod_led_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + struct device *dev = &wdata->hdev->dev; + size_t namesz = strlen(dev_name(dev)) + 9; + struct led_classdev *led; + unsigned long flags; + char *name; + int ret; + + led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL); + if (!led) + return -ENOMEM; + + name = (void*)&led[1]; + snprintf(name, namesz, "%s:blue:p%lu", dev_name(dev), ops->arg); + led->name = name; + led->brightness = 0; + led->max_brightness = 1; + led->brightness_get = wiimod_led_get; + led->brightness_set = wiimod_led_set; + + wdata->leds[ops->arg] = led; + ret = led_classdev_register(dev, led); + if (ret) + goto err_free; + + /* enable LED1 to stop initial LED-blinking */ + if (ops->arg == 0) { + spin_lock_irqsave(&wdata->state.lock, flags); + wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1); + spin_unlock_irqrestore(&wdata->state.lock, flags); + } + + return 0; + +err_free: + wdata->leds[ops->arg] = NULL; + kfree(led); + return ret; +} + +static void wiimod_led_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->leds[ops->arg]) + return; + + led_classdev_unregister(wdata->leds[ops->arg]); + kfree(wdata->leds[ops->arg]); + wdata->leds[ops->arg] = NULL; +} + +static const struct wiimod_ops wiimod_leds[4] = { + { + .flags = 0, + .arg = 0, + .probe = wiimod_led_probe, + .remove = wiimod_led_remove, + }, + { + .flags = 0, + .arg = 1, + .probe = wiimod_led_probe, + .remove = wiimod_led_remove, + }, + { + .flags = 0, + .arg = 2, + .probe = wiimod_led_probe, + .remove = wiimod_led_remove, + }, + { + .flags = 0, + .arg = 3, + .probe = wiimod_led_probe, + .remove = wiimod_led_remove, + }, +}; + +/* + * Accelerometer + * 3 axis accelerometer data is part of nearly all DRMs. If not supported by a + * device, it's mostly cleared to 0. This module parses this data and provides + * it via a separate input device. + */ + +static void wiimod_accel_in_accel(struct wiimote_data *wdata, + const __u8 *accel) +{ + __u16 x, y, z; + + if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL)) + return; + + /* + * payload is: BB BB XX YY ZZ + * Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ + * contain the upper 8 bits of each value. The lower 2 bits are + * contained in the buttons data BB BB. + * Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the + * X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y + * accel value and bit 6 is the second bit of the Z value. + * The first bit of Y and Z values is not available and always set to 0. + * 0x200 is returned on no movement. + */ + + x = accel[2] << 2; + y = accel[3] << 2; + z = accel[4] << 2; + + x |= (accel[0] >> 5) & 0x3; + y |= (accel[1] >> 4) & 0x2; + z |= (accel[1] >> 5) & 0x2; + + input_report_abs(wdata->accel, ABS_RX, x - 0x200); + input_report_abs(wdata->accel, ABS_RY, y - 0x200); + input_report_abs(wdata->accel, ABS_RZ, z - 0x200); + input_sync(wdata->accel); +} + +static int wiimod_accel_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiiproto_req_accel(wdata, true); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_accel_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiiproto_req_accel(wdata, false); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static int wiimod_accel_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret; + + wdata->accel = input_allocate_device(); + if (!wdata->accel) + return -ENOMEM; + + input_set_drvdata(wdata->accel, wdata); + wdata->accel->open = wiimod_accel_open; + wdata->accel->close = wiimod_accel_close; + wdata->accel->dev.parent = &wdata->hdev->dev; + wdata->accel->id.bustype = wdata->hdev->bus; + wdata->accel->id.vendor = wdata->hdev->vendor; + wdata->accel->id.product = wdata->hdev->product; + wdata->accel->id.version = wdata->hdev->version; + wdata->accel->name = WIIMOTE_NAME " Accelerometer"; + + set_bit(EV_ABS, wdata->accel->evbit); + set_bit(ABS_RX, wdata->accel->absbit); + set_bit(ABS_RY, wdata->accel->absbit); + set_bit(ABS_RZ, wdata->accel->absbit); + input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4); + input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4); + input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4); + + ret = input_register_device(wdata->accel); + if (ret) { + hid_err(wdata->hdev, "cannot register input device\n"); + goto err_free; + } + + return 0; + +err_free: + input_free_device(wdata->accel); + wdata->accel = NULL; + return ret; +} + +static void wiimod_accel_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->accel) + return; + + input_unregister_device(wdata->accel); + wdata->accel = NULL; +} + +static const struct wiimod_ops wiimod_accel = { + .flags = 0, + .arg = 0, + .probe = wiimod_accel_probe, + .remove = wiimod_accel_remove, + .in_accel = wiimod_accel_in_accel, +}; + +/* + * IR Cam + * Up to 4 IR sources can be tracked by a normal Wii Remote. The IR cam needs + * to be initialized with a fairly complex procedure and consumes a lot of + * power. Therefore, as long as no application uses the IR input device, it is + * kept offline. + * Nearly no other device than the normal Wii Remotes supports the IR cam so + * you can disable this module for these devices. + */ + +static void wiimod_ir_in_ir(struct wiimote_data *wdata, const __u8 *ir, + bool packed, unsigned int id) +{ + __u16 x, y; + __u8 xid, yid; + bool sync = false; + + if (!(wdata->state.flags & WIIPROTO_FLAGS_IR)) + return; + + switch (id) { + case 0: + xid = ABS_HAT0X; + yid = ABS_HAT0Y; + break; + case 1: + xid = ABS_HAT1X; + yid = ABS_HAT1Y; + break; + case 2: + xid = ABS_HAT2X; + yid = ABS_HAT2Y; + break; + case 3: + xid = ABS_HAT3X; + yid = ABS_HAT3Y; + sync = true; + break; + default: + return; + } + + /* + * Basic IR data is encoded into 3 bytes. The first two bytes are the + * lower 8 bit of the X/Y data, the 3rd byte contains the upper 2 bits + * of both. + * If data is packed, then the 3rd byte is put first and slightly + * reordered. This allows to interleave packed and non-packed data to + * have two IR sets in 5 bytes instead of 6. + * The resulting 10bit X/Y values are passed to the ABS_HAT? input dev. + */ + + if (packed) { + x = ir[1] | ((ir[0] & 0x03) << 8); + y = ir[2] | ((ir[0] & 0x0c) << 6); + } else { + x = ir[0] | ((ir[2] & 0x30) << 4); + y = ir[1] | ((ir[2] & 0xc0) << 2); + } + + input_report_abs(wdata->ir, xid, x); + input_report_abs(wdata->ir, yid, y); + + if (sync) + input_sync(wdata->ir); +} + +static int wiimod_ir_change(struct wiimote_data *wdata, __u16 mode) +{ + int ret; + unsigned long flags; + __u8 format = 0; + static const __u8 data_enable[] = { 0x01 }; + static const __u8 data_sens1[] = { 0x02, 0x00, 0x00, 0x71, 0x01, + 0x00, 0xaa, 0x00, 0x64 }; + static const __u8 data_sens2[] = { 0x63, 0x03 }; + static const __u8 data_fin[] = { 0x08 }; + + spin_lock_irqsave(&wdata->state.lock, flags); + + if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) { + spin_unlock_irqrestore(&wdata->state.lock, flags); + return 0; + } + + if (mode == 0) { + wdata->state.flags &= ~WIIPROTO_FLAGS_IR; + wiiproto_req_ir1(wdata, 0); + wiiproto_req_ir2(wdata, 0); + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + return 0; + } + + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_acquire(wdata); + if (ret) + return ret; + + /* send PIXEL CLOCK ENABLE cmd first */ + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_set(wdata, WIIPROTO_REQ_IR1, 0); + wiiproto_req_ir1(wdata, 0x06); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + if (ret) + goto unlock; + if (wdata->state.cmd_err) { + ret = -EIO; + goto unlock; + } + + /* enable IR LOGIC */ + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_set(wdata, WIIPROTO_REQ_IR2, 0); + wiiproto_req_ir2(wdata, 0x06); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + if (ret) + goto unlock; + if (wdata->state.cmd_err) { + ret = -EIO; + goto unlock; + } + + /* enable IR cam but do not make it send data, yet */ + ret = wiimote_cmd_write(wdata, 0xb00030, data_enable, + sizeof(data_enable)); + if (ret) + goto unlock; + + /* write first sensitivity block */ + ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1, + sizeof(data_sens1)); + if (ret) + goto unlock; + + /* write second sensitivity block */ + ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2, + sizeof(data_sens2)); + if (ret) + goto unlock; + + /* put IR cam into desired state */ + switch (mode) { + case WIIPROTO_FLAG_IR_FULL: + format = 5; + break; + case WIIPROTO_FLAG_IR_EXT: + format = 3; + break; + case WIIPROTO_FLAG_IR_BASIC: + format = 1; + break; + } + ret = wiimote_cmd_write(wdata, 0xb00033, &format, sizeof(format)); + if (ret) + goto unlock; + + /* make IR cam send data */ + ret = wiimote_cmd_write(wdata, 0xb00030, data_fin, sizeof(data_fin)); + if (ret) + goto unlock; + + /* request new DRM mode compatible to IR mode */ + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAGS_IR; + wdata->state.flags |= mode & WIIPROTO_FLAGS_IR; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + +unlock: + wiimote_cmd_release(wdata); + return ret; +} + +static int wiimod_ir_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + + return wiimod_ir_change(wdata, WIIPROTO_FLAG_IR_BASIC); +} + +static void wiimod_ir_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + + wiimod_ir_change(wdata, 0); +} + +static int wiimod_ir_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret; + + wdata->ir = input_allocate_device(); + if (!wdata->ir) + return -ENOMEM; + + input_set_drvdata(wdata->ir, wdata); + wdata->ir->open = wiimod_ir_open; + wdata->ir->close = wiimod_ir_close; + wdata->ir->dev.parent = &wdata->hdev->dev; + wdata->ir->id.bustype = wdata->hdev->bus; + wdata->ir->id.vendor = wdata->hdev->vendor; + wdata->ir->id.product = wdata->hdev->product; + wdata->ir->id.version = wdata->hdev->version; + wdata->ir->name = WIIMOTE_NAME " IR"; + + set_bit(EV_ABS, wdata->ir->evbit); + set_bit(ABS_HAT0X, wdata->ir->absbit); + set_bit(ABS_HAT0Y, wdata->ir->absbit); + set_bit(ABS_HAT1X, wdata->ir->absbit); + set_bit(ABS_HAT1Y, wdata->ir->absbit); + set_bit(ABS_HAT2X, wdata->ir->absbit); + set_bit(ABS_HAT2Y, wdata->ir->absbit); + set_bit(ABS_HAT3X, wdata->ir->absbit); + set_bit(ABS_HAT3Y, wdata->ir->absbit); + input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4); + + ret = input_register_device(wdata->ir); + if (ret) { + hid_err(wdata->hdev, "cannot register input device\n"); + goto err_free; + } + + return 0; + +err_free: + input_free_device(wdata->ir); + wdata->ir = NULL; + return ret; +} + +static void wiimod_ir_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->ir) + return; + + input_unregister_device(wdata->ir); + wdata->ir = NULL; +} + +static const struct wiimod_ops wiimod_ir = { + .flags = 0, + .arg = 0, + .probe = wiimod_ir_probe, + .remove = wiimod_ir_remove, + .in_ir = wiimod_ir_in_ir, +}; + +/* + * Nunchuk Extension + * The Nintendo Wii Nunchuk was the first official extension published by + * Nintendo. It provides two additional keys and a separate accelerometer. It + * can be hotplugged to standard Wii Remotes. + */ + +enum wiimod_nunchuk_keys { + WIIMOD_NUNCHUK_KEY_C, + WIIMOD_NUNCHUK_KEY_Z, + WIIMOD_NUNCHUK_KEY_NUM, +}; + +static const __u16 wiimod_nunchuk_map[] = { + BTN_C, /* WIIMOD_NUNCHUK_KEY_C */ + BTN_Z, /* WIIMOD_NUNCHUK_KEY_Z */ +}; + +static void wiimod_nunchuk_in_ext(struct wiimote_data *wdata, const __u8 *ext) +{ + __s16 x, y, z, bx, by; + + /* Byte | 8 7 | 6 5 | 4 3 | 2 | 1 | + * -----+----------+---------+---------+----+-----+ + * 1 | Button X <7:0> | + * 2 | Button Y <7:0> | + * -----+----------+---------+---------+----+-----+ + * 3 | Speed X <9:2> | + * 4 | Speed Y <9:2> | + * 5 | Speed Z <9:2> | + * -----+----------+---------+---------+----+-----+ + * 6 | Z <1:0> | Y <1:0> | X <1:0> | BC | BZ | + * -----+----------+---------+---------+----+-----+ + * Button X/Y is the analog stick. Speed X, Y and Z are the + * accelerometer data in the same format as the wiimote's accelerometer. + * The 6th byte contains the LSBs of the accelerometer data. + * BC and BZ are the C and Z buttons: 0 means pressed + * + * If reported interleaved with motionp, then the layout changes. The + * 5th and 6th byte changes to: + * -----+-----------------------------------+-----+ + * 5 | Speed Z <9:3> | EXT | + * -----+--------+-----+-----+----+----+----+-----+ + * 6 |Z <2:1> |Y <1>|X <1>| BC | BZ | 0 | 0 | + * -----+--------+-----+-----+----+----+----+-----+ + * All three accelerometer values lose their LSB. The other data is + * still available but slightly moved. + * + * Center data for button values is 128. Center value for accelerometer + * values it 512 / 0x200 + */ + + bx = ext[0]; + by = ext[1]; + bx -= 128; + by -= 128; + + x = ext[2] << 2; + y = ext[3] << 2; + z = ext[4] << 2; + + if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) { + x |= (ext[5] >> 3) & 0x02; + y |= (ext[5] >> 4) & 0x02; + z &= ~0x4; + z |= (ext[5] >> 5) & 0x06; + } else { + x |= (ext[5] >> 2) & 0x03; + y |= (ext[5] >> 4) & 0x03; + z |= (ext[5] >> 6) & 0x03; + } + + x -= 0x200; + y -= 0x200; + z -= 0x200; + + input_report_abs(wdata->extension.input, ABS_HAT0X, bx); + input_report_abs(wdata->extension.input, ABS_HAT0Y, by); + + input_report_abs(wdata->extension.input, ABS_RX, x); + input_report_abs(wdata->extension.input, ABS_RY, y); + input_report_abs(wdata->extension.input, ABS_RZ, z); + + if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) { + input_report_key(wdata->extension.input, + wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_Z], + !(ext[5] & 0x04)); + input_report_key(wdata->extension.input, + wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_C], + !(ext[5] & 0x08)); + } else { + input_report_key(wdata->extension.input, + wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_Z], + !(ext[5] & 0x01)); + input_report_key(wdata->extension.input, + wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_C], + !(ext[5] & 0x02)); + } + + input_sync(wdata->extension.input); +} + +static int wiimod_nunchuk_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_nunchuk_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static int wiimod_nunchuk_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret, i; + + wdata->extension.input = input_allocate_device(); + if (!wdata->extension.input) + return -ENOMEM; + + input_set_drvdata(wdata->extension.input, wdata); + wdata->extension.input->open = wiimod_nunchuk_open; + wdata->extension.input->close = wiimod_nunchuk_close; + wdata->extension.input->dev.parent = &wdata->hdev->dev; + wdata->extension.input->id.bustype = wdata->hdev->bus; + wdata->extension.input->id.vendor = wdata->hdev->vendor; + wdata->extension.input->id.product = wdata->hdev->product; + wdata->extension.input->id.version = wdata->hdev->version; + wdata->extension.input->name = WIIMOTE_NAME " Nunchuk"; + + set_bit(EV_KEY, wdata->extension.input->evbit); + for (i = 0; i < WIIMOD_NUNCHUK_KEY_NUM; ++i) + set_bit(wiimod_nunchuk_map[i], + wdata->extension.input->keybit); + + set_bit(EV_ABS, wdata->extension.input->evbit); + set_bit(ABS_HAT0X, wdata->extension.input->absbit); + set_bit(ABS_HAT0Y, wdata->extension.input->absbit); + input_set_abs_params(wdata->extension.input, + ABS_HAT0X, -120, 120, 2, 4); + input_set_abs_params(wdata->extension.input, + ABS_HAT0Y, -120, 120, 2, 4); + set_bit(ABS_RX, wdata->extension.input->absbit); + set_bit(ABS_RY, wdata->extension.input->absbit); + set_bit(ABS_RZ, wdata->extension.input->absbit); + input_set_abs_params(wdata->extension.input, + ABS_RX, -500, 500, 2, 4); + input_set_abs_params(wdata->extension.input, + ABS_RY, -500, 500, 2, 4); + input_set_abs_params(wdata->extension.input, + ABS_RZ, -500, 500, 2, 4); + + ret = input_register_device(wdata->extension.input); + if (ret) + goto err_free; + + return 0; + +err_free: + input_free_device(wdata->extension.input); + wdata->extension.input = NULL; + return ret; +} + +static void wiimod_nunchuk_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->extension.input) + return; + + input_unregister_device(wdata->extension.input); + wdata->extension.input = NULL; +} + +static const struct wiimod_ops wiimod_nunchuk = { + .flags = 0, + .arg = 0, + .probe = wiimod_nunchuk_probe, + .remove = wiimod_nunchuk_remove, + .in_ext = wiimod_nunchuk_in_ext, +}; + +/* + * Classic Controller + * Another official extension from Nintendo. It provides a classic + * gamecube-like controller that can be hotplugged on the Wii Remote. + * It has several hardware buttons and switches that are all reported via + * a normal extension device. + */ + +enum wiimod_classic_keys { + WIIMOD_CLASSIC_KEY_A, + WIIMOD_CLASSIC_KEY_B, + WIIMOD_CLASSIC_KEY_X, + WIIMOD_CLASSIC_KEY_Y, + WIIMOD_CLASSIC_KEY_ZL, + WIIMOD_CLASSIC_KEY_ZR, + WIIMOD_CLASSIC_KEY_PLUS, + WIIMOD_CLASSIC_KEY_MINUS, + WIIMOD_CLASSIC_KEY_HOME, + WIIMOD_CLASSIC_KEY_LEFT, + WIIMOD_CLASSIC_KEY_RIGHT, + WIIMOD_CLASSIC_KEY_UP, + WIIMOD_CLASSIC_KEY_DOWN, + WIIMOD_CLASSIC_KEY_LT, + WIIMOD_CLASSIC_KEY_RT, + WIIMOD_CLASSIC_KEY_NUM, +}; + +static const __u16 wiimod_classic_map[] = { + BTN_A, /* WIIMOD_CLASSIC_KEY_A */ + BTN_B, /* WIIMOD_CLASSIC_KEY_B */ + BTN_X, /* WIIMOD_CLASSIC_KEY_X */ + BTN_Y, /* WIIMOD_CLASSIC_KEY_Y */ + BTN_TL2, /* WIIMOD_CLASSIC_KEY_ZL */ + BTN_TR2, /* WIIMOD_CLASSIC_KEY_ZR */ + KEY_NEXT, /* WIIMOD_CLASSIC_KEY_PLUS */ + KEY_PREVIOUS, /* WIIMOD_CLASSIC_KEY_MINUS */ + BTN_MODE, /* WIIMOD_CLASSIC_KEY_HOME */ + KEY_LEFT, /* WIIMOD_CLASSIC_KEY_LEFT */ + KEY_RIGHT, /* WIIMOD_CLASSIC_KEY_RIGHT */ + KEY_UP, /* WIIMOD_CLASSIC_KEY_UP */ + KEY_DOWN, /* WIIMOD_CLASSIC_KEY_DOWN */ + BTN_TL, /* WIIMOD_CLASSIC_KEY_LT */ + BTN_TR, /* WIIMOD_CLASSIC_KEY_RT */ +}; + +static void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext) +{ + __s8 rx, ry, lx, ly, lt, rt; + + /* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | RX <5:4> | LX <5:0> | + * 2 | RX <3:2> | LY <5:0> | + * -----+-----+-----+-----+-----------------------------+ + * 3 |RX<1>| LT <5:4> | RY <5:1> | + * -----+-----+-----------+-----------------------------+ + * 4 | LT <3:1> | RT <5:1> | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | BDR | BDD | BLT | B- | BH | B+ | BRT | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | BZL | BB | BY | BA | BX | BZR | BDL | BDU | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * All buttons are 0 if pressed + * RX and RY are right analog stick + * LX and LY are left analog stick + * LT is left trigger, RT is right trigger + * BLT is 0 if left trigger is fully pressed + * BRT is 0 if right trigger is fully pressed + * BDR, BDD, BDL, BDU form the D-Pad with right, down, left, up buttons + * BZL is left Z button and BZR is right Z button + * B-, BH, B+ are +, HOME and - buttons + * BB, BY, BA, BX are A, B, X, Y buttons + * LSB of RX, RY, LT, and RT are not transmitted and always 0. + * + * With motionp enabled it changes slightly to this: + * Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | RX <5:4> | LX <5:1> | BDU | + * 2 | RX <3:2> | LY <5:1> | BDL | + * -----+-----+-----+-----+-----------------------+-----+ + * 3 |RX<1>| LT <5:4> | RY <5:1> | + * -----+-----+-----------+-----------------------------+ + * 4 | LT <3:1> | RT <5:1> | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 5 | BDR | BDD | BLT | B- | BH | B+ | BRT | EXT | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 6 | BZL | BB | BY | BA | BX | BZR | 0 | 0 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * Only the LSBs of LX and LY are lost. BDU and BDL are moved, the rest + * is the same as before. + */ + + if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) { + lx = ext[0] & 0x3e; + ly = ext[1] & 0x3e; + } else { + lx = ext[0] & 0x3f; + ly = ext[1] & 0x3f; + } + + rx = (ext[0] >> 3) & 0x18; + rx |= (ext[1] >> 5) & 0x06; + rx |= (ext[2] >> 7) & 0x01; + ry = ext[2] & 0x1f; + + rt = ext[3] & 0x1f; + lt = (ext[2] >> 2) & 0x18; + lt |= (ext[3] >> 5) & 0x07; + + rx <<= 1; + ry <<= 1; + rt <<= 1; + lt <<= 1; + + input_report_abs(wdata->extension.input, ABS_HAT1X, lx - 0x20); + input_report_abs(wdata->extension.input, ABS_HAT1Y, ly - 0x20); + input_report_abs(wdata->extension.input, ABS_HAT2X, rx - 0x20); + input_report_abs(wdata->extension.input, ABS_HAT2Y, ry - 0x20); + input_report_abs(wdata->extension.input, ABS_HAT3X, rt); + input_report_abs(wdata->extension.input, ABS_HAT3Y, lt); + + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_RIGHT], + !(ext[4] & 0x80)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_DOWN], + !(ext[4] & 0x40)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_LT], + !(ext[4] & 0x20)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_MINUS], + !(ext[4] & 0x10)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_HOME], + !(ext[4] & 0x08)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_PLUS], + !(ext[4] & 0x04)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_RT], + !(ext[4] & 0x02)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_ZL], + !(ext[5] & 0x80)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_B], + !(ext[5] & 0x40)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_Y], + !(ext[5] & 0x20)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_A], + !(ext[5] & 0x10)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_X], + !(ext[5] & 0x08)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_ZR], + !(ext[5] & 0x04)); + + if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) { + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT], + !(ext[1] & 0x01)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP], + !(ext[0] & 0x01)); + } else { + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT], + !(ext[5] & 0x02)); + input_report_key(wdata->extension.input, + wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP], + !(ext[5] & 0x01)); + } + + input_sync(wdata->extension.input); +} + +static int wiimod_classic_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_classic_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static int wiimod_classic_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret, i; + + wdata->extension.input = input_allocate_device(); + if (!wdata->extension.input) + return -ENOMEM; + + input_set_drvdata(wdata->extension.input, wdata); + wdata->extension.input->open = wiimod_classic_open; + wdata->extension.input->close = wiimod_classic_close; + wdata->extension.input->dev.parent = &wdata->hdev->dev; + wdata->extension.input->id.bustype = wdata->hdev->bus; + wdata->extension.input->id.vendor = wdata->hdev->vendor; + wdata->extension.input->id.product = wdata->hdev->product; + wdata->extension.input->id.version = wdata->hdev->version; + wdata->extension.input->name = WIIMOTE_NAME " Classic Controller"; + + set_bit(EV_KEY, wdata->extension.input->evbit); + for (i = 0; i < WIIMOD_CLASSIC_KEY_NUM; ++i) + set_bit(wiimod_classic_map[i], + wdata->extension.input->keybit); + + set_bit(EV_ABS, wdata->extension.input->evbit); + set_bit(ABS_HAT1X, wdata->extension.input->absbit); + set_bit(ABS_HAT1Y, wdata->extension.input->absbit); + set_bit(ABS_HAT2X, wdata->extension.input->absbit); + set_bit(ABS_HAT2Y, wdata->extension.input->absbit); + set_bit(ABS_HAT3X, wdata->extension.input->absbit); + set_bit(ABS_HAT3Y, wdata->extension.input->absbit); + input_set_abs_params(wdata->extension.input, + ABS_HAT1X, -30, 30, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_HAT1Y, -30, 30, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_HAT2X, -30, 30, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_HAT2Y, -30, 30, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_HAT3X, -30, 30, 1, 1); + input_set_abs_params(wdata->extension.input, + ABS_HAT3Y, -30, 30, 1, 1); + + ret = input_register_device(wdata->extension.input); + if (ret) + goto err_free; + + return 0; + +err_free: + input_free_device(wdata->extension.input); + wdata->extension.input = NULL; + return ret; +} + +static void wiimod_classic_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->extension.input) + return; + + input_unregister_device(wdata->extension.input); + wdata->extension.input = NULL; +} + +static const struct wiimod_ops wiimod_classic = { + .flags = 0, + .arg = 0, + .probe = wiimod_classic_probe, + .remove = wiimod_classic_remove, + .in_ext = wiimod_classic_in_ext, +}; + +/* + * Balance Board Extension + * The Nintendo Wii Balance Board provides four hardware weight sensor plus a + * single push button. No other peripherals are available. However, the + * balance-board data is sent via a standard Wii Remote extension. All other + * data for non-present hardware is zeroed out. + * Some 3rd party devices react allergic if we try to access normal Wii Remote + * hardware, so this extension module should be the only module that is loaded + * on balance boards. + * The balance board needs 8 bytes extension data instead of basic 6 bytes so + * it needs the WIIMOD_FLAG_EXT8 flag. + */ + +static void wiimod_bboard_in_keys(struct wiimote_data *wdata, const __u8 *keys) +{ + input_report_key(wdata->extension.input, BTN_A, + !!(keys[1] & 0x08)); + input_sync(wdata->extension.input); +} + +static void wiimod_bboard_in_ext(struct wiimote_data *wdata, + const __u8 *ext) +{ + __s32 val[4], tmp, div; + unsigned int i; + struct wiimote_state *s = &wdata->state; + + /* + * Balance board data layout: + * + * Byte | 8 7 6 5 4 3 2 1 | + * -----+--------------------------+ + * 1 | Top Right <15:8> | + * 2 | Top Right <7:0> | + * -----+--------------------------+ + * 3 | Bottom Right <15:8> | + * 4 | Bottom Right <7:0> | + * -----+--------------------------+ + * 5 | Top Left <15:8> | + * 6 | Top Left <7:0> | + * -----+--------------------------+ + * 7 | Bottom Left <15:8> | + * 8 | Bottom Left <7:0> | + * -----+--------------------------+ + * + * These values represent the weight-measurements of the Wii-balance + * board with 16bit precision. + * + * The balance-board is never reported interleaved with motionp. + */ + + val[0] = ext[0]; + val[0] <<= 8; + val[0] |= ext[1]; + + val[1] = ext[2]; + val[1] <<= 8; + val[1] |= ext[3]; + + val[2] = ext[4]; + val[2] <<= 8; + val[2] |= ext[5]; + + val[3] = ext[6]; + val[3] <<= 8; + val[3] |= ext[7]; + + /* apply calibration data */ + for (i = 0; i < 4; i++) { + if (val[i] <= s->calib_bboard[i][0]) { + tmp = 0; + } else if (val[i] < s->calib_bboard[i][1]) { + tmp = val[i] - s->calib_bboard[i][0]; + tmp *= 1700; + div = s->calib_bboard[i][1] - s->calib_bboard[i][0]; + tmp /= div ? div : 1; + } else { + tmp = val[i] - s->calib_bboard[i][1]; + tmp *= 1700; + div = s->calib_bboard[i][2] - s->calib_bboard[i][1]; + tmp /= div ? div : 1; + tmp += 1700; + } + val[i] = tmp; + } + + input_report_abs(wdata->extension.input, ABS_HAT0X, val[0]); + input_report_abs(wdata->extension.input, ABS_HAT0Y, val[1]); + input_report_abs(wdata->extension.input, ABS_HAT1X, val[2]); + input_report_abs(wdata->extension.input, ABS_HAT1Y, val[3]); + input_sync(wdata->extension.input); +} + +static int wiimod_bboard_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_bboard_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static ssize_t wiimod_bboard_calib_show(struct device *dev, + struct device_attribute *attr, + char *out) +{ + struct wiimote_data *wdata = dev_to_wii(dev); + int i, j, ret; + __u16 val; + __u8 buf[24], offs; + + ret = wiimote_cmd_acquire(wdata); + if (ret) + return ret; + + ret = wiimote_cmd_read(wdata, 0xa40024, buf, 12); + if (ret != 12) { + wiimote_cmd_release(wdata); + return ret < 0 ? ret : -EIO; + } + ret = wiimote_cmd_read(wdata, 0xa40024 + 12, buf + 12, 12); + if (ret != 12) { + wiimote_cmd_release(wdata); + return ret < 0 ? ret : -EIO; + } + + wiimote_cmd_release(wdata); + + spin_lock_irq(&wdata->state.lock); + offs = 0; + for (i = 0; i < 3; ++i) { + for (j = 0; j < 4; ++j) { + wdata->state.calib_bboard[j][i] = buf[offs]; + wdata->state.calib_bboard[j][i] <<= 8; + wdata->state.calib_bboard[j][i] |= buf[offs + 1]; + offs += 2; + } + } + spin_unlock_irq(&wdata->state.lock); + + ret = 0; + for (i = 0; i < 3; ++i) { + for (j = 0; j < 4; ++j) { + val = wdata->state.calib_bboard[j][i]; + if (i == 2 && j == 3) + ret += sprintf(&out[ret], "%04x\n", val); + else + ret += sprintf(&out[ret], "%04x:", val); + } + } + + return ret; +} + +static DEVICE_ATTR(bboard_calib, S_IRUGO, wiimod_bboard_calib_show, NULL); + +static int wiimod_bboard_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret, i, j; + __u8 buf[24], offs; + + wiimote_cmd_acquire_noint(wdata); + + ret = wiimote_cmd_read(wdata, 0xa40024, buf, 12); + if (ret != 12) { + wiimote_cmd_release(wdata); + return ret < 0 ? ret : -EIO; + } + ret = wiimote_cmd_read(wdata, 0xa40024 + 12, buf + 12, 12); + if (ret != 12) { + wiimote_cmd_release(wdata); + return ret < 0 ? ret : -EIO; + } + + wiimote_cmd_release(wdata); + + offs = 0; + for (i = 0; i < 3; ++i) { + for (j = 0; j < 4; ++j) { + wdata->state.calib_bboard[j][i] = buf[offs]; + wdata->state.calib_bboard[j][i] <<= 8; + wdata->state.calib_bboard[j][i] |= buf[offs + 1]; + offs += 2; + } + } + + wdata->extension.input = input_allocate_device(); + if (!wdata->extension.input) + return -ENOMEM; + + ret = device_create_file(&wdata->hdev->dev, + &dev_attr_bboard_calib); + if (ret) { + hid_err(wdata->hdev, "cannot create sysfs attribute\n"); + goto err_free; + } + + input_set_drvdata(wdata->extension.input, wdata); + wdata->extension.input->open = wiimod_bboard_open; + wdata->extension.input->close = wiimod_bboard_close; + wdata->extension.input->dev.parent = &wdata->hdev->dev; + wdata->extension.input->id.bustype = wdata->hdev->bus; + wdata->extension.input->id.vendor = wdata->hdev->vendor; + wdata->extension.input->id.product = wdata->hdev->product; + wdata->extension.input->id.version = wdata->hdev->version; + wdata->extension.input->name = WIIMOTE_NAME " Balance Board"; + + set_bit(EV_KEY, wdata->extension.input->evbit); + set_bit(BTN_A, wdata->extension.input->keybit); + + set_bit(EV_ABS, wdata->extension.input->evbit); + set_bit(ABS_HAT0X, wdata->extension.input->absbit); + set_bit(ABS_HAT0Y, wdata->extension.input->absbit); + set_bit(ABS_HAT1X, wdata->extension.input->absbit); + set_bit(ABS_HAT1Y, wdata->extension.input->absbit); + input_set_abs_params(wdata->extension.input, + ABS_HAT0X, 0, 65535, 2, 4); + input_set_abs_params(wdata->extension.input, + ABS_HAT0Y, 0, 65535, 2, 4); + input_set_abs_params(wdata->extension.input, + ABS_HAT1X, 0, 65535, 2, 4); + input_set_abs_params(wdata->extension.input, + ABS_HAT1Y, 0, 65535, 2, 4); + + ret = input_register_device(wdata->extension.input); + if (ret) + goto err_file; + + return 0; + +err_file: + device_remove_file(&wdata->hdev->dev, + &dev_attr_bboard_calib); +err_free: + input_free_device(wdata->extension.input); + wdata->extension.input = NULL; + return ret; +} + +static void wiimod_bboard_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->extension.input) + return; + + input_unregister_device(wdata->extension.input); + wdata->extension.input = NULL; + device_remove_file(&wdata->hdev->dev, + &dev_attr_bboard_calib); +} + +static const struct wiimod_ops wiimod_bboard = { + .flags = WIIMOD_FLAG_EXT8, + .arg = 0, + .probe = wiimod_bboard_probe, + .remove = wiimod_bboard_remove, + .in_keys = wiimod_bboard_in_keys, + .in_ext = wiimod_bboard_in_ext, +}; + +/* + * Pro Controller + * Released with the Wii U was the Nintendo Wii U Pro Controller. It does not + * work together with the classic Wii, but only with the new Wii U. However, it + * uses the same protocol and provides a builtin "classic controller pro" + * extension, few standard buttons, a rumble motor, 4 LEDs and a battery. + * We provide all these via a standard extension device as the device doesn't + * feature an extension port. + */ + +enum wiimod_pro_keys { + WIIMOD_PRO_KEY_A, + WIIMOD_PRO_KEY_B, + WIIMOD_PRO_KEY_X, + WIIMOD_PRO_KEY_Y, + WIIMOD_PRO_KEY_PLUS, + WIIMOD_PRO_KEY_MINUS, + WIIMOD_PRO_KEY_HOME, + WIIMOD_PRO_KEY_LEFT, + WIIMOD_PRO_KEY_RIGHT, + WIIMOD_PRO_KEY_UP, + WIIMOD_PRO_KEY_DOWN, + WIIMOD_PRO_KEY_TL, + WIIMOD_PRO_KEY_TR, + WIIMOD_PRO_KEY_ZL, + WIIMOD_PRO_KEY_ZR, + WIIMOD_PRO_KEY_THUMBL, + WIIMOD_PRO_KEY_THUMBR, + WIIMOD_PRO_KEY_NUM, +}; + +static const __u16 wiimod_pro_map[] = { + BTN_EAST, /* WIIMOD_PRO_KEY_A */ + BTN_SOUTH, /* WIIMOD_PRO_KEY_B */ + BTN_NORTH, /* WIIMOD_PRO_KEY_X */ + BTN_WEST, /* WIIMOD_PRO_KEY_Y */ + BTN_START, /* WIIMOD_PRO_KEY_PLUS */ + BTN_SELECT, /* WIIMOD_PRO_KEY_MINUS */ + BTN_MODE, /* WIIMOD_PRO_KEY_HOME */ + BTN_DPAD_LEFT, /* WIIMOD_PRO_KEY_LEFT */ + BTN_DPAD_RIGHT, /* WIIMOD_PRO_KEY_RIGHT */ + BTN_DPAD_UP, /* WIIMOD_PRO_KEY_UP */ + BTN_DPAD_DOWN, /* WIIMOD_PRO_KEY_DOWN */ + BTN_TL, /* WIIMOD_PRO_KEY_TL */ + BTN_TR, /* WIIMOD_PRO_KEY_TR */ + BTN_TL2, /* WIIMOD_PRO_KEY_ZL */ + BTN_TR2, /* WIIMOD_PRO_KEY_ZR */ + BTN_THUMBL, /* WIIMOD_PRO_KEY_THUMBL */ + BTN_THUMBR, /* WIIMOD_PRO_KEY_THUMBR */ +}; + +static void wiimod_pro_in_ext(struct wiimote_data *wdata, const __u8 *ext) +{ + __s16 rx, ry, lx, ly; + + /* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 1 | LX <7:0> | + * -----+-----------------------+-----------------------+ + * 2 | 0 0 0 0 | LX <11:8> | + * -----+-----------------------+-----------------------+ + * 3 | RX <7:0> | + * -----+-----------------------+-----------------------+ + * 4 | 0 0 0 0 | RX <11:8> | + * -----+-----------------------+-----------------------+ + * 5 | LY <7:0> | + * -----+-----------------------+-----------------------+ + * 6 | 0 0 0 0 | LY <11:8> | + * -----+-----------------------+-----------------------+ + * 7 | RY <7:0> | + * -----+-----------------------+-----------------------+ + * 8 | 0 0 0 0 | RY <11:8> | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 9 | BDR | BDD | BLT | B- | BH | B+ | BRT | 1 | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 10 | BZL | BB | BY | BA | BX | BZR | BDL | BDU | + * -----+-----+-----+-----+-----+-----+-----+-----+-----+ + * 11 | 1 | BATTERY | USB |CHARG|LTHUM|RTHUM| + * -----+-----+-----------------+-----------+-----+-----+ + * All buttons are low-active (0 if pressed) + * RX and RY are right analog stick + * LX and LY are left analog stick + * BLT is left trigger, BRT is right trigger. + * BDR, BDD, BDL, BDU form the D-Pad with right, down, left, up buttons + * BZL is left Z button and BZR is right Z button + * B-, BH, B+ are +, HOME and - buttons + * BB, BY, BA, BX are A, B, X, Y buttons + * + * Bits marked as 0/1 are unknown and never changed during tests. + * + * Not entirely verified: + * CHARG: 1 if uncharging, 0 if charging + * USB: 1 if not connected, 0 if connected + * BATTERY: battery capacity from 000 (empty) to 100 (full) + */ + + lx = (ext[0] & 0xff) | ((ext[1] & 0x0f) << 8); + rx = (ext[2] & 0xff) | ((ext[3] & 0x0f) << 8); + ly = (ext[4] & 0xff) | ((ext[5] & 0x0f) << 8); + ry = (ext[6] & 0xff) | ((ext[7] & 0x0f) << 8); + + /* zero-point offsets */ + lx -= 0x800; + ly = 0x800 - ly; + rx -= 0x800; + ry = 0x800 - ry; + + /* Trivial automatic calibration. We don't know any calibration data + * in the EEPROM so we must use the first report to calibrate the + * null-position of the analog sticks. Users can retrigger calibration + * via sysfs, or set it explicitly. If data is off more than abs(500), + * we skip calibration as the sticks are likely to be moved already. */ + if (!(wdata->state.flags & WIIPROTO_FLAG_PRO_CALIB_DONE)) { + wdata->state.flags |= WIIPROTO_FLAG_PRO_CALIB_DONE; + if (abs(lx) < 500) + wdata->state.calib_pro_sticks[0] = -lx; + if (abs(ly) < 500) + wdata->state.calib_pro_sticks[1] = -ly; + if (abs(rx) < 500) + wdata->state.calib_pro_sticks[2] = -rx; + if (abs(ry) < 500) + wdata->state.calib_pro_sticks[3] = -ry; + } + + /* apply calibration data */ + lx += wdata->state.calib_pro_sticks[0]; + ly += wdata->state.calib_pro_sticks[1]; + rx += wdata->state.calib_pro_sticks[2]; + ry += wdata->state.calib_pro_sticks[3]; + + input_report_abs(wdata->extension.input, ABS_X, lx); + input_report_abs(wdata->extension.input, ABS_Y, ly); + input_report_abs(wdata->extension.input, ABS_RX, rx); + input_report_abs(wdata->extension.input, ABS_RY, ry); + + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_RIGHT], + !(ext[8] & 0x80)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_DOWN], + !(ext[8] & 0x40)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_TL], + !(ext[8] & 0x20)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_MINUS], + !(ext[8] & 0x10)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_HOME], + !(ext[8] & 0x08)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_PLUS], + !(ext[8] & 0x04)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_TR], + !(ext[8] & 0x02)); + + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_ZL], + !(ext[9] & 0x80)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_B], + !(ext[9] & 0x40)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_Y], + !(ext[9] & 0x20)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_A], + !(ext[9] & 0x10)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_X], + !(ext[9] & 0x08)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_ZR], + !(ext[9] & 0x04)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_LEFT], + !(ext[9] & 0x02)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_UP], + !(ext[9] & 0x01)); + + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_THUMBL], + !(ext[10] & 0x02)); + input_report_key(wdata->extension.input, + wiimod_pro_map[WIIMOD_PRO_KEY_THUMBR], + !(ext[10] & 0x01)); + + input_sync(wdata->extension.input); +} + +static int wiimod_pro_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_pro_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static int wiimod_pro_play(struct input_dev *dev, void *data, + struct ff_effect *eff) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + __u8 value; + + /* + * The wiimote supports only a single rumble motor so if any magnitude + * is set to non-zero then we start the rumble motor. If both are set to + * zero, we stop the rumble motor. + */ + + if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude) + value = 1; + else + value = 0; + + /* Locking state.lock here might deadlock with input_event() calls. + * schedule_work acts as barrier. Merging multiple changes is fine. */ + wdata->state.cache_rumble = value; + schedule_work(&wdata->rumble_worker); + + return 0; +} + +static ssize_t wiimod_pro_calib_show(struct device *dev, + struct device_attribute *attr, + char *out) +{ + struct wiimote_data *wdata = dev_to_wii(dev); + int r; + + r = 0; + r += sprintf(&out[r], "%+06hd:", wdata->state.calib_pro_sticks[0]); + r += sprintf(&out[r], "%+06hd ", wdata->state.calib_pro_sticks[1]); + r += sprintf(&out[r], "%+06hd:", wdata->state.calib_pro_sticks[2]); + r += sprintf(&out[r], "%+06hd\n", wdata->state.calib_pro_sticks[3]); + + return r; +} + +static ssize_t wiimod_pro_calib_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wiimote_data *wdata = dev_to_wii(dev); + int r; + s16 x1, y1, x2, y2; + + if (!strncmp(buf, "scan\n", 5)) { + spin_lock_irq(&wdata->state.lock); + wdata->state.flags &= ~WIIPROTO_FLAG_PRO_CALIB_DONE; + spin_unlock_irq(&wdata->state.lock); + } else { + r = sscanf(buf, "%hd:%hd %hd:%hd", &x1, &y1, &x2, &y2); + if (r != 4) + return -EINVAL; + + spin_lock_irq(&wdata->state.lock); + wdata->state.flags |= WIIPROTO_FLAG_PRO_CALIB_DONE; + spin_unlock_irq(&wdata->state.lock); + + wdata->state.calib_pro_sticks[0] = x1; + wdata->state.calib_pro_sticks[1] = y1; + wdata->state.calib_pro_sticks[2] = x2; + wdata->state.calib_pro_sticks[3] = y2; + } + + return strnlen(buf, PAGE_SIZE); +} + +static DEVICE_ATTR(pro_calib, S_IRUGO|S_IWUSR|S_IWGRP, wiimod_pro_calib_show, + wiimod_pro_calib_store); + +static int wiimod_pro_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret, i; + unsigned long flags; + + INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker); + wdata->state.calib_pro_sticks[0] = 0; + wdata->state.calib_pro_sticks[1] = 0; + wdata->state.calib_pro_sticks[2] = 0; + wdata->state.calib_pro_sticks[3] = 0; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAG_PRO_CALIB_DONE; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + wdata->extension.input = input_allocate_device(); + if (!wdata->extension.input) + return -ENOMEM; + + set_bit(FF_RUMBLE, wdata->extension.input->ffbit); + input_set_drvdata(wdata->extension.input, wdata); + + if (input_ff_create_memless(wdata->extension.input, NULL, + wiimod_pro_play)) { + ret = -ENOMEM; + goto err_free; + } + + ret = device_create_file(&wdata->hdev->dev, + &dev_attr_pro_calib); + if (ret) { + hid_err(wdata->hdev, "cannot create sysfs attribute\n"); + goto err_free; + } + + wdata->extension.input->open = wiimod_pro_open; + wdata->extension.input->close = wiimod_pro_close; + wdata->extension.input->dev.parent = &wdata->hdev->dev; + wdata->extension.input->id.bustype = wdata->hdev->bus; + wdata->extension.input->id.vendor = wdata->hdev->vendor; + wdata->extension.input->id.product = wdata->hdev->product; + wdata->extension.input->id.version = wdata->hdev->version; + wdata->extension.input->name = WIIMOTE_NAME " Pro Controller"; + + set_bit(EV_KEY, wdata->extension.input->evbit); + for (i = 0; i < WIIMOD_PRO_KEY_NUM; ++i) + set_bit(wiimod_pro_map[i], + wdata->extension.input->keybit); + + set_bit(EV_ABS, wdata->extension.input->evbit); + set_bit(ABS_X, wdata->extension.input->absbit); + set_bit(ABS_Y, wdata->extension.input->absbit); + set_bit(ABS_RX, wdata->extension.input->absbit); + set_bit(ABS_RY, wdata->extension.input->absbit); + input_set_abs_params(wdata->extension.input, + ABS_X, -0x400, 0x400, 4, 100); + input_set_abs_params(wdata->extension.input, + ABS_Y, -0x400, 0x400, 4, 100); + input_set_abs_params(wdata->extension.input, + ABS_RX, -0x400, 0x400, 4, 100); + input_set_abs_params(wdata->extension.input, + ABS_RY, -0x400, 0x400, 4, 100); + + ret = input_register_device(wdata->extension.input); + if (ret) + goto err_file; + + return 0; + +err_file: + device_remove_file(&wdata->hdev->dev, + &dev_attr_pro_calib); +err_free: + input_free_device(wdata->extension.input); + wdata->extension.input = NULL; + return ret; +} + +static void wiimod_pro_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + unsigned long flags; + + if (!wdata->extension.input) + return; + + input_unregister_device(wdata->extension.input); + wdata->extension.input = NULL; + cancel_work_sync(&wdata->rumble_worker); + device_remove_file(&wdata->hdev->dev, + &dev_attr_pro_calib); + + spin_lock_irqsave(&wdata->state.lock, flags); + wiiproto_req_rumble(wdata, 0); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static const struct wiimod_ops wiimod_pro = { + .flags = WIIMOD_FLAG_EXT16, + .arg = 0, + .probe = wiimod_pro_probe, + .remove = wiimod_pro_remove, + .in_ext = wiimod_pro_in_ext, +}; + +/* + * Builtin Motion Plus + * This module simply sets the WIIPROTO_FLAG_BUILTIN_MP protocol flag which + * disables polling for Motion-Plus. This should be set only for devices which + * don't allow MP hotplugging. + */ + +static int wiimod_builtin_mp_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_BUILTIN_MP; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_builtin_mp_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_BUILTIN_MP; + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static const struct wiimod_ops wiimod_builtin_mp = { + .flags = 0, + .arg = 0, + .probe = wiimod_builtin_mp_probe, + .remove = wiimod_builtin_mp_remove, +}; + +/* + * No Motion Plus + * This module simply sets the WIIPROTO_FLAG_NO_MP protocol flag which + * disables motion-plus. This is needed for devices that advertise this but we + * don't know how to use it (or whether it is actually present). + */ + +static int wiimod_no_mp_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_NO_MP; + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_no_mp_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_NO_MP; + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static const struct wiimod_ops wiimod_no_mp = { + .flags = 0, + .arg = 0, + .probe = wiimod_no_mp_probe, + .remove = wiimod_no_mp_remove, +}; + +/* + * Motion Plus + * The Motion Plus extension provides rotation sensors (gyro) as a small + * extension device for Wii Remotes. Many devices have them built-in so + * you cannot see them from the outside. + * Motion Plus extensions are special because they are on a separate extension + * port and allow other extensions to be used simultaneously. This is all + * handled by the Wiimote Core so we don't have to deal with it. + */ + +static void wiimod_mp_in_mp(struct wiimote_data *wdata, const __u8 *ext) +{ + __s32 x, y, z; + + /* | 8 7 6 5 4 3 | 2 | 1 | + * -----+------------------------------+-----+-----+ + * 1 | Yaw Speed <7:0> | + * 2 | Roll Speed <7:0> | + * 3 | Pitch Speed <7:0> | + * -----+------------------------------+-----+-----+ + * 4 | Yaw Speed <13:8> | Yaw |Pitch| + * -----+------------------------------+-----+-----+ + * 5 | Roll Speed <13:8> |Roll | Ext | + * -----+------------------------------+-----+-----+ + * 6 | Pitch Speed <13:8> | 1 | 0 | + * -----+------------------------------+-----+-----+ + * The single bits Yaw, Roll, Pitch in the lower right corner specify + * whether the wiimote is rotating fast (0) or slow (1). Speed for slow + * roation is 440 deg/s and for fast rotation 2000 deg/s. To get a + * linear scale we multiply by 2000/440 = ~4.5454 which is 18 for fast + * and 9 for slow. + * If the wiimote is not rotating the sensor reports 2^13 = 8192. + * Ext specifies whether an extension is connected to the motionp. + * which is parsed by wiimote-core. + */ + + x = ext[0]; + y = ext[1]; + z = ext[2]; + + x |= (((__u16)ext[3]) << 6) & 0xff00; + y |= (((__u16)ext[4]) << 6) & 0xff00; + z |= (((__u16)ext[5]) << 6) & 0xff00; + + x -= 8192; + y -= 8192; + z -= 8192; + + if (!(ext[3] & 0x02)) + x *= 18; + else + x *= 9; + if (!(ext[4] & 0x02)) + y *= 18; + else + y *= 9; + if (!(ext[3] & 0x01)) + z *= 18; + else + z *= 9; + + input_report_abs(wdata->mp, ABS_RX, x); + input_report_abs(wdata->mp, ABS_RY, y); + input_report_abs(wdata->mp, ABS_RZ, z); + input_sync(wdata->mp); +} + +static int wiimod_mp_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags |= WIIPROTO_FLAG_MP_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + __wiimote_schedule(wdata); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimod_mp_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAG_MP_USED; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + __wiimote_schedule(wdata); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static int wiimod_mp_probe(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + int ret; + + wdata->mp = input_allocate_device(); + if (!wdata->mp) + return -ENOMEM; + + input_set_drvdata(wdata->mp, wdata); + wdata->mp->open = wiimod_mp_open; + wdata->mp->close = wiimod_mp_close; + wdata->mp->dev.parent = &wdata->hdev->dev; + wdata->mp->id.bustype = wdata->hdev->bus; + wdata->mp->id.vendor = wdata->hdev->vendor; + wdata->mp->id.product = wdata->hdev->product; + wdata->mp->id.version = wdata->hdev->version; + wdata->mp->name = WIIMOTE_NAME " Motion Plus"; + + set_bit(EV_ABS, wdata->mp->evbit); + set_bit(ABS_RX, wdata->mp->absbit); + set_bit(ABS_RY, wdata->mp->absbit); + set_bit(ABS_RZ, wdata->mp->absbit); + input_set_abs_params(wdata->mp, + ABS_RX, -16000, 16000, 4, 8); + input_set_abs_params(wdata->mp, + ABS_RY, -16000, 16000, 4, 8); + input_set_abs_params(wdata->mp, + ABS_RZ, -16000, 16000, 4, 8); + + ret = input_register_device(wdata->mp); + if (ret) + goto err_free; + + return 0; + +err_free: + input_free_device(wdata->mp); + wdata->mp = NULL; + return ret; +} + +static void wiimod_mp_remove(const struct wiimod_ops *ops, + struct wiimote_data *wdata) +{ + if (!wdata->mp) + return; + + input_unregister_device(wdata->mp); + wdata->mp = NULL; +} + +const struct wiimod_ops wiimod_mp = { + .flags = 0, + .arg = 0, + .probe = wiimod_mp_probe, + .remove = wiimod_mp_remove, + .in_mp = wiimod_mp_in_mp, +}; + +/* module table */ + +static const struct wiimod_ops wiimod_dummy; + +const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = { + [WIIMOD_KEYS] = &wiimod_keys, + [WIIMOD_RUMBLE] = &wiimod_rumble, + [WIIMOD_BATTERY] = &wiimod_battery, + [WIIMOD_LED1] = &wiimod_leds[0], + [WIIMOD_LED2] = &wiimod_leds[1], + [WIIMOD_LED3] = &wiimod_leds[2], + [WIIMOD_LED4] = &wiimod_leds[3], + [WIIMOD_ACCEL] = &wiimod_accel, + [WIIMOD_IR] = &wiimod_ir, + [WIIMOD_BUILTIN_MP] = &wiimod_builtin_mp, + [WIIMOD_NO_MP] = &wiimod_no_mp, +}; + +const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = { + [WIIMOTE_EXT_NONE] = &wiimod_dummy, + [WIIMOTE_EXT_UNKNOWN] = &wiimod_dummy, + [WIIMOTE_EXT_NUNCHUK] = &wiimod_nunchuk, + [WIIMOTE_EXT_CLASSIC_CONTROLLER] = &wiimod_classic, + [WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard, + [WIIMOTE_EXT_PRO_CONTROLLER] = &wiimod_pro, +}; diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index c81dbeb086c..10934aa129f 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -2,8 +2,8 @@ #define __HID_WIIMOTE_H /* - * HID driver for Nintendo Wiimote devices - * Copyright (c) 2011 David Herrmann + * HID driver for Nintendo Wii / Wii U peripherals + * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com> */ /* @@ -22,6 +22,7 @@ #include <linux/mutex.h> #include <linux/power_supply.h> #include <linux/spinlock.h> +#include <linux/timer.h> #define WIIMOTE_NAME "Nintendo Wii Remote" #define WIIMOTE_BUFSIZE 32 @@ -35,6 +36,18 @@ #define WIIPROTO_FLAG_IR_BASIC 0x40 #define WIIPROTO_FLAG_IR_EXT 0x80 #define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */ +#define WIIPROTO_FLAG_EXT_PLUGGED 0x0100 +#define WIIPROTO_FLAG_EXT_USED 0x0200 +#define WIIPROTO_FLAG_EXT_ACTIVE 0x0400 +#define WIIPROTO_FLAG_MP_PLUGGED 0x0800 +#define WIIPROTO_FLAG_MP_USED 0x1000 +#define WIIPROTO_FLAG_MP_ACTIVE 0x2000 +#define WIIPROTO_FLAG_EXITING 0x4000 +#define WIIPROTO_FLAG_DRM_LOCKED 0x8000 +#define WIIPROTO_FLAG_BUILTIN_MP 0x010000 +#define WIIPROTO_FLAG_NO_MP 0x020000 +#define WIIPROTO_FLAG_PRO_CALIB_DONE 0x040000 + #define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \ WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4) #define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \ @@ -43,16 +56,71 @@ /* return flag for led \num */ #define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1)) +enum wiiproto_keys { + WIIPROTO_KEY_LEFT, + WIIPROTO_KEY_RIGHT, + WIIPROTO_KEY_UP, + WIIPROTO_KEY_DOWN, + WIIPROTO_KEY_PLUS, + WIIPROTO_KEY_MINUS, + WIIPROTO_KEY_ONE, + WIIPROTO_KEY_TWO, + WIIPROTO_KEY_A, + WIIPROTO_KEY_B, + WIIPROTO_KEY_HOME, + WIIPROTO_KEY_COUNT +}; + +enum wiimote_devtype { + WIIMOTE_DEV_PENDING, + WIIMOTE_DEV_UNKNOWN, + WIIMOTE_DEV_GENERIC, + WIIMOTE_DEV_GEN10, + WIIMOTE_DEV_GEN20, + WIIMOTE_DEV_BALANCE_BOARD, + WIIMOTE_DEV_PRO_CONTROLLER, + WIIMOTE_DEV_NUM, +}; + +enum wiimote_exttype { + WIIMOTE_EXT_NONE, + WIIMOTE_EXT_UNKNOWN, + WIIMOTE_EXT_NUNCHUK, + WIIMOTE_EXT_CLASSIC_CONTROLLER, + WIIMOTE_EXT_BALANCE_BOARD, + WIIMOTE_EXT_PRO_CONTROLLER, + WIIMOTE_EXT_NUM, +}; + +enum wiimote_mptype { + WIIMOTE_MP_NONE, + WIIMOTE_MP_UNKNOWN, + WIIMOTE_MP_SINGLE, + WIIMOTE_MP_PASSTHROUGH_NUNCHUK, + WIIMOTE_MP_PASSTHROUGH_CLASSIC, +}; + struct wiimote_buf { __u8 data[HID_MAX_BUFFER_SIZE]; size_t size; }; +struct wiimote_queue { + spinlock_t lock; + struct work_struct worker; + __u8 head; + __u8 tail; + struct wiimote_buf outq[WIIMOTE_BUFSIZE]; +}; + struct wiimote_state { spinlock_t lock; - __u8 flags; + __u32 flags; __u8 accel_split[2]; __u8 drm; + __u8 devtype; + __u8 exttype; + __u8 mp; /* synchronous cmd requests */ struct mutex sync; @@ -65,27 +133,78 @@ struct wiimote_state { __u8 cmd_err; __u8 *cmd_read_buf; __u8 cmd_read_size; + + /* calibration/cache data */ + __u16 calib_bboard[4][3]; + __s16 calib_pro_sticks[4]; + __u8 cache_rumble; }; struct wiimote_data { struct hid_device *hdev; struct input_dev *input; + struct work_struct rumble_worker; struct led_classdev *leds[4]; struct input_dev *accel; struct input_dev *ir; struct power_supply battery; - struct wiimote_ext *ext; + struct input_dev *mp; + struct timer_list timer; struct wiimote_debug *debug; - spinlock_t qlock; - __u8 head; - __u8 tail; - struct wiimote_buf outq[WIIMOTE_BUFSIZE]; - struct work_struct worker; + union { + struct input_dev *input; + } extension; + struct wiimote_queue queue; struct wiimote_state state; + struct work_struct init_worker; +}; + +/* wiimote modules */ + +enum wiimod_module { + WIIMOD_KEYS, + WIIMOD_RUMBLE, + WIIMOD_BATTERY, + WIIMOD_LED1, + WIIMOD_LED2, + WIIMOD_LED3, + WIIMOD_LED4, + WIIMOD_ACCEL, + WIIMOD_IR, + WIIMOD_BUILTIN_MP, + WIIMOD_NO_MP, + WIIMOD_NUM, + WIIMOD_NULL = WIIMOD_NUM, +}; + +#define WIIMOD_FLAG_INPUT 0x0001 +#define WIIMOD_FLAG_EXT8 0x0002 +#define WIIMOD_FLAG_EXT16 0x0004 + +struct wiimod_ops { + __u16 flags; + unsigned long arg; + int (*probe) (const struct wiimod_ops *ops, + struct wiimote_data *wdata); + void (*remove) (const struct wiimod_ops *ops, + struct wiimote_data *wdata); + + void (*in_keys) (struct wiimote_data *wdata, const __u8 *keys); + void (*in_accel) (struct wiimote_data *wdata, const __u8 *accel); + void (*in_ir) (struct wiimote_data *wdata, const __u8 *ir, bool packed, + unsigned int id); + void (*in_mp) (struct wiimote_data *wdata, const __u8 *mp); + void (*in_ext) (struct wiimote_data *wdata, const __u8 *ext); }; +extern const struct wiimod_ops *wiimod_table[WIIMOD_NUM]; +extern const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM]; +extern const struct wiimod_ops wiimod_mp; + +/* wiimote requests */ + enum wiiproto_reqs { WIIPROTO_REQ_NULL = 0x0, WIIPROTO_REQ_RUMBLE = 0x10, @@ -99,24 +218,55 @@ enum wiiproto_reqs { WIIPROTO_REQ_STATUS = 0x20, WIIPROTO_REQ_DATA = 0x21, WIIPROTO_REQ_RETURN = 0x22, + + /* DRM_K: BB*2 */ WIIPROTO_REQ_DRM_K = 0x30, + + /* DRM_KA: BB*2 AA*3 */ WIIPROTO_REQ_DRM_KA = 0x31, + + /* DRM_KE: BB*2 EE*8 */ WIIPROTO_REQ_DRM_KE = 0x32, + + /* DRM_KAI: BB*2 AA*3 II*12 */ WIIPROTO_REQ_DRM_KAI = 0x33, + + /* DRM_KEE: BB*2 EE*19 */ WIIPROTO_REQ_DRM_KEE = 0x34, + + /* DRM_KAE: BB*2 AA*3 EE*16 */ WIIPROTO_REQ_DRM_KAE = 0x35, + + /* DRM_KIE: BB*2 II*10 EE*9 */ WIIPROTO_REQ_DRM_KIE = 0x36, + + /* DRM_KAIE: BB*2 AA*3 II*10 EE*6 */ WIIPROTO_REQ_DRM_KAIE = 0x37, + + /* DRM_E: EE*21 */ WIIPROTO_REQ_DRM_E = 0x3d, + + /* DRM_SKAI1: BB*2 AA*1 II*18 */ WIIPROTO_REQ_DRM_SKAI1 = 0x3e, + + /* DRM_SKAI2: BB*2 AA*1 II*18 */ WIIPROTO_REQ_DRM_SKAI2 = 0x3f, + WIIPROTO_REQ_MAX }; #define dev_to_wii(pdev) hid_get_drvdata(container_of(pdev, struct hid_device, \ dev)) +void __wiimote_schedule(struct wiimote_data *wdata); + extern void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm); +extern void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble); +extern void wiiproto_req_leds(struct wiimote_data *wdata, int leds); +extern void wiiproto_req_status(struct wiimote_data *wdata); +extern void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel); +extern void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags); +extern void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags); extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, const __u8 *wmem, __u8 size); extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, @@ -129,24 +279,6 @@ extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, extern void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom, __u32 offset, __u16 size); -#ifdef CONFIG_HID_WIIMOTE_EXT - -extern int wiiext_init(struct wiimote_data *wdata); -extern void wiiext_deinit(struct wiimote_data *wdata); -extern void wiiext_event(struct wiimote_data *wdata, bool plugged); -extern bool wiiext_active(struct wiimote_data *wdata); -extern void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload); - -#else - -static inline int wiiext_init(void *u) { return 0; } -static inline void wiiext_deinit(void *u) { } -static inline void wiiext_event(void *u, bool p) { } -static inline bool wiiext_active(void *u) { return false; } -static inline void wiiext_handle(void *u, const __u8 *p) { } - -#endif - #ifdef CONFIG_DEBUG_FS extern int wiidebug_init(struct wiimote_data *wdata); @@ -173,16 +305,31 @@ static inline void wiimote_cmd_complete(struct wiimote_data *wdata) complete(&wdata->state.ready); } +/* requires the state.lock spinlock to be held */ +static inline void wiimote_cmd_abort(struct wiimote_data *wdata) +{ + /* Abort synchronous request by waking up the sleeping caller. But + * reset the state.cmd field to an invalid value so no further event + * handlers will work with it. */ + wdata->state.cmd = WIIPROTO_REQ_MAX; + complete(&wdata->state.ready); +} + static inline int wiimote_cmd_acquire(struct wiimote_data *wdata) { return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0; } +static inline void wiimote_cmd_acquire_noint(struct wiimote_data *wdata) +{ + mutex_lock(&wdata->state.sync); +} + /* requires the state.lock spinlock to be held */ static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd, __u32 opt) { - INIT_COMPLETION(wdata->state.ready); + reinit_completion(&wdata->state.ready); wdata->state.cmd = cmd; wdata->state.opt = opt; } @@ -196,11 +343,31 @@ static inline int wiimote_cmd_wait(struct wiimote_data *wdata) { int ret; + /* The completion acts as implicit memory barrier so we can safely + * assume that state.cmd is set on success/failure and isn't accessed + * by any other thread, anymore. */ + ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ); if (ret < 0) return -ERESTARTSYS; else if (ret == 0) return -EIO; + else if (wdata->state.cmd != WIIPROTO_REQ_NULL) + return -EIO; + else + return 0; +} + +static inline int wiimote_cmd_wait_noint(struct wiimote_data *wdata) +{ + unsigned long ret; + + /* no locking needed; see wiimote_cmd_wait() */ + ret = wait_for_completion_timeout(&wdata->state.ready, HZ); + if (!ret) + return -EIO; + else if (wdata->state.cmd != WIIPROTO_REQ_NULL) + return -EIO; else return 0; } diff --git a/drivers/hid/hid-xinmo.c b/drivers/hid/hid-xinmo.c new file mode 100644 index 00000000000..7df5227a7e6 --- /dev/null +++ b/drivers/hid/hid-xinmo.c @@ -0,0 +1,61 @@ +/* + * HID driver for Xin-Mo devices, currently only the Dual Arcade controller. + * Fixes the negative axis event values (the devices sends -2) to match the + * logical axis minimum of the HID report descriptor (the report announces + * -1). It is needed because hid-input discards out of bounds values. + * (This module is based on "hid-saitek" and "hid-lg".) + * + * Copyright (c) 2013 Olivier Scherler + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/kernel.h> + +#include "hid-ids.h" + +/* + * Fix negative events that are out of bounds. + */ +static int xinmo_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + switch (usage->code) { + case ABS_X: + case ABS_Y: + case ABS_Z: + case ABS_RX: + if (value < -1) { + input_event(field->hidinput->input, usage->type, + usage->code, -1); + return 1; + } + break; + } + + return 0; +} + +static const struct hid_device_id xinmo_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, xinmo_devices); + +static struct hid_driver xinmo_driver = { + .name = "xinmo", + .id_table = xinmo_devices, + .event = xinmo_event +}; + +module_hid_driver(xinmo_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c index f6ba81df71b..a29756c6ca0 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -24,13 +24,11 @@ #include <linux/hid.h> #include <linux/input.h> #include <linux/slab.h> -#include <linux/usb.h> #include <linux/module.h> #include "hid-ids.h" #ifdef CONFIG_ZEROPLUS_FF -#include "usbhid/usbhid.h" struct zpff_device { struct hid_report *report; @@ -59,7 +57,7 @@ static int zpff_play(struct input_dev *dev, void *data, zpff->report->field[2]->value[0] = left; zpff->report->field[3]->value[0] = right; dbg_hid("running with 0x%02x 0x%02x\n", left, right); - usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); + hid_hw_request(hid, zpff->report, HID_REQ_SET_REPORT); return 0; } @@ -70,21 +68,13 @@ static int zpff_init(struct hid_device *hid) struct hid_report *report; struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct list_head *report_list = - &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; - int error; + int i, error; - if (list_empty(report_list)) { - hid_err(hid, "no output report found\n"); - return -ENODEV; - } - - report = list_entry(report_list->next, struct hid_report, list); - - if (report->maxfield < 4) { - hid_err(hid, "not enough fields in report\n"); - return -ENODEV; + for (i = 0; i < 4; i++) { + report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1); + if (!report) + return -ENODEV; } zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL); @@ -104,7 +94,7 @@ static int zpff_init(struct hid_device *hid) zpff->report->field[1]->value[0] = 0x02; zpff->report->field[2]->value[0] = 0x00; zpff->report->field[3]->value[0] = 0x00; - usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); + hid_hw_request(hid, zpff->report, HID_REQ_SET_REPORT); hid_info(hid, "force feedback for Zeroplus based devices by Anssi Hannula <anssi.hannula@gmail.com>\n"); @@ -152,17 +142,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 1ad85f2257b..1a660bd97ab 100644 --- a/drivers/hid/hid-zydacron.c +++ b/drivers/hid/hid-zydacron.c @@ -169,7 +169,7 @@ static int zc_probe(struct hid_device *hdev, const struct hid_device_id *id) int ret; struct zc_device *zc; - zc = kzalloc(sizeof(*zc), GFP_KERNEL); + zc = devm_kzalloc(&hdev->dev, sizeof(*zc), GFP_KERNEL); if (zc == NULL) { hid_err(hdev, "can't alloc descriptor\n"); return -ENOMEM; @@ -180,28 +180,16 @@ static int zc_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err_free; + return ret; } ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); - goto err_free; + return ret; } return 0; -err_free: - kfree(zc); - - return ret; -} - -static void zc_remove(struct hid_device *hdev) -{ - struct zc_device *zc = hid_get_drvdata(hdev); - - hid_hw_stop(hdev); - kfree(zc); } static const struct hid_device_id zc_devices[] = { @@ -217,19 +205,7 @@ static struct hid_driver zc_driver = { .input_mapping = zc_input_mapping, .raw_event = zc_raw_event, .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 413a73187d3..9c2d7c23f29 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -6,7 +6,7 @@ * to work on raw hid events as they want to, and avoids a need to * use a transport-specific userspace libhid/libusb libraries. * - * Copyright (c) 2007 Jiri Kosina + * Copyright (c) 2007-2014 Jiri Kosina */ /* @@ -104,26 +104,25 @@ out: return ret; } -/* The first byte is expected to be a report number. - * This function is to be called with the minors_lock mutex held */ +/* + * The first byte of the report buffer is expected to be a report number. + * + * This function is to be called with the minors_lock mutex held. + */ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type) { - unsigned int minor = iminor(file->f_path.dentry->d_inode); + unsigned int minor = iminor(file_inode(file)); struct hid_device *dev; __u8 *buf; int ret = 0; - if (!hidraw_table[minor]) { + if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { ret = -ENODEV; goto out; } dev = hidraw_table[minor]->hid; - if (!dev->hid_output_raw_report) { - ret = -ENODEV; - goto out; - } if (count > HID_MAX_BUFFER_SIZE) { hid_warn(dev, "pid %d passed too large report\n", @@ -150,14 +149,27 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, goto out_free; } - ret = dev->hid_output_raw_report(dev, buf, count, report_type); + if ((report_type == HID_OUTPUT_REPORT) && + !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) { + ret = hid_hw_output_report(dev, buf, count); + /* + * compatibility with old implementation of USB-HID and I2C-HID: + * if the device does not support receiving output reports, + * on an interrupt endpoint, fallback to SET_REPORT HID command. + */ + if (ret != -ENOSYS) + goto out_free; + } + + ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type, + HID_REQ_SET_REPORT); + out_free: kfree(buf); out: return ret; } -/* the first byte is expected to be a report number */ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { ssize_t ret; @@ -168,15 +180,18 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t } -/* This function performs a Get_Report transfer over the control endpoint +/* + * This function performs a Get_Report transfer over the control endpoint * per section 7.2.1 of the HID specification, version 1.1. The first byte * of buffer is the report number to request, or 0x0 if the defice does not * use numbered reports. The report_type parameter can be HID_FEATURE_REPORT - * or HID_INPUT_REPORT. This function is to be called with the minors_lock - * mutex held. */ + * or HID_INPUT_REPORT. + * + * This function is to be called with the minors_lock mutex held. + */ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type) { - unsigned int minor = iminor(file->f_path.dentry->d_inode); + unsigned int minor = iminor(file_inode(file)); struct hid_device *dev; __u8 *buf; int ret = 0, len; @@ -184,7 +199,7 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t dev = hidraw_table[minor]->hid; - if (!dev->hid_get_raw_report) { + if (!dev->ll_driver->raw_request) { ret = -ENODEV; goto out; } @@ -209,14 +224,17 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t goto out; } - /* Read the first byte from the user. This is the report number, - * which is passed to dev->hid_get_raw_report(). */ + /* + * Read the first byte from the user. This is the report number, + * which is passed to hid_hw_raw_request(). + */ if (copy_from_user(&report_number, buffer, 1)) { ret = -EFAULT; goto out_free; } - ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type); + ret = hid_hw_raw_request(dev, report_number, buf, count, report_type, + HID_REQ_GET_REPORT); if (ret < 0) goto out_free; @@ -253,6 +271,7 @@ static int hidraw_open(struct inode *inode, struct file *file) unsigned int minor = iminor(inode); struct hidraw *dev; struct hidraw_list *list; + unsigned long flags; int err = 0; if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { @@ -261,16 +280,11 @@ static int hidraw_open(struct inode *inode, struct file *file) } mutex_lock(&minors_lock); - if (!hidraw_table[minor]) { + if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { err = -ENODEV; goto out_unlock; } - list->hidraw = hidraw_table[minor]; - mutex_init(&list->read_mutex); - list_add_tail(&list->node, &hidraw_table[minor]->list); - file->private_data = list; - dev = hidraw_table[minor]; if (!dev->open++) { err = hid_hw_power(dev->hid, PM_HINT_FULLON); @@ -283,9 +297,16 @@ static int hidraw_open(struct inode *inode, struct file *file) if (err < 0) { hid_hw_power(dev->hid, PM_HINT_NORMAL); dev->open--; + goto out_unlock; } } + list->hidraw = hidraw_table[minor]; + mutex_init(&list->read_mutex); + spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); + list_add_tail(&list->node, &hidraw_table[minor]->list); + spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); + file->private_data = list; out_unlock: mutex_unlock(&minors_lock); out: @@ -302,45 +323,54 @@ static int hidraw_fasync(int fd, struct file *file, int on) return fasync_helper(fd, file, on, &list->fasync); } +static void drop_ref(struct hidraw *hidraw, int exists_bit) +{ + if (exists_bit) { + hidraw->exist = 0; + if (hidraw->open) { + hid_hw_close(hidraw->hid); + wake_up_interruptible(&hidraw->wait); + } + device_destroy(hidraw_class, + MKDEV(hidraw_major, hidraw->minor)); + } else { + --hidraw->open; + } + if (!hidraw->open) { + if (!hidraw->exist) { + hidraw_table[hidraw->minor] = NULL; + kfree(hidraw); + } else { + /* close device for last reader */ + hid_hw_power(hidraw->hid, PM_HINT_NORMAL); + hid_hw_close(hidraw->hid); + } + } +} + static int hidraw_release(struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); - struct hidraw *dev; struct hidraw_list *list = file->private_data; - int ret; - int i; + unsigned long flags; mutex_lock(&minors_lock); - if (!hidraw_table[minor]) { - ret = -ENODEV; - goto unlock; - } + spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); list_del(&list->node); - dev = hidraw_table[minor]; - if (!--dev->open) { - if (list->hidraw->exist) { - hid_hw_power(dev->hid, PM_HINT_NORMAL); - hid_hw_close(dev->hid); - } else { - kfree(list->hidraw); - } - } - - for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i) - kfree(list->buffer[i].value); + spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); kfree(list); - ret = 0; -unlock: - mutex_unlock(&minors_lock); - return ret; + drop_ref(hidraw_table[minor], 0); + + mutex_unlock(&minors_lock); + return 0; } static long hidraw_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); unsigned int minor = iminor(inode); long ret = 0; struct hidraw *dev; @@ -457,7 +487,9 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len) struct hidraw *dev = hid->hidraw; struct hidraw_list *list; int ret = 0; + unsigned long flags; + spin_lock_irqsave(&dev->list_lock, flags); list_for_each_entry(list, &dev->list, node) { int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); @@ -472,6 +504,7 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len) list->head = new_head; kill_fasync(&list->fasync, SIGIO, POLL_IN); } + spin_unlock_irqrestore(&dev->list_lock, flags); wake_up_interruptible(&dev->wait); return ret; @@ -483,7 +516,7 @@ int hidraw_connect(struct hid_device *hid) int minor, result; struct hidraw *dev; - /* we accept any HID device, no matter the applications */ + /* we accept any HID device, all applications */ dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL); if (!dev) @@ -518,8 +551,8 @@ int hidraw_connect(struct hid_device *hid) goto out; } - mutex_unlock(&minors_lock); init_waitqueue_head(&dev->wait); + spin_lock_init(&dev->list_lock); INIT_LIST_HEAD(&dev->list); dev->hid = hid; @@ -528,6 +561,7 @@ int hidraw_connect(struct hid_device *hid) dev->exist = 1; hid->hidraw = dev; + mutex_unlock(&minors_lock); out: return result; @@ -539,18 +573,9 @@ void hidraw_disconnect(struct hid_device *hid) struct hidraw *hidraw = hid->hidraw; mutex_lock(&minors_lock); - hidraw->exist = 0; - device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); + drop_ref(hidraw, 1); - hidraw_table[hidraw->minor] = NULL; - - if (hidraw->open) { - hid_hw_close(hid); - wake_up_interruptible(&hidraw->wait); - } else { - kfree(hidraw); - } mutex_unlock(&minors_lock); } EXPORT_SYMBOL_GPL(hidraw_disconnect); @@ -581,6 +606,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 12e4fdc810b..21aafc8f48c 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -25,6 +25,7 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/device.h> #include <linux/wait.h> #include <linux/err.h> @@ -34,6 +35,8 @@ #include <linux/kernel.h> #include <linux/hid.h> #include <linux/mutex.h> +#include <linux/acpi.h> +#include <linux/of.h> #include <linux/i2c/i2c-hid.h> @@ -107,6 +110,7 @@ static const struct i2c_hid_cmd hid_reset_cmd = { I2C_HID_CMD(0x01), static const struct i2c_hid_cmd hid_get_report_cmd = { I2C_HID_CMD(0x02) }; static const struct i2c_hid_cmd hid_set_report_cmd = { I2C_HID_CMD(0x03) }; static const struct i2c_hid_cmd hid_set_power_cmd = { I2C_HID_CMD(0x08) }; +static const struct i2c_hid_cmd hid_no_cmd = { .length = 0 }; /* * These definitions are not used here, but are defined by the spec. @@ -139,6 +143,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, @@ -251,15 +257,27 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType, return 0; } -static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, - u8 reportID, unsigned char *buf, size_t data_len) +/** + * i2c_hid_set_or_send_report: forward an incoming report to the device + * @client: the i2c_client of the device + * @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT + * @reportID: the report ID + * @buf: the actual data to transfer, without the report ID + * @len: size of buf + * @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report + */ +static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType, + u8 reportID, unsigned char *buf, size_t data_len, bool use_data) { struct i2c_hid *ihid = i2c_get_clientdata(client); u8 *args = ihid->argsbuf; + const struct i2c_hid_cmd *hidcmd; int ret; u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister); + u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister); + u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength); - /* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */ + /* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */ u16 size = 2 /* size */ + (reportID ? 1 : 0) /* reportID */ + data_len /* buf */; @@ -270,13 +288,27 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, i2c_hid_dbg(ihid, "%s\n", __func__); + if (!use_data && maxOutputLength == 0) + return -ENOSYS; + if (reportID >= 0x0F) { args[index++] = reportID; reportID = 0x0F; } - args[index++] = dataRegister & 0xFF; - args[index++] = dataRegister >> 8; + /* + * use the data register for feature reports or if the device does not + * support the output register + */ + if (use_data) { + args[index++] = dataRegister & 0xFF; + args[index++] = dataRegister >> 8; + hidcmd = &hid_set_report_cmd; + } else { + args[index++] = outputRegister & 0xFF; + args[index++] = outputRegister >> 8; + hidcmd = &hid_no_cmd; + } args[index++] = size & 0xFF; args[index++] = size >> 8; @@ -286,7 +318,7 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, memcpy(&args[index], buf, data_len); - ret = __i2c_hid_command(client, &hid_set_report_cmd, reportID, + ret = __i2c_hid_command(client, hidcmd, reportID, reportType, args, args_len, NULL, 0); if (ret) { dev_err(&client->dev, "failed to set a report to device.\n"); @@ -436,14 +468,18 @@ static void i2c_hid_init_reports(struct hid_device *hid) return; } - list_for_each_entry(report, - &hid->report_enum[HID_INPUT_REPORT].report_list, list) - i2c_hid_init_report(report, inbuf, ihid->bufsize); + /* + * The device must be powered on while we fetch initial reports + * from it. + */ + pm_runtime_get_sync(&client->dev); list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) i2c_hid_init_report(report, inbuf, ihid->bufsize); + pm_runtime_put(&client->dev); + kfree(inbuf); } @@ -536,17 +572,51 @@ static int i2c_hid_get_raw_report(struct hid_device *hid, } static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, - size_t count, unsigned char report_type) + size_t count, unsigned char report_type, bool use_data) { struct i2c_client *client = hid->driver_data; int report_id = buf[0]; + int ret; if (report_type == HID_INPUT_REPORT) return -EINVAL; - return i2c_hid_set_report(client, + if (report_id) { + buf++; + count--; + } + + ret = i2c_hid_set_or_send_report(client, report_type == HID_FEATURE_REPORT ? 0x03 : 0x02, - report_id, buf, count); + report_id, buf, count, use_data); + + if (report_id && ret >= 0) + ret++; /* add report_id to the number of transfered bytes */ + + return ret; +} + +static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf, + size_t count) +{ + return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT, + false); +} + +static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, + int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype); + case HID_REQ_SET_REPORT: + if (buf[0] != reportnum) + return -EINVAL; + return i2c_hid_output_raw_report(hid, buf, len, rtype, true); + default: + return -EIO; + } } static int i2c_hid_parse(struct hid_device *hid) @@ -648,8 +718,8 @@ static int i2c_hid_open(struct hid_device *hid) mutex_lock(&i2c_hid_open_mut); if (!hid->open++) { - ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); - if (ret) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { hid->open--; goto done; } @@ -657,7 +727,7 @@ static int i2c_hid_open(struct hid_device *hid) } done: mutex_unlock(&i2c_hid_open_mut); - return ret; + return ret < 0 ? ret : 0; } static void i2c_hid_close(struct hid_device *hid) @@ -674,7 +744,7 @@ static void i2c_hid_close(struct hid_device *hid) clear_bit(I2C_HID_STARTED, &ihid->flags); /* Save some power */ - i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); + pm_runtime_put(&client->dev); } mutex_unlock(&i2c_hid_open_mut); } @@ -683,42 +753,18 @@ static int i2c_hid_power(struct hid_device *hid, int lvl) { struct i2c_client *client = hid->driver_data; struct i2c_hid *ihid = i2c_get_clientdata(client); - int ret = 0; i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl); switch (lvl) { case PM_HINT_FULLON: - ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); + pm_runtime_get_sync(&client->dev); break; case PM_HINT_NORMAL: - ret = i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); + pm_runtime_put(&client->dev); break; } - return ret; -} - -static int i2c_hid_hidinput_input_event(struct input_dev *dev, - unsigned int type, unsigned int code, int value) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct hid_field *field; - int offset; - - if (type == EV_FF) - return input_ff_event(dev, type, code, value); - - if (type != EV_LED) - return -1; - - offset = hidinput_find_field(hid, type, code, &field); - - if (offset == -1) { - hid_warn(dev, "event field not found\n"); - return -1; - } - - return hid_set_field(field, offset, value); + return 0; } static struct hid_ll_driver i2c_hid_ll_driver = { @@ -728,7 +774,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = { .open = i2c_hid_open, .close = i2c_hid_close, .power = i2c_hid_power, - .hidinput_input_event = i2c_hid_hidinput_input_event, + .output_report = i2c_hid_output_report, + .raw_request = i2c_hid_raw_request, }; static int i2c_hid_init_irq(struct i2c_client *client) @@ -760,56 +807,116 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) unsigned int dsize; int ret; - /* Fetch the length of HID description, retrieve the 4 first bytes: + /* i2c hid fetch using a fixed descriptor size (30 bytes) */ + i2c_hid_dbg(ihid, "Fetching the HID descriptor\n"); + ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, + sizeof(struct i2c_hid_desc)); + if (ret) { + dev_err(&client->dev, "hid_descr_cmd failed\n"); + return -ENODEV; + } + + /* Validate the length of HID descriptor, the 4 first bytes: * bytes 0-1 -> length * bytes 2-3 -> bcdVersion (has to be 1.00) */ - ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, 4); - - i2c_hid_dbg(ihid, "%s, ihid->hdesc_buffer: %*ph\n", - __func__, 4, ihid->hdesc_buffer); - - if (ret) { + /* check bcdVersion == 1.0 */ + if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) { dev_err(&client->dev, - "unable to fetch the size of HID descriptor (ret=%d)\n", - ret); + "unexpected HID descriptor bcdVersion (0x%04hx)\n", + le16_to_cpu(hdesc->bcdVersion)); return -ENODEV; } + /* Descriptor length should be 30 bytes as per the specification */ dsize = le16_to_cpu(hdesc->wHIDDescLength); - /* - * the size of the HID descriptor should at least contain - * its size and the bcdVersion (4 bytes), and should not be greater - * than sizeof(struct i2c_hid_desc) as we directly fill this struct - * through i2c_hid_command. - */ - if (dsize < 4 || dsize > sizeof(struct i2c_hid_desc)) { + if (dsize != sizeof(struct i2c_hid_desc)) { dev_err(&client->dev, "weird size of HID descriptor (%u)\n", dsize); return -ENODEV; } + i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer); + return 0; +} - /* check bcdVersion == 1.0 */ - if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) { - dev_err(&client->dev, - "unexpected HID descriptor bcdVersion (0x%04hx)\n", - le16_to_cpu(hdesc->bcdVersion)); +#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, + }; + union acpi_object *obj; + struct acpi_device *adev; + acpi_handle handle; + + handle = ACPI_HANDLE(&client->dev); + if (!handle || acpi_bus_get_device(handle, &adev)) + return -ENODEV; + + obj = acpi_evaluate_dsm_typed(handle, i2c_hid_guid, 1, 1, NULL, + ACPI_TYPE_INTEGER); + if (!obj) { + dev_err(&client->dev, "device _DSM execution failed\n"); return -ENODEV; } - i2c_hid_dbg(ihid, "Fetching the HID descriptor\n"); + pdata->hid_descriptor_address = obj->integer.value; + ACPI_FREE(obj); - ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, - dsize); + 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 + +#ifdef CONFIG_OF +static int i2c_hid_of_probe(struct i2c_client *client, + struct i2c_hid_platform_data *pdata) +{ + struct device *dev = &client->dev; + u32 val; + int ret; + + ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val); if (ret) { - dev_err(&client->dev, "hid_descr_cmd Fail\n"); + dev_err(&client->dev, "HID register address not provided\n"); return -ENODEV; } - - i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer); + if (val >> 16) { + dev_err(&client->dev, "Bad HID register address: 0x%08x\n", + val); + return -EINVAL; + } + pdata->hid_descriptor_address = val; return 0; } +static const struct of_device_id i2c_hid_of_match[] = { + { .compatible = "hid-over-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_hid_of_match); +#else +static inline int i2c_hid_of_probe(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) { @@ -821,11 +928,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"); @@ -836,11 +938,26 @@ static int i2c_hid_probe(struct i2c_client *client, if (!ihid) return -ENOMEM; + if (client->dev.of_node) { + ret = i2c_hid_of_probe(client, &ihid->pdata); + if (ret) + goto err; + } else 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); @@ -852,13 +969,17 @@ static int i2c_hid_probe(struct i2c_client *client, if (ret < 0) goto err; + pm_runtime_get_noresume(&client->dev); + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + ret = i2c_hid_fetch_hid_descriptor(ihid); if (ret < 0) - goto err; + goto err_pm; ret = i2c_hid_init_irq(client); if (ret < 0) - goto err; + goto err_pm; hid = hid_allocate_device(); if (IS_ERR(hid)) { @@ -870,9 +991,8 @@ static int i2c_hid_probe(struct i2c_client *client, hid->driver_data = client; hid->ll_driver = &i2c_hid_ll_driver; - 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_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev)); hid->bus = BUS_I2C; hid->version = le16_to_cpu(ihid->hdesc.bcdVersion); hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); @@ -888,6 +1008,7 @@ static int i2c_hid_probe(struct i2c_client *client, goto err_mem_free; } + pm_runtime_put(&client->dev); return 0; err_mem_free: @@ -896,6 +1017,10 @@ err_mem_free: err_irq: free_irq(client->irq, ihid); +err_pm: + pm_runtime_put_noidle(&client->dev); + pm_runtime_disable(&client->dev); + err: i2c_hid_free_buffers(ihid); kfree(ihid); @@ -907,6 +1032,11 @@ static int i2c_hid_remove(struct i2c_client *client) struct i2c_hid *ihid = i2c_get_clientdata(client); struct hid_device *hid; + pm_runtime_get_sync(&client->dev); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + hid = ihid->hid; hid_destroy_device(hid); @@ -925,6 +1055,7 @@ static int i2c_hid_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + disable_irq(client->irq); if (device_may_wakeup(&client->dev)) enable_irq_wake(client->irq); @@ -939,6 +1070,7 @@ static int i2c_hid_resume(struct device *dev) int ret; struct i2c_client *client = to_i2c_client(dev); + enable_irq(client->irq); ret = i2c_hid_hwreset(client); if (ret) return ret; @@ -950,7 +1082,31 @@ static int i2c_hid_resume(struct device *dev) } #endif -static SIMPLE_DEV_PM_OPS(i2c_hid_pm, i2c_hid_suspend, i2c_hid_resume); +#ifdef CONFIG_PM_RUNTIME +static int i2c_hid_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); + disable_irq(client->irq); + return 0; +} + +static int i2c_hid_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + enable_irq(client->irq); + i2c_hid_set_power(client, I2C_HID_PWR_ON); + return 0; +} +#endif + +static const struct dev_pm_ops i2c_hid_pm = { + SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume) + SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume, + NULL) +}; static const struct i2c_device_id i2c_hid_id_table[] = { { "hid", 0 }, @@ -964,6 +1120,8 @@ 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), + .of_match_table = of_match_ptr(i2c_hid_of_match), }, .probe = i2c_hid_probe, diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 714cd8cc957..0cb92e34725 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> @@ -115,30 +116,6 @@ static void uhid_hid_close(struct hid_device *hid) uhid_queue_event(uhid, UHID_CLOSE); } -static int uhid_hid_input(struct input_dev *input, unsigned int type, - unsigned int code, int value) -{ - struct hid_device *hid = input_get_drvdata(input); - struct uhid_device *uhid = hid->driver_data; - unsigned long flags; - struct uhid_event *ev; - - ev = kzalloc(sizeof(*ev), GFP_ATOMIC); - if (!ev) - return -ENOMEM; - - ev->type = UHID_OUTPUT_EV; - ev->u.output_ev.type = type; - ev->u.output_ev.code = code; - ev->u.output_ev.value = value; - - spin_lock_irqsave(&uhid->qlock, flags); - uhid_queue(uhid, ev); - spin_unlock_irqrestore(&uhid->qlock, flags); - - return 0; -} - static int uhid_hid_parse(struct hid_device *hid) { struct uhid_device *uhid = hid->driver_data; @@ -267,15 +244,125 @@ static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, return count; } +static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf, + size_t count) +{ + return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT); +} + +static int uhid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, + int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + return uhid_hid_get_raw(hid, reportnum, buf, len, rtype); + case HID_REQ_SET_REPORT: + /* TODO: implement proper SET_REPORT functionality */ + return -ENOSYS; + default: + return -EIO; + } +} + static struct hid_ll_driver uhid_hid_driver = { .start = uhid_hid_start, .stop = uhid_hid_stop, .open = uhid_hid_open, .close = uhid_hid_close, - .hidinput_input_event = uhid_hid_input, .parse = uhid_hid_parse, + .output_report = uhid_hid_output_report, + .raw_request = uhid_raw_request, }; +#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 = kzalloc(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) { @@ -313,8 +400,6 @@ static int uhid_dev_create(struct uhid_device *uhid, hid->uniq[63] = 0; hid->ll_driver = &uhid_hid_driver; - hid->hid_get_raw_report = uhid_hid_get_raw; - hid->hid_output_raw_report = uhid_hid_output_raw; hid->bus = ev->u.create.bus; hid->vendor = ev->u.create.vendor; hid->product = ev->u.create.product; @@ -343,6 +428,66 @@ err_free: return ret; } +static int uhid_dev_create2(struct uhid_device *uhid, + const struct uhid_event *ev) +{ + struct hid_device *hid; + int ret; + + if (uhid->running) + return -EALREADY; + + uhid->rd_size = ev->u.create2.rd_size; + if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) + return -EINVAL; + + uhid->rd_data = kmemdup(ev->u.create2.rd_data, uhid->rd_size, + GFP_KERNEL); + if (!uhid->rd_data) + return -ENOMEM; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err_free; + } + + strncpy(hid->name, ev->u.create2.name, 127); + hid->name[127] = 0; + strncpy(hid->phys, ev->u.create2.phys, 63); + hid->phys[63] = 0; + strncpy(hid->uniq, ev->u.create2.uniq, 63); + hid->uniq[63] = 0; + + hid->ll_driver = &uhid_hid_driver; + hid->bus = ev->u.create2.bus; + hid->vendor = ev->u.create2.vendor; + hid->product = ev->u.create2.product; + hid->version = ev->u.create2.version; + hid->country = ev->u.create2.country; + hid->driver_data = uhid; + hid->dev.parent = uhid_misc.this_device; + + uhid->hid = hid; + uhid->running = true; + + ret = hid_add_device(hid); + if (ret) { + hid_err(hid, "Cannot register HID device\n"); + goto err_hid; + } + + return 0; + +err_hid: + hid_destroy_device(hid); + uhid->hid = NULL; + uhid->running = false; +err_free: + kfree(uhid->rd_data); + return ret; +} + static int uhid_dev_destroy(struct uhid_device *uhid) { if (!uhid->running) @@ -371,6 +516,17 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) return 0; } +static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev) +{ + if (!uhid->running) + return -EINVAL; + + hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data, + min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0); + + return 0; +} + static int uhid_dev_feature_answer(struct uhid_device *uhid, struct uhid_event *ev) { @@ -498,21 +654,27 @@ 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: ret = uhid_dev_create(uhid, &uhid->input_buf); break; + case UHID_CREATE2: + ret = uhid_dev_create2(uhid, &uhid->input_buf); + break; case UHID_DESTROY: ret = uhid_dev_destroy(uhid); break; case UHID_INPUT: ret = uhid_dev_input(uhid, &uhid->input_buf); break; + case UHID_INPUT2: + ret = uhid_dev_input2(uhid, &uhid->input_buf); + break; case UHID_FEATURE_ANSWER: ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); break; @@ -551,7 +713,7 @@ static const struct file_operations uhid_fops = { static struct miscdevice uhid_misc = { .fops = &uhid_fops, - .minor = MISC_DYNAMIC_MINOR, + .minor = UHID_MINOR, .name = UHID_NAME, }; @@ -570,3 +732,5 @@ module_exit(uhid_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>"); MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); +MODULE_ALIAS_MISCDEV(UHID_MINOR); +MODULE_ALIAS("devname:" UHID_NAME); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 8e0c4bf94eb..7b88f4cb990 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -535,7 +535,6 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re { int head; struct usbhid_device *usbhid = hid->driver_data; - int len = ((report->size - 1) >> 3) + 1 + (report->id > 0); if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) return; @@ -546,7 +545,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re return; } - usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC); + usbhid->out[usbhid->outhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC); if (!usbhid->out[usbhid->outhead].raw_report) { hid_warn(hid, "output queueing failed\n"); return; @@ -595,7 +594,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re } if (dir == USB_DIR_OUT) { - usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC); + usbhid->ctrl[usbhid->ctrlhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC); if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) { hid_warn(hid, "control queueing failed\n"); return; @@ -639,7 +638,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re } } -void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) +static void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) { struct usbhid_device *usbhid = hid->driver_data; unsigned long flags; @@ -648,65 +647,8 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns __usbhid_submit_report(hid, report, dir); spin_unlock_irqrestore(&usbhid->lock, flags); } -EXPORT_SYMBOL_GPL(usbhid_submit_report); -/* Workqueue routine to send requests to change LEDs */ -static void hid_led(struct work_struct *work) -{ - struct usbhid_device *usbhid = - container_of(work, struct usbhid_device, led_work); - struct hid_device *hid = usbhid->hid; - struct hid_field *field; - unsigned long flags; - - field = hidinput_get_led_field(hid); - if (!field) { - hid_warn(hid, "LED event field not found\n"); - return; - } - - spin_lock_irqsave(&usbhid->lock, flags); - if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) { - usbhid->ledcount = hidinput_count_leds(hid); - hid_dbg(usbhid->hid, "New ledcount = %u\n", usbhid->ledcount); - __usbhid_submit_report(hid, field->report, USB_DIR_OUT); - } - spin_unlock_irqrestore(&usbhid->lock, flags); -} - -static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct usbhid_device *usbhid = hid->driver_data; - struct hid_field *field; - unsigned long flags; - int offset; - - if (type == EV_FF) - return input_ff_event(dev, type, code, value); - - if (type != EV_LED) - return -1; - - if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) { - hid_warn(dev, "event field not found\n"); - return -1; - } - - spin_lock_irqsave(&usbhid->lock, flags); - hid_set_field(field, offset, value); - spin_unlock_irqrestore(&usbhid->lock, flags); - - /* - * Defer performing requested LED action. - * This is more likely gather all LED changes into a single URB. - */ - schedule_work(&usbhid->led_work); - - return 0; -} - -int usbhid_wait_io(struct hid_device *hid) +static int usbhid_wait_io(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; @@ -720,7 +662,6 @@ int usbhid_wait_io(struct hid_device *hid) return 0; } -EXPORT_SYMBOL_GPL(usbhid_wait_io); static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) { @@ -809,12 +750,17 @@ void usbhid_init_reports(struct hid_device *hid) { struct hid_report *report; struct usbhid_device *usbhid = hid->driver_data; + struct hid_report_enum *report_enum; int err, ret; - list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) - usbhid_submit_report(hid, report, USB_DIR_IN); + if (!(hid->quirks & HID_QUIRK_NO_INIT_INPUT_REPORTS)) { + report_enum = &hid->report_enum[HID_INPUT_REPORT]; + list_for_each_entry(report, &report_enum->report_list, list) + usbhid_submit_report(hid, report, USB_DIR_IN); + } - list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) + report_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(report, &report_enum->report_list, list) usbhid_submit_report(hid, report, USB_DIR_IN); err = 0; @@ -859,7 +805,7 @@ static int hid_find_field_early(struct hid_device *hid, unsigned int page, return -1; } -void usbhid_set_leds(struct hid_device *hid) +static void usbhid_set_leds(struct hid_device *hid) { struct hid_field *field; int offset; @@ -869,7 +815,6 @@ void usbhid_set_leds(struct hid_device *hid) usbhid_submit_report(hid, field->report, USB_DIR_OUT); } } -EXPORT_SYMBOL_GPL(usbhid_set_leds); /* * Traverse the supplied list of reports and find the longest @@ -939,52 +884,66 @@ static int usbhid_get_raw_report(struct hid_device *hid, return ret; } -static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, - unsigned char report_type) +static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum, + __u8 *buf, size_t count, unsigned char rtype) { struct usbhid_device *usbhid = hid->driver_data; struct usb_device *dev = hid_to_usb_dev(hid); struct usb_interface *intf = usbhid->intf; struct usb_host_interface *interface = intf->cur_altsetting; - int ret; + int ret, skipped_report_id = 0; - if (usbhid->urbout && report_type != HID_FEATURE_REPORT) { - int actual_length; - int skipped_report_id = 0; + /* Byte 0 is the report number. Report data starts at byte 1.*/ + if ((rtype == HID_OUTPUT_REPORT) && + (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORT_ID)) + buf[0] = 0; + else + buf[0] = reportnum; + + if (buf[0] == 0x0) { + /* Don't send the Report ID */ + buf++; + count--; + skipped_report_id = 1; + } - if (buf[0] == 0x0) { - /* Don't send the Report ID */ - buf++; - count--; - skipped_report_id = 1; - } - ret = usb_interrupt_msg(dev, usbhid->urbout->pipe, - buf, count, &actual_length, - USB_CTRL_SET_TIMEOUT); - /* return the number of bytes transferred */ - if (ret == 0) { - ret = actual_length; - /* count also the report id */ - if (skipped_report_id) - ret++; - } - } else { - int skipped_report_id = 0; - int report_id = buf[0]; - if (buf[0] == 0x0) { - /* Don't send the Report ID */ - buf++; - count--; - skipped_report_id = 1; - } - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), HID_REQ_SET_REPORT, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - ((report_type + 1) << 8) | report_id, + ((rtype + 1) << 8) | reportnum, interface->desc.bInterfaceNumber, buf, count, USB_CTRL_SET_TIMEOUT); - /* count also the report id, if this was a numbered report. */ - if (ret > 0 && skipped_report_id) + /* count also the report id, if this was a numbered report. */ + if (ret > 0 && skipped_report_id) + ret++; + + return ret; +} + +static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count) +{ + struct usbhid_device *usbhid = hid->driver_data; + struct usb_device *dev = hid_to_usb_dev(hid); + int actual_length, skipped_report_id = 0, ret; + + if (!usbhid->urbout) + return -ENOSYS; + + if (buf[0] == 0x0) { + /* Don't send the Report ID */ + buf++; + count--; + skipped_report_id = 1; + } + + ret = usb_interrupt_msg(dev, usbhid->urbout->pipe, + buf, count, &actual_length, + USB_CTRL_SET_TIMEOUT); + /* return the number of bytes transferred */ + if (ret == 0) { + ret = actual_length; + /* count also the report id */ + if (skipped_report_id) ret++; } @@ -1243,6 +1202,46 @@ static int usbhid_power(struct hid_device *hid, int lvl) return r; } +static void usbhid_request(struct hid_device *hid, struct hid_report *rep, int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + usbhid_submit_report(hid, rep, USB_DIR_IN); + break; + case HID_REQ_SET_REPORT: + usbhid_submit_report(hid, rep, USB_DIR_OUT); + break; + } +} + +static int usbhid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, + int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + return usbhid_get_raw_report(hid, reportnum, buf, len, rtype); + case HID_REQ_SET_REPORT: + return usbhid_set_raw_report(hid, reportnum, buf, len, rtype); + default: + return -EIO; + } +} + +static int usbhid_idle(struct hid_device *hid, int report, int idle, + int reqtype) +{ + struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_interface *intf = to_usb_interface(hid->dev.parent); + struct usb_host_interface *interface = intf->cur_altsetting; + int ifnum = interface->desc.bInterfaceNumber; + + if (reqtype != HID_REQ_SET_IDLE) + return -EINVAL; + + return hid_set_idle(dev, ifnum, report, idle); +} + static struct hid_ll_driver usb_hid_driver = { .parse = usbhid_parse, .start = usbhid_start, @@ -1250,7 +1249,11 @@ static struct hid_ll_driver usb_hid_driver = { .open = usbhid_open, .close = usbhid_close, .power = usbhid_power, - .hidinput_input_event = usb_hidinput_input_event, + .request = usbhid_request, + .wait = usbhid_wait_io, + .raw_request = usbhid_raw_request, + .output_report = usbhid_output_report, + .idle = usbhid_idle, }; static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -1280,8 +1283,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * usb_set_intfdata(intf, hid); hid->ll_driver = &usb_hid_driver; - hid->hid_get_raw_report = usbhid_get_raw_report; - hid->hid_output_raw_report = usbhid_output_raw_report; hid->ff_init = hid_pidff_init; #ifdef CONFIG_USB_HIDDEV hid->hiddev_connect = hiddev_connect; @@ -1341,8 +1342,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); spin_lock_init(&usbhid->lock); - INIT_WORK(&usbhid->led_work, hid_led); - ret = hid_add_device(hid); if (ret) { if (ret != -ENODEV) @@ -1375,7 +1374,6 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid) { del_timer_sync(&usbhid->io_retry); cancel_work_sync(&usbhid->reset_work); - cancel_work_sync(&usbhid->led_work); } static void hid_cease_io(struct usbhid_device *usbhid) @@ -1493,17 +1491,19 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) { struct hid_device *hid = usb_get_intfdata(intf); struct usbhid_device *usbhid = hid->driver_data; - int status; + int status = 0; bool driver_suspended = false; + unsigned int ledcount; if (PMSG_IS_AUTO(message)) { + ledcount = hidinput_count_leds(hid); spin_lock_irq(&usbhid->lock); /* Sync with error handler */ if (!test_bit(HID_RESET_PENDING, &usbhid->iofl) && !test_bit(HID_CLEAR_HALT, &usbhid->iofl) && !test_bit(HID_OUT_RUNNING, &usbhid->iofl) && !test_bit(HID_CTRL_RUNNING, &usbhid->iofl) && !test_bit(HID_KEYS_PRESSED, &usbhid->iofl) - && (!usbhid->ledcount || ignoreled)) + && (!ledcount || ignoreled)) { set_bit(HID_SUSPENDED, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); @@ -1520,19 +1520,15 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) } } else { - if (hid->driver && hid->driver->suspend) { + /* TODO: resume() might need to handle suspend failure */ + if (hid->driver && hid->driver->suspend) status = hid->driver->suspend(hid, message); - if (status < 0) - return status; - } driver_suspended = true; spin_lock_irq(&usbhid->lock); set_bit(HID_SUSPENDED, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); - if (usbhid_wait_io(hid) < 0) { + if (usbhid_wait_io(hid) < 0) status = -EIO; - goto failed; - } } hid_cancel_delayed_stuff(usbhid); @@ -1544,7 +1540,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) goto failed; } dev_dbg(&intf->dev, "suspend\n"); - return 0; + return status; failed: hid_resume_common(hid, driver_suspended); diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index f91c136821f..10b61670278 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -263,8 +263,8 @@ static void pidff_set_envelope_report(struct pidff_device *pidff, envelope->attack_level, pidff->set_envelope[PID_ATTACK_LEVEL].value[0]); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_ENVELOPE], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_ENVELOPE], + HID_REQ_SET_REPORT); } /* @@ -290,8 +290,8 @@ static void pidff_set_constant_force_report(struct pidff_device *pidff, pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE], effect->u.constant.level); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONSTANT], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_CONSTANT], + HID_REQ_SET_REPORT); } /* @@ -325,8 +325,8 @@ static void pidff_set_effect_report(struct pidff_device *pidff, pidff->effect_direction); pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay; - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT], + HID_REQ_SET_REPORT); } /* @@ -357,8 +357,8 @@ static void pidff_set_periodic_report(struct pidff_device *pidff, pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase); pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period; - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_PERIODIC], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_PERIODIC], + HID_REQ_SET_REPORT); } @@ -399,8 +399,8 @@ static void pidff_set_condition_report(struct pidff_device *pidff, effect->u.condition[i].left_saturation); pidff_set(&pidff->set_condition[PID_DEAD_BAND], effect->u.condition[i].deadband); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_CONDITION], + HID_REQ_SET_REPORT); } } @@ -440,8 +440,8 @@ static void pidff_set_ramp_force_report(struct pidff_device *pidff, effect->u.ramp.start_level); pidff_set_signed(&pidff->set_ramp[PID_RAMP_END], effect->u.ramp.end_level); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_RAMP], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_RAMP], + HID_REQ_SET_REPORT); } /* @@ -465,19 +465,19 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum) int j; pidff->create_new_effect_type->value[0] = efnum; - usbhid_submit_report(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT], + HID_REQ_SET_REPORT); hid_dbg(pidff->hid, "create_new_effect sent, type: %d\n", efnum); pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0; pidff->block_load_status->value[0] = 0; - usbhid_wait_io(pidff->hid); + hid_hw_wait(pidff->hid); for (j = 0; j < 60; j++) { hid_dbg(pidff->hid, "pid_block_load requested\n"); - usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_LOAD], - USB_DIR_IN); - usbhid_wait_io(pidff->hid); + hid_hw_request(pidff->hid, pidff->reports[PID_BLOCK_LOAD], + HID_REQ_GET_REPORT); + hid_hw_wait(pidff->hid); if (pidff->block_load_status->value[0] == pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) { hid_dbg(pidff->hid, "device reported free memory: %d bytes\n", @@ -513,8 +513,8 @@ static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n) pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; } - usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], + HID_REQ_SET_REPORT); } /** @@ -535,8 +535,8 @@ static int pidff_playback(struct input_dev *dev, int effect_id, int value) static void pidff_erase_pid(struct pidff_device *pidff, int pid_id) { pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; - usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_FREE], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_BLOCK_FREE], + HID_REQ_SET_REPORT); } /* @@ -551,7 +551,7 @@ static int pidff_erase_effect(struct input_dev *dev, int effect_id) effect_id, pidff->pid_id[effect_id]); /* Wait for the queue to clear. We do not want a full fifo to prevent the effect removal. */ - usbhid_wait_io(pidff->hid); + hid_hw_wait(pidff->hid); pidff_playback_pid(pidff, pid_id, 0); pidff_erase_pid(pidff, pid_id); @@ -718,8 +718,8 @@ static void pidff_set_gain(struct input_dev *dev, u16 gain) struct pidff_device *pidff = dev->ff->private; pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain); - usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_DEVICE_GAIN], + HID_REQ_SET_REPORT); } static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude) @@ -744,8 +744,8 @@ static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude) pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; pidff->set_effect[PID_START_DELAY].value[0] = 0; - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT], + HID_REQ_SET_REPORT); } /* @@ -1158,19 +1158,19 @@ static void pidff_reset(struct pidff_device *pidff) pidff->device_control->value[0] = pidff->control_id[PID_RESET]; /* We reset twice as sometimes hid_wait_io isn't waiting long enough */ - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); - usbhid_wait_io(hid); - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); - usbhid_wait_io(hid); + hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); + hid_hw_wait(hid); + hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); + hid_hw_wait(hid); pidff->device_control->value[0] = pidff->control_id[PID_ENABLE_ACTUATORS]; - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); - usbhid_wait_io(hid); + hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); + hid_hw_wait(hid); /* pool report is sometimes messed up, refetch it */ - usbhid_submit_report(hid, pidff->reports[PID_POOL], USB_DIR_IN); - usbhid_wait_io(hid); + hid_hw_request(hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT); + hid_hw_wait(hid); if (pidff->pool[PID_SIMULTANEOUS_MAX].value) { while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) { @@ -1181,9 +1181,9 @@ static void pidff_reset(struct pidff_device *pidff) break; } hid_dbg(pidff->hid, "pid_pool requested again\n"); - usbhid_submit_report(hid, pidff->reports[PID_POOL], - USB_DIR_IN); - usbhid_wait_io(hid); + hid_hw_request(hid, pidff->reports[PID_POOL], + HID_REQ_GET_REPORT); + hid_hw_wait(hid); } } } @@ -1269,8 +1269,8 @@ int hid_pidff_init(struct hid_device *hid) if (test_bit(FF_GAIN, dev->ffbit)) { pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff); - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_GAIN], - USB_DIR_OUT); + hid_hw_request(hid, pidff->reports[PID_DEVICE_GAIN], + HID_REQ_SET_REPORT); } error = pidff_check_autocenter(pidff, dev); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index ac9e3522825..31e6727cd00 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -49,6 +49,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH_2968, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, @@ -70,17 +71,27 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1610, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1640, HID_QUIRK_NOGET }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET }, { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_REALTEK, USB_DEVICE_ID_REALTEK_READER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SIGMATEL, USB_DEVICE_ID_SIGMATEL_STMP3780, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS9200_TOUCH, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS817_TOUCH, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS_TS, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS1030_TOUCH, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_1, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_2, HID_QUIRK_NOGET }, @@ -106,6 +117,14 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS }, + { 0, 0 } }; diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 87bd64959a9..2f1ddca6f2e 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -705,8 +705,8 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (report == NULL) break; - usbhid_submit_report(hid, report, USB_DIR_IN); - usbhid_wait_io(hid); + hid_hw_request(hid, report, HID_REQ_GET_REPORT); + hid_hw_wait(hid); r = 0; break; @@ -724,8 +724,8 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (report == NULL) break; - usbhid_submit_report(hid, report, USB_DIR_OUT); - usbhid_wait_io(hid); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); + hid_hw_wait(hid); r = 0; break; diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index bd87a61e530..f633c24ce28 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -34,12 +34,9 @@ #include <linux/input.h> /* API provided by hid-core.c for USB HID drivers */ -int usbhid_wait_io(struct hid_device* hid); void usbhid_close(struct hid_device *hid); int usbhid_open(struct hid_device *hid); void usbhid_init_reports(struct hid_device *hid); -void usbhid_submit_report -(struct hid_device *hid, struct hid_report *report, unsigned char dir); int usbhid_get_power(struct hid_device *hid); void usbhid_put_power(struct hid_device *hid); struct usb_interface *usbhid_find_interface(int minor); @@ -95,9 +92,6 @@ struct usbhid_device { unsigned int retry_delay; /* Delay length in ms */ struct work_struct reset_work; /* Task context for resets */ wait_queue_head_t wait; /* For sleeping */ - int ledcount; /* counting the number of active leds */ - - struct work_struct led_work; /* Task context for setting LEDs */ }; #define hid_to_usb_dev(hid_dev) \ diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 796086980f4..9a332e683db 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -146,7 +146,7 @@ static void usb_kbd_irq(struct urb *urb) input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); else hid_info(urb->dev, - "Unknown key (scancode %#x) released.\n", + "Unknown key (scancode %#x) pressed.\n", kbd->new[i]); } } |
