diff options
Diffstat (limited to 'drivers/hid')
73 files changed, 11458 insertions, 3840 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index fb52f3f6de8..5e79c6ad914 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -175,6 +175,15 @@ 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 HID @@ -217,6 +226,13 @@ config HID_ELECOM ---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 HID @@ -231,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" @@ -240,6 +261,12 @@ 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 HID @@ -306,7 +333,7 @@ config HID_LCPOWER 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--- @@ -326,6 +353,7 @@ config HID_LOGITECH 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. @@ -345,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" @@ -420,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 @@ -436,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 @@ -561,15 +593,6 @@ config HID_PRIMAX Support for Primax devices that are not fully compliant with the HID standard. -config HID_PS3REMOTE - tristate "Sony PS3 BD Remote Control" - depends on HID - ---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 @@ -585,7 +608,10 @@ config HID_SAITEK 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" @@ -594,12 +620,27 @@ config HID_SAMSUNG 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" @@ -619,6 +660,14 @@ config HID_SUNPLUS ---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 HID @@ -707,22 +756,37 @@ config HID_WACOM Support for Wacom Graphire Bluetooth and Intuos4 WL tablets. config HID_WIIMOTE - tristate "Nintendo Wii Remote support" + 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. -config HID_WIIMOTE_EXT - bool "Nintendo Wii Remote Extension support" - depends on HID_WIIMOTE - default HID_WIIMOTE + 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_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" @@ -746,7 +810,7 @@ config HID_ZYDACRON config HID_SENSOR_HUB tristate "HID Sensors framework support" - depends on HID && GENERIC_HARDIRQS + depends on HID && HAS_IOMEM select MFD_CORE default n ---help--- diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 2065694f57a..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 @@ -44,14 +41,18 @@ 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 @@ -92,11 +93,11 @@ 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-konepure.o hid-roccat-kovaplus.o \ - hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-savu.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 @@ -111,6 +112,7 @@ 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 7c5507e9482..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,7 +132,6 @@ 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); diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index feae88b53fc..f822fd2a1ad 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -46,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; @@ -150,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) { @@ -242,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; } @@ -349,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; @@ -362,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) @@ -373,19 +395,10 @@ 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[] = { @@ -456,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), @@ -524,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), @@ -545,7 +567,6 @@ 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, diff --git a/drivers/hid/hid-appleir.c b/drivers/hid/hid-appleir.c index a42e6a394c5..0e6a42d37eb 100644 --- a/drivers/hid/hid-appleir.c +++ b/drivers/hid/hid-appleir.c @@ -297,6 +297,9 @@ static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id) 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); diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c index 64ab94a55aa..a594e478a1e 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -95,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; @@ -180,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); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 264f5509994..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,48 +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->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; } @@ -755,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 * @@ -1128,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) @@ -1144,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 @@ -1152,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); @@ -1189,6 +1341,41 @@ 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) { @@ -1228,7 +1415,7 @@ 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; @@ -1293,10 +1480,8 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i 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); @@ -1480,6 +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_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) }, @@ -1547,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) }, @@ -1565,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) }, @@ -1573,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) }, @@ -1584,10 +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) }, @@ -1618,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) }, @@ -1640,6 +1843,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { 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) }, @@ -1665,25 +1869,39 @@ 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) }, @@ -1725,6 +1943,7 @@ 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) }, @@ -1906,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) { @@ -1938,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, @@ -1966,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) }, @@ -2042,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) }, @@ -2120,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) }, { } }; @@ -2179,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) }, { } @@ -2228,15 +2456,6 @@ 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) - return true; - break; case USB_VENDOR_ID_VELLEMAN: /* These are not HID devices. They are handled by comedi. */ if ((hdev->product >= USB_DEVICE_ID_VELLEMAN_K8055_FIRST && @@ -2281,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. */ 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-debug.c b/drivers/hid/hid-debug.c index 8453214ec37..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" }, @@ -768,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", @@ -797,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", @@ -852,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] = { 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-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 9a8f0512452..9325545fc3a 100644 --- a/drivers/hid/hid-holtekff.c +++ b/drivers/hid/hid-holtekff.c @@ -98,7 +98,7 @@ 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); hid_hw_request(hid, holtekff->field->report, HID_REQ_SET_REPORT); } 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 aa3fec0d9dc..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; @@ -592,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-ids.h b/drivers/hid/hid-ids.h index 38535c9243d..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,6 +138,10 @@ #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_IRCONTROL 0x8240 @@ -237,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 @@ -248,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 @@ -272,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 @@ -307,6 +323,7 @@ #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 @@ -327,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 @@ -425,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 @@ -432,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 @@ -440,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 @@ -447,6 +486,10 @@ #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 @@ -467,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 @@ -528,6 +574,7 @@ #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 @@ -554,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 @@ -578,6 +626,7 @@ #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 @@ -586,6 +635,9 @@ #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 @@ -611,6 +663,7 @@ #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 @@ -643,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 @@ -662,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 @@ -701,21 +757,30 @@ #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 @@ -725,6 +790,12 @@ #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 @@ -733,7 +804,10 @@ #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 @@ -771,6 +845,14 @@ #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 @@ -864,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 @@ -895,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 945b8158ec4..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,7 +1301,7 @@ 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); @@ -1273,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++) { @@ -1374,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-kye.c b/drivers/hid/hid-kye.c index 6af90dbdc3d..e7769636759 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -268,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) { @@ -314,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; } @@ -390,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; @@ -407,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); diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c index 07837f5a4eb..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; } @@ -339,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; @@ -356,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); @@ -389,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; } @@ -436,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); diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 6f12ecd36c8..a976f48263f 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -45,7 +45,9 @@ /* 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 descriptors for Logitech Driving Force (and Pro) * wheel controllers @@ -170,6 +172,73 @@ 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), */ @@ -216,6 +285,54 @@ static __u8 momo_rdesc_fixed[] = { 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 * above the logical maximum described in descriptor. This extends @@ -275,6 +392,24 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, } 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, @@ -492,6 +627,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, 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: @@ -556,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 */ @@ -568,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); } } @@ -622,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 }, @@ -639,6 +779,8 @@ static const struct hid_device_id lg_devices[] = { .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), diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c index b3cd1507dda..0e3fb1a7e42 100644 --- a/drivers/hid/hid-lg2ff.c +++ b/drivers/hid/hid-lg2ff.c @@ -64,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,7 +95,7 @@ int lg2ff_init(struct hid_device *hid) 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 e52f181f6aa..8c2da183d3b 100644 --- a/drivers/hid/hid-lg3ff.c +++ b/drivers/hid/hid-lg3ff.c @@ -66,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: @@ -129,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 0ddae2a00d5..cc2bd202219 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -43,6 +43,7 @@ #define G25_REV_MIN 0x22 #define G27_REV_MAJ 0x12 #define G27_REV_MIN 0x38 +#define G27_2_REV_MIN 0x39 #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) @@ -51,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,6 +131,7 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = { {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 */ @@ -196,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; @@ -218,12 +235,70 @@ 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; @@ -484,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"); + if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7)) return -1; - } - - field = report->field[0]; - if (!field) { - hid_err(hid, "NULL field\n"); - return -1; - } /* Check what wheel has been connected */ for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) { @@ -558,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) { @@ -589,6 +635,17 @@ 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) diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c index d7ea8c845b4..e1394af0ae7 100644 --- a/drivers/hid/hid-lgff.c +++ b/drivers/hid/hid-lgff.c @@ -128,27 +128,14 @@ static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude) 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 5207591a598..486dbde2ba2 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -44,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) */ @@ -60,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 }; @@ -189,9 +193,7 @@ 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, struct dj_report *dj_report) @@ -232,6 +234,7 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] & SPFUNCTION_DEVICE_LIST_EMPTY) { dbg_hid("%s: device list is empty\n", __func__); + djrcv_dev->querying_devices = false; return; } @@ -242,6 +245,12 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, return; } + if (djrcv_dev->paired_dj_devices[dj_report->device_index]) { + /* The device is already known. No need to reallocate it. */ + dbg_hid("%s: device is already known\n", __func__); + return; + } + dj_hiddev = hid_allocate_device(); if (IS_ERR(dj_hiddev)) { dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n", @@ -250,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; @@ -305,6 +313,7 @@ static void delayedwork_callback(struct work_struct *work) struct dj_report dj_report; unsigned long flags; int count; + int retval; dbg_hid("%s\n", __func__); @@ -337,6 +346,25 @@ static void delayedwork_callback(struct work_struct *work) logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report); break; default: + /* A normal report (i. e. not belonging to a pair/unpair notification) + * arriving here, means that the report arrived but we did not have a + * paired dj_device associated to the report's device_index, this + * means that the original "device paired" notification corresponding + * to this dj_device never arrived to this driver. The reason is that + * hid-core discards all packets coming from a device while probe() is + * executing. */ + if (!djrcv_dev->paired_dj_devices[dj_report.device_index]) { + /* ok, we don't know the device, just re-ask the + * receiver for the list of connected devices. */ + retval = logi_dj_recv_query_paired_devices(djrcv_dev); + if (!retval) { + /* everything went fine, so just leave */ + break; + } + dev_err(&djrcv_dev->hdev->dev, + "%s:logi_dj_recv_query_paired_devices " + "error:%d\n", __func__, retval); + } dbg_hid("%s: unexpected report type\n", __func__); } } @@ -367,6 +395,12 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev, if (!djdev) { dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]" " is NULL, index %d\n", dj_report->device_index); + kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report)); + + if (schedule_work(&djrcv_dev->work) == 0) { + dbg_hid("%s: did not schedule the work item, was already " + "queued\n", __func__); + } return; } @@ -397,6 +431,12 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev, if (dj_device == NULL) { dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]" " is NULL, index %d\n", dj_report->device_index); + kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report)); + + if (schedule_work(&djrcv_dev->work) == 0) { + dbg_hid("%s: did not schedule the work item, was already " + "queued\n", __func__); + } return; } @@ -421,7 +461,7 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev, struct hid_report *report; struct hid_report_enum *output_report_enum; u8 *data = (u8 *)(&dj_report->device_index); - int i; + unsigned int i; output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; @@ -431,7 +471,7 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev, return -ENODEV; } - for (i = 0; i < report->field[0]->report_count; i++) + 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); @@ -444,6 +484,10 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev) struct dj_report *dj_report; int retval; + /* no need to protect djrcv_dev->querying_devices */ + if (djrcv_dev->querying_devices) + return 0; + dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL); if (!dj_report) return -ENOMEM; @@ -455,6 +499,7 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev) return retval; } + static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, unsigned timeout) { @@ -471,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; } @@ -487,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; } @@ -522,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) { @@ -560,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]); - - hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT); - - return 0; - -} - static int logi_dj_ll_start(struct hid_device *hid) { dbg_hid("%s\n", __func__); @@ -622,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, }; @@ -738,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); @@ -756,10 +792,10 @@ 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; } @@ -776,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: @@ -818,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-logitech-dj.h b/drivers/hid/hid-logitech-dj.h index fd28a5e0ca3..4a4000340ce 100644 --- a/drivers/hid/hid-logitech-dj.h +++ b/drivers/hid/hid-logitech-dj.h @@ -101,6 +101,7 @@ struct dj_receiver_dev { struct work_struct work; struct kfifo notif_fifo; spinlock_t lock; + bool querying_devices; }; struct dj_device { diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 5bc37343eb2..ecc2cbf300c 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -36,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; @@ -484,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; @@ -498,13 +498,13 @@ 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; } if (!msc->input) { @@ -538,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; @@ -548,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 }, @@ -574,7 +564,6 @@ 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, diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 551795b7da1..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,6 +258,8 @@ 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), diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index d39a5cede0b..51e25b9407f 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -68,6 +68,9 @@ MODULE_LICENSE("GPL"); #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; __s32 contactid; /* the device ContactID assigned to this slot */ @@ -84,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 { @@ -100,11 +104,11 @@ struct mt_device { int cc_value_index; /* contact count value index in the field */ unsigned last_slot_field; /* the last field of a slot */ unsigned mt_report_id; /* the report ID of the multitouch device */ - unsigned pen_report_id; /* the report ID of the pen device */ - __s8 inputmode; /* InputMode HID feature, -1 if non-existent */ - __s8 inputmode_index; /* InputMode HID feature index in the report */ - __s8 maxcontact_report_id; /* Maximum Contact Number HID feature, + __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; @@ -128,15 +132,17 @@ static void mt_post_parse(struct mt_device *td); #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 -#define MT_CLS_DUAL_CONTACT_NUMBER 0x0010 -#define MT_CLS_DUAL_CONTACT_ID 0x0011 +/* 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 @@ -188,23 +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_DUAL_CONTACT_NUMBER, + { .name = MT_CLS_WIN_8, .quirks = MT_QUIRK_ALWAYS_VALID | - MT_QUIRK_CONTACT_CNT_ACCURATE | - MT_QUIRK_SLOT_IS_CONTACTNUMBER, - .maxcontacts = 2 }, - { .name = MT_CLS_DUAL_CONTACT_ID, + 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 | - MT_QUIRK_SLOT_IS_CONTACTID, - .maxcontacts = 2 }, + MT_QUIRK_CONTACT_CNT_ACCURATE, + .export_all_inputs = true }, /* * vendor specific classes @@ -217,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, @@ -244,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, @@ -261,17 +258,6 @@ static struct mt_class mt_classes[] = { { } }; -static void mt_free_input_name(struct hid_input *hi) -{ - struct hid_device *hdev = hi->report->device; - const char *name = hi->input->name; - - if (name != hdev->name) { - hi->input->name = hdev->name; - kfree(name); - } -} - static ssize_t mt_show_quirks(struct device *dev, struct device_attribute *attr, char *buf) @@ -317,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; @@ -343,19 +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_CONTACT_CNT_ACCURATE; - *quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; - *quirks &= ~MT_QUIRK_VALID_IS_INRANGE; - *quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; - } - break; } } @@ -380,52 +351,6 @@ static void mt_store_field(struct hid_usage *usage, struct mt_device *td, f->usages[f->length++] = usage->hid; } -static int mt_pen_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); - - td->pen_report_id = field->report->id; - - return 0; -} - -static int mt_pen_input_mapped(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - return 0; -} - -static int mt_pen_event(struct hid_device *hid, struct hid_field *field, - struct hid_usage *usage, __s32 value) -{ - /* let hid-input handle it */ - return 0; -} - -static void mt_pen_report(struct hid_device *hid, struct hid_report *report) -{ - struct hid_field *field = report->field[0]; - - input_sync(field->hidinput->input); -} - -static void mt_pen_input_configured(struct hid_device *hdev, - struct hid_input *hi) -{ - char *name = kzalloc(strlen(hi->input->name) + 5, GFP_KERNEL); - if (name) { - sprintf(name, "%s Pen", hi->input->name); - mt_free_input_name(hi); - hi->input->name = name; - } - - /* force BTN_STYLUS to allow tablet matching in udev */ - __set_bit(BTN_STYLUS, hi->input->keybit); -} - 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) @@ -442,8 +367,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, * 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; + td->inputmode_value = MT_INPUTMODE_TOUCHPAD; + } if (usage->usage_index) prev_usage = &field->usage[usage->usage_index - 1]; @@ -536,6 +463,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, mt_store_field(usage, td, hi); return 1; case HID_DG_CONTACTCOUNT: + /* 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; @@ -799,28 +730,52 @@ 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) { - /* 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 && + 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 mt_pen_input_mapping(hdev, hi, field, usage, bit, max); + return 0; - return mt_touch_input_mapping(hdev, hi, field, usage, bit, max); + 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 mt_pen_input_mapped(hdev, hi, field, usage, bit, max); + return 0; - return mt_touch_input_mapped(hdev, hi, field, usage, bit, max); + 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, @@ -831,25 +786,22 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, if (field->report->id == td->mt_report_id) return mt_touch_event(hid, field, usage, value); - if (field->report->id == td->pen_report_id) - return mt_pen_event(hid, field, usage, value); - - /* ignore other reports */ - return 1; + 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) - mt_touch_report(hid, report); + return mt_touch_report(hid, report); - if (report->id == td->pen_report_id) - mt_pen_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) @@ -864,7 +816,7 @@ 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; + r->field[0]->value[td->inputmode_index] = td->inputmode_value; hid_hw_request(hdev, r, HID_REQ_SET_REPORT); } } @@ -928,16 +880,62 @@ static void mt_post_parse(struct mt_device *td) static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); - char *name = kstrdup(hdev->name, GFP_KERNEL); - - if (name) - hi->input->name = name; + char *name; + const char *suffix = NULL; + struct hid_field *field = hi->report->field[0]; if (hi->report->id == td->mt_report_id) mt_touch_input_configured(hdev, hi); - if (hi->report->id == td->pen_report_id) - mt_pen_input_configured(hdev, hi); + /* + * 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; + } + } + + 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) @@ -945,7 +943,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) int ret, i; struct mt_device *td; struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */ - struct hid_input *hi; for (i = 0; mt_classes[i].name ; i++) { if (id->driver_data == mt_classes[i].name) { @@ -967,7 +964,19 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) hdev->quirks |= HID_QUIRK_MULTI_INPUT; hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT; - td = kzalloc(sizeof(struct mt_device), GFP_KERNEL); + /* + * 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; @@ -975,16 +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; - td->pen_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) @@ -992,29 +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 hid_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; - -hid_fail: - list_for_each_entry(hi, &hdev->inputs, list) - mt_free_input_name(hi); -fail: - kfree(td->fields); - kfree(td); - return ret; } #ifdef CONFIG_PM @@ -1039,19 +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); - struct hid_input *hi; - sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); - list_for_each_entry(hi, &hdev->inputs, list) - mt_free_input_name(hi); - 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 */ @@ -1065,33 +1064,25 @@ 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_NSMU, - 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_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_CONTACT_NUMBER, - 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) }, @@ -1106,11 +1097,6 @@ static const struct hid_device_id mt_devices[] = { 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, @@ -1120,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, @@ -1162,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_CONTACT_ID, - 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, @@ -1189,6 +1177,21 @@ 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_NSMU, @@ -1205,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_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, @@ -1247,11 +1224,6 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) }, - /* Nexio panels */ - { .driver_data = MT_CLS_DEFAULT, - MT_USB_DEVICE(USB_VENDOR_ID_NEXIO, - USB_DEVICE_ID_NEXIO_MULTITOUCH_420)}, - /* Panasonic panels */ { .driver_data = MT_CLS_PANASONIC, MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC, @@ -1265,11 +1237,6 @@ static const struct hid_device_id mt_devices[] = { 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, @@ -1283,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_DEFAULT, - MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, - USB_DEVICE_ID_MTP_SITRONIX)}, /* TopSeed panels */ { .driver_data = MT_CLS_TOPSEED, @@ -1328,6 +1280,12 @@ static const struct hid_device_id mt_devices[] = { { .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_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_XAT, @@ -1362,13 +1320,13 @@ static const struct hid_device_id mt_devices[] = { 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); diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index ef95102515e..600f2075512 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -115,7 +115,8 @@ 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; hid_hw_request(hdev, report, HID_REQ_GET_REPORT); @@ -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; /* diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c index e346038f0f1..cf1a9f1c121 100644 --- a/drivers/hid/hid-picolcd_cir.c +++ b/drivers/hid/hid-picolcd_cir.c @@ -114,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; @@ -145,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 b48092d0e13..acbb021065e 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -290,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')) diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c index 59ab8e157e6..024cdf3c229 100644 --- a/drivers/hid/hid-picolcd_debugfs.c +++ b/drivers/hid/hid-picolcd_debugfs.c @@ -394,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 @@ -407,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 */ @@ -644,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 591f6b22aa9..7f965e23143 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c @@ -501,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 */ @@ -593,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-pl.c b/drivers/hid/hid-pl.c index d29112fa5cd..2dcd7d98dbd 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -132,8 +132,14 @@ static int plff_init(struct hid_device *hid) 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]; diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c index 7ed82805641..91fab975063 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c @@ -624,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; @@ -660,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); diff --git a/drivers/hid/hid-ps3remote.c b/drivers/hid/hid-ps3remote.c deleted file mode 100644 index f1239d3c5b1..00000000000 --- a/drivers/hid/hid-ps3remote.c +++ /dev/null @@ -1,204 +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, -}; -module_hid_driver(ps3remote_driver); - -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 8023751d525..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, @@ -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,52 +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_W(talkfx, TALKFX) -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_W(talkfx, TALKFX), - 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, @@ -427,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) diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 7fae070788f..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, @@ -891,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-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 index c79d0b06c14..07de2f9014c 100644 --- a/drivers/hid/hid-roccat-konepure.c +++ b/drivers/hid/hid-roccat-konepure.c @@ -15,6 +15,7 @@ * 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> @@ -23,136 +24,62 @@ #include <linux/hid-roccat.h> #include "hid-ids.h" #include "hid-roccat-common.h" -#include "hid-roccat-konepure.h" -static struct class *konepure_class; - -static ssize_t konepure_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 konepure_device *konepure = 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(&konepure->konepure_lock); - retval = roccat_common2_receive(usb_dev, command, buf, real_size); - mutex_unlock(&konepure->konepure_lock); - - return retval ? retval : real_size; -} - -static ssize_t konepure_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 konepure_device *konepure = 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(&konepure->konepure_lock); - retval = roccat_common2_send_with_status(usb_dev, command, - (void *)buf, real_size); - mutex_unlock(&konepure->konepure_lock); - - return retval ? retval : real_size; -} - -#define KONEPURE_SYSFS_W(thingy, THINGY) \ -static ssize_t konepure_sysfs_write_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ -{ \ - return konepure_sysfs_write(fp, kobj, buf, off, count, \ - KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \ -} - -#define KONEPURE_SYSFS_R(thingy, THINGY) \ -static ssize_t konepure_sysfs_read_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ -{ \ - return konepure_sysfs_read(fp, kobj, buf, off, count, \ - KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \ -} - -#define KONEPURE_SYSFS_RW(thingy, THINGY) \ -KONEPURE_SYSFS_W(thingy, THINGY) \ -KONEPURE_SYSFS_R(thingy, THINGY) - -#define KONEPURE_BIN_ATTRIBUTE_RW(thingy, THINGY) \ -{ \ - .attr = { .name = #thingy, .mode = 0660 }, \ - .size = KONEPURE_SIZE_ ## THINGY, \ - .read = konepure_sysfs_read_ ## thingy, \ - .write = konepure_sysfs_write_ ## thingy \ -} +enum { + KONEPURE_MOUSE_REPORT_NUMBER_BUTTON = 3, +}; -#define KONEPURE_BIN_ATTRIBUTE_R(thingy, THINGY) \ -{ \ - .attr = { .name = #thingy, .mode = 0440 }, \ - .size = KONEPURE_SIZE_ ## THINGY, \ - .read = konepure_sysfs_read_ ## thingy, \ -} +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; -#define KONEPURE_BIN_ATTRIBUTE_W(thingy, THINGY) \ -{ \ - .attr = { .name = #thingy, .mode = 0220 }, \ - .size = KONEPURE_SIZE_ ## THINGY, \ - .write = konepure_sysfs_write_ ## thingy \ -} +static struct class *konepure_class; -KONEPURE_SYSFS_RW(actual_profile, ACTUAL_PROFILE) -KONEPURE_SYSFS_W(control, CONTROL) -KONEPURE_SYSFS_RW(info, INFO) -KONEPURE_SYSFS_W(talk, TALK) -KONEPURE_SYSFS_W(macro, MACRO) -KONEPURE_SYSFS_RW(sensor, SENSOR) -KONEPURE_SYSFS_RW(tcu, TCU) -KONEPURE_SYSFS_R(tcu_image, TCU_IMAGE) -KONEPURE_SYSFS_RW(profile_settings, PROFILE_SETTINGS) -KONEPURE_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) - -static struct bin_attribute konepure_bin_attributes[] = { - KONEPURE_BIN_ATTRIBUTE_RW(actual_profile, ACTUAL_PROFILE), - KONEPURE_BIN_ATTRIBUTE_W(control, CONTROL), - KONEPURE_BIN_ATTRIBUTE_RW(info, INFO), - KONEPURE_BIN_ATTRIBUTE_W(talk, TALK), - KONEPURE_BIN_ATTRIBUTE_W(macro, MACRO), - KONEPURE_BIN_ATTRIBUTE_RW(sensor, SENSOR), - KONEPURE_BIN_ATTRIBUTE_RW(tcu, TCU), - KONEPURE_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE), - KONEPURE_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), - KONEPURE_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), - __ATTR_NULL +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 int konepure_init_konepure_device_struct(struct usb_device *usb_dev, - struct konepure_device *konepure) -{ - mutex_init(&konepure->konepure_lock); +static const struct attribute_group konepure_group = { + .bin_attrs = konepure_bin_attrs, +}; - return 0; -} +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 konepure_device *konepure; + struct roccat_common2_device *konepure; int retval; if (intf->cur_altsetting->desc.bInterfaceProtocol @@ -168,9 +95,9 @@ static int konepure_init_specials(struct hid_device *hdev) } hid_set_drvdata(hdev, konepure); - retval = konepure_init_konepure_device_struct(usb_dev, konepure); + retval = roccat_common2_device_init_struct(usb_dev, konepure); if (retval) { - hid_err(hdev, "couldn't init struct konepure_device\n"); + hid_err(hdev, "couldn't init KonePure device\n"); goto exit_free; } @@ -192,7 +119,7 @@ exit_free: static void konepure_remove_specials(struct hid_device *hdev) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - struct konepure_device *konepure; + struct roccat_common2_device *konepure; if (intf->cur_altsetting->desc.bInterfaceProtocol != USB_INTERFACE_PROTOCOL_MOUSE) @@ -245,7 +172,7 @@ 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 konepure_device *konepure = hid_get_drvdata(hdev); + struct roccat_common2_device *konepure = hid_get_drvdata(hdev); if (intf->cur_altsetting->desc.bInterfaceProtocol != USB_INTERFACE_PROTOCOL_MOUSE) @@ -262,6 +189,7 @@ static int konepure_raw_event(struct hid_device *hdev, 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) }, { } }; @@ -282,7 +210,7 @@ static int __init konepure_init(void) konepure_class = class_create(THIS_MODULE, "konepure"); if (IS_ERR(konepure_class)) return PTR_ERR(konepure_class); - konepure_class->dev_bin_attrs = konepure_bin_attributes; + konepure_class->dev_groups = konepure_groups; retval = hid_register_driver(&konepure_driver); if (retval) @@ -300,5 +228,5 @@ module_init(konepure_init); module_exit(konepure_exit); MODULE_AUTHOR("Stefan Achatz"); -MODULE_DESCRIPTION("USB Roccat KonePure driver"); +MODULE_DESCRIPTION("USB Roccat KonePure/Optical driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-konepure.h b/drivers/hid/hid-roccat-konepure.h deleted file mode 100644 index 2cd24e93dfd..00000000000 --- a/drivers/hid/hid-roccat-konepure.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef __HID_ROCCAT_KONEPURE_H -#define __HID_ROCCAT_KONEPURE_H - -/* - * 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. - */ - -#include <linux/types.h> - -enum { - KONEPURE_SIZE_ACTUAL_PROFILE = 0x03, - KONEPURE_SIZE_CONTROL = 0x03, - KONEPURE_SIZE_FIRMWARE_WRITE = 0x0402, - KONEPURE_SIZE_INFO = 0x06, - KONEPURE_SIZE_MACRO = 0x0822, - KONEPURE_SIZE_PROFILE_SETTINGS = 0x1f, - KONEPURE_SIZE_PROFILE_BUTTONS = 0x3b, - KONEPURE_SIZE_SENSOR = 0x06, - KONEPURE_SIZE_TALK = 0x10, - KONEPURE_SIZE_TCU = 0x04, - KONEPURE_SIZE_TCU_IMAGE = 0x0404, -}; - -enum konepure_control_requests { - KONEPURE_CONTROL_REQUEST_GENERAL = 0x80, - KONEPURE_CONTROL_REQUEST_BUTTONS = 0x90, -}; - -enum konepure_commands { - KONEPURE_COMMAND_CONTROL = 0x04, - KONEPURE_COMMAND_ACTUAL_PROFILE = 0x05, - KONEPURE_COMMAND_PROFILE_SETTINGS = 0x06, - KONEPURE_COMMAND_PROFILE_BUTTONS = 0x07, - KONEPURE_COMMAND_MACRO = 0x08, - KONEPURE_COMMAND_INFO = 0x09, - KONEPURE_COMMAND_TCU = 0x0c, - KONEPURE_COMMAND_TCU_IMAGE = 0x0c, - KONEPURE_COMMAND_E = 0x0e, - KONEPURE_COMMAND_SENSOR = 0x0f, - KONEPURE_COMMAND_TALK = 0x10, - KONEPURE_COMMAND_FIRMWARE_WRITE = 0x1b, - KONEPURE_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c, -}; - -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; - -struct konepure_device { - int roccat_claimed; - int chrdev_minor; - struct mutex konepure_lock; -}; - -#endif 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-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 b59b3df9ca9..65c4ccfcbd2 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -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); @@ -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 37961c7e397..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,7 +189,10 @@ 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); diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index ca749810732..e244e449cbb 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -26,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. @@ -54,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; @@ -64,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; }; /** @@ -76,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; }; @@ -94,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; } @@ -147,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; } @@ -174,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; } @@ -192,12 +209,12 @@ 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; } @@ -216,12 +233,13 @@ 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; } @@ -241,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; @@ -255,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; - } + hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); - spin_unlock_irqrestore(&data->lock, flags); wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5); switch (data->pending.raw_size) { case 1: @@ -286,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, @@ -293,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; @@ -302,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); @@ -357,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; } @@ -394,6 +417,7 @@ static int sensor_hub_reset_resume(struct hid_device *hdev) return 0; } #endif + /* * Handle raw report as sent by device */ @@ -408,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); @@ -416,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); } @@ -443,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) { @@ -473,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; + return ret; } - ret = hid_hw_open(hdev); - if (ret) { - hid_err(hdev, "failed to open input interrupt pipe\n"); - goto err_stop_hw; - } - 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), @@ -530,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; } @@ -593,16 +691,40 @@ 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(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) }, { } @@ -615,10 +737,11 @@ static struct hid_driver sensor_hub_driver = { .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 }; module_hid_driver(sensor_hub_driver); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 312098e4af4..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, @@ -55,10 +84,742 @@ static const u8 sixaxis_rdesc_fixup2[] = { 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) @@ -81,6 +842,21 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, 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)) && @@ -95,65 +871,173 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, *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; } /* @@ -163,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"); @@ -189,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) @@ -197,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; @@ -206,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[] = { @@ -257,18 +1975,52 @@ static const struct hid_device_id sony_devices[] = { .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 }; -module_hid_driver(sony_driver); + +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 a2f587d004e..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> */ /* @@ -46,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) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index d1649119211..29f328f411f 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -249,6 +249,11 @@ static int steelseries_srws1_probe(struct hid_device *hdev, 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"); diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c index 99342cfa0ea..134be89b15e 100644 --- a/drivers/hid/hid-thingm.c +++ b/drivers/hid/hid-thingm.c @@ -1,7 +1,7 @@ /* * ThingM blink(1) USB RGB LED driver * - * Copyright 2013 Savoir-faire Linux Inc. + * Copyright 2013-2014 Savoir-faire Linux Inc. * Vivien Didelot <vivien.didelot@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or @@ -10,244 +10,285 @@ */ #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 BLINK1_CMD_SIZE 9 +#define REPORT_ID 1 +#define REPORT_SIZE 9 -#define blink1_rgb_to_r(rgb) ((rgb & 0xFF0000) >> 16) -#define blink1_rgb_to_g(rgb) ((rgb & 0x00FF00) >> 8) -#define blink1_rgb_to_b(rgb) ((rgb & 0x0000FF) >> 0) +/* Firmware major number of supported devices */ +#define THINGM_MAJOR_MK1 '1' +#define THINGM_MAJOR_MK2 '2' -/** - * struct blink1_data - blink(1) device specific data - * @hdev: HID device. - * @led_cdev: LED class instance. - * @rgb: 8-bit per channel RGB notation. - * @fade: fade time in hundredths of a second. - * @brightness: brightness coefficient. - * @play: play/pause in-memory patterns. - */ -struct blink1_data { +struct 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 led_classdev led_cdev; - u32 rgb; - u16 fade; - u8 brightness; - bool play; + struct { + char major; + char minor; + } version; + const struct thingm_fwinfo *fwinfo; + struct mutex lock; + struct thingm_rgb *rgb; }; -static int blink1_send_command(struct blink1_data *data, - u8 buf[BLINK1_CMD_SIZE]) +static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE]) { int ret; - hid_dbg(data->hdev, "command: %d%c%.2x%.2x%.2x%.2x%.2x%.2x%.2x\n", + 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 = data->hdev->hid_output_raw_report(data->hdev, buf, - BLINK1_CMD_SIZE, HID_FEATURE_REPORT); + 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 blink1_update_color(struct blink1_data *data) +static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE]) { - u8 buf[BLINK1_CMD_SIZE] = { 1, 'n', 0, 0, 0, 0, 0, 0, 0 }; - - if (data->brightness) { - unsigned int coef = DIV_ROUND_CLOSEST(255, data->brightness); + int ret; - buf[2] = DIV_ROUND_CLOSEST(blink1_rgb_to_r(data->rgb), coef); - buf[3] = DIV_ROUND_CLOSEST(blink1_rgb_to_g(data->rgb), coef); - buf[4] = DIV_ROUND_CLOSEST(blink1_rgb_to_b(data->rgb), coef); - } + ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret < 0) + return ret; - if (data->fade) { - buf[1] = 'c'; - buf[5] = (data->fade & 0xFF00) >> 8; - buf[6] = (data->fade & 0x00FF); - } + 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 blink1_send_command(data, buf); + return 0; } -static void blink1_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) +static int thingm_version(struct thingm_device *tdev) { - struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent); + u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 }; + int err; - data->brightness = brightness; - if (blink1_update_color(data)) - hid_err(data->hdev, "failed to update color\n"); -} + err = thingm_send(tdev, buf); + if (err) + return err; -static enum led_brightness blink1_led_get(struct led_classdev *led_cdev) -{ - struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent); + err = thingm_recv(tdev, buf); + if (err) + return err; - return data->brightness; + tdev->version.major = buf[3]; + tdev->version.minor = buf[4]; + + return 0; } -static ssize_t blink1_show_rgb(struct device *dev, - struct device_attribute *attr, char *buf) +static int thingm_write_color(struct thingm_rgb *rgb) { - struct blink1_data *data = dev_get_drvdata(dev->parent); + u8 buf[REPORT_SIZE] = { REPORT_ID, 'c', 0, 0, 0, 0, 0, rgb->num, 0 }; - return sprintf(buf, "%.6X\n", data->rgb); + 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 ssize_t blink1_store_rgb(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static void thingm_work(struct work_struct *work) { - struct blink1_data *data = dev_get_drvdata(dev->parent); - long unsigned int rgb; - int ret; + struct thingm_rgb *rgb = container_of(work, struct thingm_rgb, work); - ret = kstrtoul(buf, 16, &rgb); - if (ret) - return ret; - - /* RGB triplet notation is 24-bit hexadecimal */ - if (rgb > 0xFFFFFF) - return -EINVAL; + mutex_lock(&rgb->tdev->lock); - data->rgb = rgb; - ret = blink1_update_color(data); + if (thingm_write_color(rgb)) + hid_err(rgb->tdev->hdev, "failed to write color\n"); - return ret ? ret : count; + mutex_unlock(&rgb->tdev->lock); } -static DEVICE_ATTR(rgb, S_IRUGO | S_IWUSR, blink1_show_rgb, blink1_store_rgb); - -static ssize_t blink1_show_fade(struct device *dev, - struct device_attribute *attr, char *buf) +static void thingm_led_set(struct led_classdev *ldev, + enum led_brightness brightness) { - struct blink1_data *data = dev_get_drvdata(dev->parent); + struct thingm_led *led = container_of(ldev, struct thingm_led, ldev); - return sprintf(buf, "%d\n", data->fade * 10); + /* the ledclass has already stored the brightness value */ + schedule_work(&led->rgb->work); } -static ssize_t blink1_store_fade(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static int thingm_init_rgb(struct thingm_rgb *rgb) { - struct blink1_data *data = dev_get_drvdata(dev->parent); - long unsigned int fade; - int ret; + 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); - ret = kstrtoul(buf, 10, &fade); - if (ret) - return ret; + return 0; - /* blink(1) accepts 16-bit fade time, number of 10ms ticks */ - fade = DIV_ROUND_CLOSEST(fade, 10); - if (fade > 65535) - return -EINVAL; +unregister_green: + led_classdev_unregister(&rgb->green.ldev); - data->fade = fade; +unregister_red: + led_classdev_unregister(&rgb->red.ldev); - return count; + return err; } -static DEVICE_ATTR(fade, S_IRUGO | S_IWUSR, - blink1_show_fade, blink1_store_fade); - -static ssize_t blink1_show_play(struct device *dev, - struct device_attribute *attr, char *buf) +static void thingm_remove_rgb(struct thingm_rgb *rgb) { - struct blink1_data *data = dev_get_drvdata(dev->parent); - - return sprintf(buf, "%d\n", data->play); + flush_work(&rgb->work); + led_classdev_unregister(&rgb->red.ldev); + led_classdev_unregister(&rgb->green.ldev); + led_classdev_unregister(&rgb->blue.ldev); } -static ssize_t blink1_store_play(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id) { - struct blink1_data *data = dev_get_drvdata(dev->parent); - u8 cmd[BLINK1_CMD_SIZE] = { 1, 'p', 0, 0, 0, 0, 0, 0, 0 }; - long unsigned int play; - int ret; + struct thingm_device *tdev; + int i, err; - ret = kstrtoul(buf, 10, &play); - if (ret) - return ret; + tdev = devm_kzalloc(&hdev->dev, sizeof(struct thingm_device), + GFP_KERNEL); + if (!tdev) + return -ENOMEM; - data->play = !!play; - cmd[2] = data->play; - ret = blink1_send_command(data, cmd); + tdev->hdev = hdev; + hid_set_drvdata(hdev, tdev); - return ret ? ret : count; -} - -static DEVICE_ATTR(play, S_IRUGO | S_IWUSR, - blink1_show_play, blink1_store_play); + err = hid_parse(hdev); + if (err) + goto error; -static const struct attribute_group blink1_sysfs_group = { - .attrs = (struct attribute *[]) { - &dev_attr_rgb.attr, - &dev_attr_fade.attr, - &dev_attr_play.attr, - NULL - }, -}; + err = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (err) + goto error; -static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - struct blink1_data *data; - struct led_classdev *led; - char led_name[13]; - int ret; + mutex_init(&tdev->lock); - data = devm_kzalloc(&hdev->dev, sizeof(struct blink1_data), GFP_KERNEL); - if (!data) - return -ENOMEM; + err = thingm_version(tdev); + if (err) + goto stop; - hid_set_drvdata(hdev, data); - data->hdev = hdev; - data->rgb = 0xFFFFFF; /* set a default white color */ + hid_dbg(hdev, "firmware version: %c.%c\n", + tdev->version.major, tdev->version.minor); - ret = hid_parse(hdev); - if (ret) - goto error; + for (i = 0; i < ARRAY_SIZE(thingm_fwinfo) && !tdev->fwinfo; ++i) + if (thingm_fwinfo[i].major == tdev->version.major) + tdev->fwinfo = &thingm_fwinfo[i]; - ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); - if (ret) - goto error; + if (!tdev->fwinfo) { + hid_err(hdev, "unsupported firmware %c\n", tdev->version.major); + goto stop; + } - /* blink(1) serial numbers range is 0x1A001000 to 0x1A002FFF */ - led = &data->led_cdev; - snprintf(led_name, sizeof(led_name), "blink1::%s", hdev->uniq + 4); - led->name = led_name; - led->brightness_set = blink1_led_set; - led->brightness_get = blink1_led_get; - ret = led_classdev_register(&hdev->dev, led); - if (ret) + tdev->rgb = devm_kzalloc(&hdev->dev, + sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb, + GFP_KERNEL); + if (!tdev->rgb) { + err = -ENOMEM; goto stop; + } - ret = sysfs_create_group(&led->dev->kobj, &blink1_sysfs_group); - if (ret) - goto remove_led; + 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; - -remove_led: - led_classdev_unregister(led); stop: hid_hw_stop(hdev); error: - return ret; + return err; } static void thingm_remove(struct hid_device *hdev) { - struct blink1_data *data = hid_get_drvdata(hdev); - struct led_classdev *led = &data->led_cdev; + struct thingm_device *tdev = hid_get_drvdata(hdev); + int i; + + for (i = 0; i < tdev->fwinfo->numrgb; ++i) + thingm_remove_rgb(tdev->rgb + i); - sysfs_remove_group(&led->dev->kobj, &blink1_sysfs_group); - led_classdev_unregister(led); hid_hw_stop(hdev); } diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index a4a8bb0da68..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; diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index e5ee1f20bbd..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,399 +410,993 @@ 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) { - __u16 x, y, z; + if (!(wdata->state.flags & WIIPROTO_FLAG_EXITING)) + schedule_work(&wdata->init_worker); +} - if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL)) - return; +static void wiimote_schedule(struct wiimote_data *wdata) +{ + unsigned long 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. - */ + spin_lock_irqsave(&wdata->state.lock, flags); + __wiimote_schedule(wdata); + spin_unlock_irqrestore(&wdata->state.lock, flags); +} + +static void wiimote_init_timeout(unsigned long arg) +{ + struct wiimote_data *wdata = (void*)arg; + + wiimote_schedule(wdata); +} + +/* protocol handlers */ - x = payload[2] << 2; - y = payload[3] << 2; - z = payload[4] << 2; +static void handler_keys(struct wiimote_data *wdata, const __u8 *payload) +{ + const __u8 *iter, *mods; + const struct wiimod_ops *ops; - x |= (payload[0] >> 5) & 0x3; - y |= (payload[1] >> 4) & 0x2; - z |= (payload[1] >> 5) & 0x2; + ops = wiimod_ext_table[wdata->state.exttype]; + if (ops->in_keys) { + ops->in_keys(wdata, payload); + return; + } - 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); + 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; -static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir, - bool packed, __u8 xid, __u8 yid) + ops = wiimod_ext_table[wdata->state.exttype]; + if (ops->in_accel) { + ops->in_accel(wdata, payload); + return; + } + + 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; + } + + /* 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; + } + } +} + +#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; } - input_report_abs(wdata->ir, xid, x); - input_report_abs(wdata->ir, yid, y); + 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 */ @@ -804,12 +1414,27 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload) { handler_status_K(wdata, payload); - wiiext_event(wdata, payload[2] & 0x02); + /* 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 */ @@ -864,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) @@ -875,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) @@ -891,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) @@ -910,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) @@ -929,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) @@ -950,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 { @@ -1017,177 +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); - kfree(wdata->battery.name); - 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); @@ -1219,62 +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); - if (ret) { - hid_err(hdev, "Cannot register input device\n"); - goto err_ir; - } - - ret = input_register_device(wdata->input); + ret = device_create_file(&hdev->dev, &dev_attr_extension); if (ret) { - hid_err(hdev, "Cannot register input device\n"); - goto err_input; - } - - 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.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) { - ret = -ENOMEM; - goto err_battery_name; + hid_err(hdev, "cannot create sysfs attribute\n"); + goto err_close; } - ret = power_supply_register(&wdata->hdev->dev, &wdata->battery); + ret = device_create_file(&hdev->dev, &dev_attr_devtype); if (ret) { - hid_err(hdev, "Cannot register battery device\n"); - goto err_battery; + hid_err(hdev, "cannot create sysfs attribute\n"); + goto err_ext; } - 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; @@ -1282,23 +1831,15 @@ err_free: wiimote_destroy(wdata); return ret; -err_battery: - kfree(wdata->battery.name); -err_battery_name: - 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; } @@ -1331,4 +1872,4 @@ module_hid_driver(wiimote_hid_driver); 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 90124ffaa2a..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> */ /* @@ -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 0472191d4a7..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 6ec28a37c14..a29756c6ca0 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -68,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); diff --git a/drivers/hid/hid-zydacron.c b/drivers/hid/hid-zydacron.c index e4cddeccd6b..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,7 +205,6 @@ 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); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index a7451632ceb..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,8 +104,11 @@ 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_inode(file)); @@ -113,17 +116,13 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, __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,12 +180,15 @@ 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_inode(file)); @@ -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,39 +323,48 @@ 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, @@ -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); diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 2b1799a3b21..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> @@ -35,6 +36,7 @@ #include <linux/hid.h> #include <linux/mutex.h> #include <linux/acpi.h> +#include <linux/of.h> #include <linux/i2c/i2c-hid.h> @@ -108,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. @@ -254,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 */; @@ -273,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; @@ -289,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"); @@ -439,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); } @@ -539,7 +572,7 @@ 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]; @@ -553,9 +586,9 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, count--; } - ret = i2c_hid_set_report(client, + 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 */ @@ -563,34 +596,27 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, return ret; } -static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep, - int reqtype) +static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf, + size_t count) { - struct i2c_client *client = hid->driver_data; - char *buf; - int ret; - int len = i2c_hid_get_report_length(rep) - 2; - - buf = kzalloc(len, GFP_KERNEL); - if (!buf) - return; + 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: - ret = i2c_hid_get_raw_report(hid, rep->id, buf, len, rep->type); - if (ret < 0) - dev_err(&client->dev, "%s: unable to get report: %d\n", - __func__, ret); - else - hid_input_report(hid, rep->type, buf, ret, 0); - break; + return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype); case HID_REQ_SET_REPORT: - hid_output_report(rep, buf); - i2c_hid_output_raw_report(hid, buf, len, rep->type); - break; + if (buf[0] != reportnum) + return -EINVAL; + return i2c_hid_output_raw_report(hid, buf, len, rtype, true); + default: + return -EIO; } - - kfree(buf); } static int i2c_hid_parse(struct hid_device *hid) @@ -692,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; } @@ -701,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) @@ -718,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); } @@ -727,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 = { @@ -772,8 +774,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = { .open = i2c_hid_open, .close = i2c_hid_close, .power = i2c_hid_power, - .request = i2c_hid_request, - .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) @@ -805,34 +807,18 @@ 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: - * 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); - + /* 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, - "unable to fetch the size of HID descriptor (ret=%d)\n", - ret); - return -ENODEV; - } - - 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)) { - dev_err(&client->dev, "weird size of HID descriptor (%u)\n", - dsize); + 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) */ /* check bcdVersion == 1.0 */ if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) { dev_err(&client->dev, @@ -841,17 +827,14 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) return -ENODEV; } - i2c_hid_dbg(ihid, "Fetching the HID descriptor\n"); - - ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, - dsize); - if (ret) { - dev_err(&client->dev, "hid_descr_cmd Fail\n"); + /* Descriptor length should be 30 bytes as per the specification */ + dsize = le16_to_cpu(hdesc->wHIDDescLength); + 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; } @@ -863,9 +846,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client, 0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45, 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE, }; - struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object params[4], *obj; - struct acpi_object_list input; + union acpi_object *obj; struct acpi_device *adev; acpi_handle handle; @@ -873,35 +854,16 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client, if (!handle || acpi_bus_get_device(handle, &adev)) return -ENODEV; - input.count = ARRAY_SIZE(params); - input.pointer = params; - - params[0].type = ACPI_TYPE_BUFFER; - params[0].buffer.length = sizeof(i2c_hid_guid); - params[0].buffer.pointer = i2c_hid_guid; - params[1].type = ACPI_TYPE_INTEGER; - params[1].integer.value = 1; - params[2].type = ACPI_TYPE_INTEGER; - params[2].integer.value = 1; /* HID function */ - params[3].type = ACPI_TYPE_INTEGER; - params[3].integer.value = 0; - - if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DSM", &input, &buf))) { + 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; } - obj = (union acpi_object *)buf.pointer; - if (obj->type != ACPI_TYPE_INTEGER) { - dev_err(&client->dev, "device _DSM returned invalid type: %d\n", - obj->type); - kfree(buf.pointer); - return -EINVAL; - } - pdata->hid_descriptor_address = obj->integer.value; + ACPI_FREE(obj); - kfree(buf.pointer); return 0; } @@ -919,6 +881,42 @@ static inline int i2c_hid_acpi_pdata(struct i2c_client *client, } #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 register address not provided\n"); + return -ENODEV; + } + 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) { @@ -940,7 +938,11 @@ static int i2c_hid_probe(struct i2c_client *client, if (!ihid) return -ENOMEM; - if (!platform_data) { + 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, @@ -967,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)) { @@ -985,10 +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_HANDLE_SET(&hid->dev, ACPI_HANDLE(&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); @@ -1004,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: @@ -1012,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); @@ -1023,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); @@ -1041,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); @@ -1055,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; @@ -1066,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 }, @@ -1081,6 +1121,7 @@ static struct i2c_driver i2c_hid_driver = { .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 fc307e0422a..0cb92e34725 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -116,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; @@ -268,13 +244,35 @@ 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 @@ -312,7 +310,7 @@ static int uhid_event_from_user(const char __user *buffer, size_t len, */ struct uhid_create_req_compat *compat; - compat = kmalloc(sizeof(*compat), GFP_KERNEL); + compat = kzalloc(sizeof(*compat), GFP_KERNEL); if (!compat) return -ENOMEM; @@ -402,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; @@ -432,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) @@ -460,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) { @@ -596,12 +663,18 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, 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; @@ -640,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, }; @@ -659,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 99418285222..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; @@ -649,62 +648,6 @@ static void usbhid_submit_report(struct hid_device *hid, struct hid_report *repo spin_unlock_irqrestore(&usbhid->lock, flags); } -/* 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; -} - static int usbhid_wait_io(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; @@ -807,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; @@ -857,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; @@ -867,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 @@ -937,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++; } @@ -1253,6 +1214,20 @@ static void usbhid_request(struct hid_device *hid, struct hid_report *rep, int r } } +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) { @@ -1274,9 +1249,10 @@ 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, }; @@ -1307,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; @@ -1368,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) @@ -1402,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) @@ -1522,15 +1493,17 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) struct usbhid_device *usbhid = hid->driver_data; 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); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 19b8360f233..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 }, @@ -74,7 +75,10 @@ static const struct hid_blacklist { { 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 }, @@ -84,6 +88,10 @@ static const struct hid_blacklist { { 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 }, @@ -109,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/usbhid.h b/drivers/hid/usbhid/usbhid.h index dbb6af69913..f633c24ce28 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -92,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]); } } |
