mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-17 23:25:46 +08:00
Merge branches 'hidraw', 'magicmouse', 'multitouch', 'roccat', 'suspend-fixes' and 'upstream' into for-linus
This commit is contained in:
commit
929bd380b7
38
Documentation/ABI/testing/sysfs-driver-hid-lenovo-tpkbd
Normal file
38
Documentation/ABI/testing/sysfs-driver-hid-lenovo-tpkbd
Normal file
@ -0,0 +1,38 @@
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/press_to_select
|
||||
Date: July 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This controls if mouse clicks should be generated if the trackpoint is quickly pressed. How fast this press has to be
|
||||
is being controlled by press_speed.
|
||||
Values are 0 or 1.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/dragging
|
||||
Date: July 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: If this setting is enabled, it is possible to do dragging by pressing the trackpoint. This requires press_to_select to be enabled.
|
||||
Values are 0 or 1.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/release_to_select
|
||||
Date: July 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: For details regarding this setting please refer to http://www.pc.ibm.com/ww/healthycomputing/trkpntb.html
|
||||
Values are 0 or 1.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/select_right
|
||||
Date: July 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This setting controls if the mouse click events generated by pressing the trackpoint (if press_to_select is enabled) generate
|
||||
a left or right mouse button click.
|
||||
Values are 0 or 1.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/sensitivity
|
||||
Date: July 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This file contains the trackpoint sensitivity.
|
||||
Values are decimal integers from 1 (lowest sensitivity) to 255 (highest sensitivity).
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/press_speed
|
||||
Date: July 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description: This setting controls how fast the trackpoint needs to be pressed to generate a mouse click if press_to_select is enabled.
|
||||
Values are decimal integers from 1 (slowest) to 255 (fastest).
|
||||
|
77
Documentation/ABI/testing/sysfs-driver-hid-roccat-savu
Normal file
77
Documentation/ABI/testing/sysfs-driver-hid-roccat-savu
Normal file
@ -0,0 +1,77 @@
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/buttons
|
||||
Date: Mai 2012
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The mouse can store 5 profiles which can be switched by the
|
||||
press of a button. A profile is split into general settings and
|
||||
button settings. buttons holds informations about button layout.
|
||||
When written, this file lets one write the respective profile
|
||||
buttons to the mouse. The data has to be 47 bytes long.
|
||||
The mouse will reject invalid data.
|
||||
Which profile to write is determined by the profile number
|
||||
contained in the data.
|
||||
Before reading this file, control has to be written to select
|
||||
which profile to read.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/control
|
||||
Date: Mai 2012
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one select which data from which
|
||||
profile will be read next. The data has to be 3 bytes long.
|
||||
This file is writeonly.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/general
|
||||
Date: Mai 2012
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The mouse can store 5 profiles which can be switched by the
|
||||
press of a button. A profile is split into general settings and
|
||||
button settings. profile holds informations like resolution, sensitivity
|
||||
and light effects.
|
||||
When written, this file lets one write the respective profile
|
||||
settings back to the mouse. The data has to be 43 bytes long.
|
||||
The mouse will reject invalid data.
|
||||
Which profile to write is determined by the profile number
|
||||
contained in the data.
|
||||
This file is writeonly.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/info
|
||||
Date: Mai 2012
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When read, this file returns general data like firmware version.
|
||||
The data is 8 bytes long.
|
||||
This file is readonly.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/macro
|
||||
Date: Mai 2012
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one store macros with max 500
|
||||
keystrokes for a specific button for a specific profile.
|
||||
Button and profile numbers are included in written data.
|
||||
The data has to be 2083 bytes long.
|
||||
Before reading this file, control has to be written to select
|
||||
which profile and key to read.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/profile
|
||||
Date: Mai 2012
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The mouse can store 5 profiles which can be switched by the
|
||||
press of a button. profile holds number of actual profile.
|
||||
This value is persistent, so its value determines the profile
|
||||
that's active when the mouse is powered on next time.
|
||||
When written, the mouse activates the set profile immediately.
|
||||
The data has to be 3 bytes long.
|
||||
The mouse will reject invalid data.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/sensor
|
||||
Date: July 2012
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The mouse has a Avago ADNS-3090 sensor.
|
||||
This file allows reading and writing of the mouse sensors registers.
|
||||
The data has to be 4 bytes long.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
@ -193,10 +193,12 @@ config HID_EZKEY
|
||||
Support for Ezkey BTC 8193 keyboard.
|
||||
|
||||
config HID_HOLTEK
|
||||
tristate "Holtek On Line Grip based game controller support"
|
||||
tristate "Holtek HID devices"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Say Y here if you have a Holtek On Line Grip based game controller.
|
||||
Support for Holtek based devices:
|
||||
- Holtek On Line Grip based game controller
|
||||
- Trust GXT 18 Gaming Keyboard
|
||||
|
||||
config HOLTEK_FF
|
||||
bool "Holtek On Line Grip force feedback support"
|
||||
@ -261,6 +263,19 @@ config HID_LCPOWER
|
||||
---help---
|
||||
Support for LC-Power RC1000MCE RF remote control.
|
||||
|
||||
config HID_LENOVO_TPKBD
|
||||
tristate "Lenovo ThinkPad USB Keyboard with TrackPoint"
|
||||
depends on USB_HID
|
||||
select NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
---help---
|
||||
Support for the Lenovo ThinkPad USB Keyboard with TrackPoint.
|
||||
|
||||
Say Y here if you have a Lenovo ThinkPad USB Keyboard with TrackPoint
|
||||
and would like to use device-specific features like changing the
|
||||
sensitivity of the trackpoint, using the microphone mute button or
|
||||
controlling the mute and microphone mute LEDs.
|
||||
|
||||
config HID_LOGITECH
|
||||
tristate "Logitech devices" if EXPERT
|
||||
depends on USB_HID
|
||||
|
@ -48,12 +48,14 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
|
||||
obj-$(CONFIG_HID_ELECOM) += hid-elecom.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-holtekff.o
|
||||
obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o
|
||||
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
|
||||
obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
|
||||
obj-$(CONFIG_HID_KYE) += hid-kye.o
|
||||
obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
|
||||
obj-$(CONFIG_HID_LENOVO_TPKBD) += hid-lenovo-tpkbd.o
|
||||
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
|
||||
obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
|
||||
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
|
||||
@ -69,7 +71,8 @@ obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o
|
||||
obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
|
||||
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
|
||||
hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
|
||||
hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o
|
||||
hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \
|
||||
hid-roccat-savu.o
|
||||
obj-$(CONFIG_HID_SAITEK) += hid-saitek.o
|
||||
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
|
||||
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
|
||||
|
@ -60,6 +60,7 @@ static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
static const struct hid_device_id ch_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, ch_devices);
|
||||
|
@ -1379,8 +1379,10 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
|
||||
if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev))
|
||||
hdev->claimed |= HID_CLAIMED_HIDRAW;
|
||||
|
||||
if (!hdev->claimed) {
|
||||
hid_err(hdev, "claimed by neither input, hiddev nor hidraw\n");
|
||||
/* Drivers with the ->raw_event callback set are not required to connect
|
||||
* to any other listener. */
|
||||
if (!hdev->claimed && !hdev->driver->raw_event) {
|
||||
hid_err(hdev, "device has no listeners, quitting\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -1527,10 +1529,12 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS) },
|
||||
{ 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_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) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_4) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
|
||||
@ -1545,6 +1549,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
|
||||
{ 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_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
|
||||
@ -1553,6 +1558,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
|
||||
@ -1626,6 +1632,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
|
||||
{ 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_SAVU) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
|
||||
{ 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) },
|
||||
|
@ -129,6 +129,8 @@ static const struct hid_device_id cp_devices[] = {
|
||||
.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3),
|
||||
.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_4),
|
||||
.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE),
|
||||
.driver_data = CP_2WHEEL_MOUSE_HACK },
|
||||
{ }
|
||||
|
183
drivers/hid/hid-holtek-kbd.c
Normal file
183
drivers/hid/hid-holtek-kbd.c
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* HID driver for Holtek keyboard
|
||||
* Copyright (c) 2012 Tom Harwood
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 "hid-ids.h"
|
||||
#include "usbhid/usbhid.h"
|
||||
|
||||
/* Holtek based keyboards (USB ID 04d9:a055) have the following issues:
|
||||
* - The report descriptor 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.
|
||||
* - The report descriptor reports on caps/scroll/num lock key presses, but
|
||||
* doesn't have an LED output usage block.
|
||||
*
|
||||
* The replacement descriptor below fixes the number of consumer usages,
|
||||
* and provides an LED output usage block. LED output events are redirected
|
||||
* to the boot interface.
|
||||
*/
|
||||
|
||||
static __u8 holtek_kbd_rdesc_fixed[] = {
|
||||
/* Original report descriptor, with reduced number of consumer usages */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x80, /* Usage (Sys Control), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x01, /* Report ID (1), */
|
||||
0x19, 0x81, /* Usage Minimum (Sys Power Down), */
|
||||
0x29, 0x83, /* Usage Maximum (Sys Wake Up), */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x75, 0x05, /* Report Size (5), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x0C, /* Usage Page (Consumer), */
|
||||
0x09, 0x01, /* Usage (Consumer Control), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x02, /* Report ID (2), */
|
||||
0x19, 0x00, /* Usage Minimum (00h), */
|
||||
0x2A, 0xFF, 0x2F, /* Usage Maximum (0x2FFF), previously 0x7FFF */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x26, 0xFF, 0x2F, /* Logical Maximum (0x2FFF),previously 0x7FFF*/
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x81, 0x00, /* Input, */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x06, /* Usage (Keyboard), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x03, /* Report ID (3), */
|
||||
0x95, 0x38, /* Report Count (56), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x05, 0x07, /* Usage Page (Keyboard), */
|
||||
0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */
|
||||
0x29, 0xE7, /* Usage Maximum (KB Right GUI), */
|
||||
0x19, 0x00, /* Usage Minimum (None), */
|
||||
0x29, 0x2F, /* Usage Maximum (KB Lboxbracket And Lbrace),*/
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x06, /* Usage (Keyboard), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x04, /* Report ID (4), */
|
||||
0x95, 0x38, /* Report Count (56), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x05, 0x07, /* Usage Page (Keyboard), */
|
||||
0x19, 0x30, /* Usage Minimum (KB Rboxbracket And Rbrace),*/
|
||||
0x29, 0x67, /* Usage Maximum (KP Equals), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection */
|
||||
|
||||
/* LED usage for the boot protocol interface */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x06, /* Usage (Keyboard), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x05, 0x08, /* Usage Page (LED), */
|
||||
0x19, 0x01, /* Usage Minimum (01h), */
|
||||
0x29, 0x03, /* Usage Maximum (03h), */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x91, 0x02, /* Output (Variable), */
|
||||
0x95, 0x05, /* Report Count (5), */
|
||||
0x91, 0x01, /* Output (Constant), */
|
||||
0xC0, /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 *holtek_kbd_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) {
|
||||
rdesc = holtek_kbd_rdesc_fixed;
|
||||
*rsize = sizeof(holtek_kbd_rdesc_fixed);
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static int holtek_kbd_input_event(struct input_dev *dev, unsigned int type,
|
||||
unsigned int code,
|
||||
int value)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
struct usb_device *usb_dev = hid_to_usb_dev(hid);
|
||||
|
||||
/* Locate the boot interface, to receive the LED change events */
|
||||
struct usb_interface *boot_interface = usb_ifnum_to_if(usb_dev, 0);
|
||||
|
||||
struct hid_device *boot_hid = usb_get_intfdata(boot_interface);
|
||||
struct hid_input *boot_hid_input = list_first_entry(&boot_hid->inputs,
|
||||
struct hid_input, list);
|
||||
|
||||
return boot_hid_input->input->event(boot_hid_input->input, type, code,
|
||||
value);
|
||||
}
|
||||
|
||||
static int holtek_kbd_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
int ret = hid_parse(hdev);
|
||||
|
||||
if (!ret)
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
|
||||
if (!ret && intf->cur_altsetting->desc.bInterfaceNumber == 1) {
|
||||
struct hid_input *hidinput;
|
||||
list_for_each_entry(hidinput, &hdev->inputs, list) {
|
||||
hidinput->input->event = holtek_kbd_input_event;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct hid_device_id holtek_kbd_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
|
||||
USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, holtek_kbd_devices);
|
||||
|
||||
static struct hid_driver holtek_kbd_driver = {
|
||||
.name = "holtek_kbd",
|
||||
.id_table = holtek_kbd_devices,
|
||||
.report_fixup = holtek_kbd_report_fixup,
|
||||
.probe = holtek_kbd_probe
|
||||
};
|
||||
|
||||
static int __init holtek_kbd_init(void)
|
||||
{
|
||||
return hid_register_driver(&holtek_kbd_driver);
|
||||
}
|
||||
|
||||
static void __exit holtek_kbd_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&holtek_kbd_driver);
|
||||
}
|
||||
|
||||
module_exit(holtek_kbd_exit);
|
||||
module_init(holtek_kbd_init);
|
||||
MODULE_LICENSE("GPL");
|
@ -208,6 +208,7 @@
|
||||
#define USB_DEVICE_ID_CHICONY_MULTI_TOUCH 0xb19d
|
||||
#define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618
|
||||
#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123
|
||||
#define USB_DEVICE_ID_CHICONY_AK1D 0x1125
|
||||
|
||||
#define USB_VENDOR_ID_CHUNGHWAT 0x2247
|
||||
#define USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH 0x0001
|
||||
@ -237,6 +238,7 @@
|
||||
#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61
|
||||
#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64
|
||||
#define USB_DEVICE_ID_CYPRESS_BARCODE_3 0xbca1
|
||||
#define USB_DEVICE_ID_CYPRESS_BARCODE_4 0xed81
|
||||
#define USB_DEVICE_ID_CYPRESS_TRUETOUCH 0xc001
|
||||
|
||||
#define USB_VENDOR_ID_DEALEXTREAME 0x10c5
|
||||
@ -410,6 +412,9 @@
|
||||
#define USB_VENDOR_ID_HOLTEK 0x1241
|
||||
#define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP 0x5015
|
||||
|
||||
#define USB_VENDOR_ID_HOLTEK_ALT 0x04d9
|
||||
#define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD 0xa055
|
||||
|
||||
#define USB_VENDOR_ID_IMATION 0x0718
|
||||
#define USB_DEVICE_ID_DISC_STAKKA 0xd000
|
||||
|
||||
@ -479,6 +484,9 @@
|
||||
#define USB_DEVICE_ID_LD_HYBRID 0x2090
|
||||
#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0
|
||||
|
||||
#define USB_VENDOR_ID_LENOVO 0x17ef
|
||||
#define USB_DEVICE_ID_LENOVO_TPKBD 0x6009
|
||||
|
||||
#define USB_VENDOR_ID_LG 0x1fd2
|
||||
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
|
||||
|
||||
@ -573,6 +581,9 @@
|
||||
#define USB_VENDOR_ID_NINTENDO 0x057e
|
||||
#define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306
|
||||
|
||||
#define USB_VENDOR_ID_NOVATEK 0x0603
|
||||
#define USB_DEVICE_ID_NOVATEK_PCT 0x0600
|
||||
|
||||
#define USB_VENDOR_ID_NTRIG 0x1b96
|
||||
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
|
||||
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1 0x0003
|
||||
@ -650,6 +661,7 @@
|
||||
#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50
|
||||
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24
|
||||
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6
|
||||
#define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a
|
||||
|
||||
#define USB_VENDOR_ID_SAITEK 0x06a3
|
||||
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
|
||||
|
@ -837,6 +837,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_UP_HPVENDOR2:
|
||||
set_bit(EV_REP, input->evbit);
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
case 0x003: map_key_clear(KEY_BRIGHTNESSDOWN); break;
|
||||
case 0x004: map_key_clear(KEY_BRIGHTNESSUP); break;
|
||||
default: goto ignore;
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_UP_MSVENDOR:
|
||||
goto ignore;
|
||||
|
||||
|
564
drivers/hid/hid-lenovo-tpkbd.c
Normal file
564
drivers/hid/hid-lenovo-tpkbd.c
Normal file
@ -0,0 +1,564 @@
|
||||
/*
|
||||
* HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint
|
||||
*
|
||||
* Copyright (c) 2012 Bernhard Seibold
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/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"
|
||||
|
||||
/* This is only used for the trackpoint part of the driver, hence _tp */
|
||||
struct tpkbd_data_pointer {
|
||||
int led_state;
|
||||
struct led_classdev led_mute;
|
||||
struct led_classdev led_micmute;
|
||||
int press_to_select;
|
||||
int dragging;
|
||||
int release_to_select;
|
||||
int select_right;
|
||||
int sensitivity;
|
||||
int press_speed;
|
||||
};
|
||||
|
||||
#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
|
||||
|
||||
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)) {
|
||||
map_key_clear(KEY_MICMUTE);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef map_key_clear
|
||||
|
||||
static int tpkbd_features_set(struct hid_device *hdev)
|
||||
{
|
||||
struct hid_report *report;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
|
||||
|
||||
report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02;
|
||||
report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08;
|
||||
report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20;
|
||||
report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40;
|
||||
report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver
|
||||
report->field[2]->value[0] = data_pointer->sensitivity;
|
||||
report->field[3]->value[0] = data_pointer->press_speed;
|
||||
|
||||
usbhid_submit_report(hdev, report, USB_DIR_OUT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t pointer_press_to_select_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
|
||||
}
|
||||
|
||||
static ssize_t pointer_press_to_select_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
int value;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
if (kstrtoint(buf, 10, &value))
|
||||
return -EINVAL;
|
||||
if (value < 0 || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
data_pointer->press_to_select = value;
|
||||
tpkbd_features_set(hdev);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t pointer_dragging_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
|
||||
}
|
||||
|
||||
static ssize_t pointer_dragging_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
int value;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
if (kstrtoint(buf, 10, &value))
|
||||
return -EINVAL;
|
||||
if (value < 0 || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
data_pointer->dragging = value;
|
||||
tpkbd_features_set(hdev);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t pointer_release_to_select_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
|
||||
}
|
||||
|
||||
static ssize_t pointer_release_to_select_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
int value;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
if (kstrtoint(buf, 10, &value))
|
||||
return -EINVAL;
|
||||
if (value < 0 || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
data_pointer->release_to_select = value;
|
||||
tpkbd_features_set(hdev);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t pointer_select_right_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
|
||||
}
|
||||
|
||||
static ssize_t pointer_select_right_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
int value;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
if (kstrtoint(buf, 10, &value))
|
||||
return -EINVAL;
|
||||
if (value < 0 || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
data_pointer->select_right = value;
|
||||
tpkbd_features_set(hdev);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t pointer_sensitivity_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
data_pointer->sensitivity);
|
||||
}
|
||||
|
||||
static ssize_t pointer_sensitivity_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
int value;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
|
||||
return -EINVAL;
|
||||
|
||||
data_pointer->sensitivity = value;
|
||||
tpkbd_features_set(hdev);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t pointer_press_speed_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
data_pointer->press_speed);
|
||||
}
|
||||
|
||||
static ssize_t pointer_press_speed_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
int value;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
if (hdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
|
||||
return -EINVAL;
|
||||
|
||||
data_pointer->press_speed = value;
|
||||
tpkbd_features_set(hdev);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct device_attribute dev_attr_pointer_press_to_select =
|
||||
__ATTR(press_to_select, S_IWUSR | S_IRUGO,
|
||||
pointer_press_to_select_show,
|
||||
pointer_press_to_select_store);
|
||||
|
||||
static struct device_attribute dev_attr_pointer_dragging =
|
||||
__ATTR(dragging, S_IWUSR | S_IRUGO,
|
||||
pointer_dragging_show,
|
||||
pointer_dragging_store);
|
||||
|
||||
static struct device_attribute dev_attr_pointer_release_to_select =
|
||||
__ATTR(release_to_select, S_IWUSR | S_IRUGO,
|
||||
pointer_release_to_select_show,
|
||||
pointer_release_to_select_store);
|
||||
|
||||
static struct device_attribute dev_attr_pointer_select_right =
|
||||
__ATTR(select_right, S_IWUSR | S_IRUGO,
|
||||
pointer_select_right_show,
|
||||
pointer_select_right_store);
|
||||
|
||||
static struct device_attribute dev_attr_pointer_sensitivity =
|
||||
__ATTR(sensitivity, S_IWUSR | S_IRUGO,
|
||||
pointer_sensitivity_show,
|
||||
pointer_sensitivity_store);
|
||||
|
||||
static struct device_attribute dev_attr_pointer_press_speed =
|
||||
__ATTR(press_speed, S_IWUSR | S_IRUGO,
|
||||
pointer_press_speed_show,
|
||||
pointer_press_speed_store);
|
||||
|
||||
static struct attribute *tpkbd_attributes_pointer[] = {
|
||||
&dev_attr_pointer_press_to_select.attr,
|
||||
&dev_attr_pointer_dragging.attr,
|
||||
&dev_attr_pointer_release_to_select.attr,
|
||||
&dev_attr_pointer_select_right.attr,
|
||||
&dev_attr_pointer_sensitivity.attr,
|
||||
&dev_attr_pointer_press_speed.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group tpkbd_attr_group_pointer = {
|
||||
.attrs = tpkbd_attributes_pointer,
|
||||
};
|
||||
|
||||
static enum led_brightness tpkbd_led_brightness_get(
|
||||
struct led_classdev *led_cdev)
|
||||
{
|
||||
struct device *dev;
|
||||
struct hid_device *hdev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
int led_nr = 0;
|
||||
|
||||
dev = led_cdev->dev->parent;
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
if (led_cdev == &data_pointer->led_micmute)
|
||||
led_nr = 1;
|
||||
|
||||
return data_pointer->led_state & (1 << led_nr)
|
||||
? LED_FULL
|
||||
: LED_OFF;
|
||||
}
|
||||
|
||||
static void tpkbd_led_brightness_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct device *dev;
|
||||
struct hid_device *hdev;
|
||||
struct hid_report *report;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
int led_nr = 0;
|
||||
|
||||
dev = led_cdev->dev->parent;
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
if (led_cdev == &data_pointer->led_micmute)
|
||||
led_nr = 1;
|
||||
|
||||
if (value == LED_OFF)
|
||||
data_pointer->led_state &= ~(1 << led_nr);
|
||||
else
|
||||
data_pointer->led_state |= 1 << led_nr;
|
||||
|
||||
report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
|
||||
report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
|
||||
report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
|
||||
usbhid_submit_report(hdev, report, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
static int tpkbd_probe_tp(struct hid_device *hdev)
|
||||
{
|
||||
struct device *dev = &hdev->dev;
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
size_t name_sz = strlen(dev_name(dev)) + 16;
|
||||
char *name_mute, *name_micmute;
|
||||
int ret;
|
||||
|
||||
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);
|
||||
if (data_pointer == NULL) {
|
||||
hid_err(hdev, "Could not allocate memory for driver data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
// set same default values as windows driver
|
||||
data_pointer->sensitivity = 0xa0;
|
||||
data_pointer->press_speed = 0x38;
|
||||
|
||||
name_mute = kzalloc(name_sz, GFP_KERNEL);
|
||||
if (name_mute == NULL) {
|
||||
hid_err(hdev, "Could not allocate memory for led data\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
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);
|
||||
|
||||
data_pointer->led_mute.name = name_mute;
|
||||
data_pointer->led_mute.brightness_get = tpkbd_led_brightness_get;
|
||||
data_pointer->led_mute.brightness_set = tpkbd_led_brightness_set;
|
||||
data_pointer->led_mute.dev = dev;
|
||||
led_classdev_register(dev, &data_pointer->led_mute);
|
||||
|
||||
data_pointer->led_micmute.name = name_micmute;
|
||||
data_pointer->led_micmute.brightness_get = tpkbd_led_brightness_get;
|
||||
data_pointer->led_micmute.brightness_set = tpkbd_led_brightness_set;
|
||||
data_pointer->led_micmute.dev = dev;
|
||||
led_classdev_register(dev, &data_pointer->led_micmute);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hid_hw_start failed\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
uhdev = (struct usbhid_device *) hdev->driver_data;
|
||||
|
||||
if (uhdev->ifnum == 1)
|
||||
return tpkbd_probe_tp(hdev);
|
||||
|
||||
return 0;
|
||||
err_free:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tpkbd_remove_tp(struct hid_device *hdev)
|
||||
{
|
||||
struct tpkbd_data_pointer *data_pointer;
|
||||
|
||||
sysfs_remove_group(&hdev->dev.kobj,
|
||||
&tpkbd_attr_group_pointer);
|
||||
|
||||
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
|
||||
|
||||
led_classdev_unregister(&data_pointer->led_micmute);
|
||||
led_classdev_unregister(&data_pointer->led_mute);
|
||||
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
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)
|
||||
tpkbd_remove_tp(hdev);
|
||||
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static const struct hid_device_id tpkbd_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(hid, tpkbd_devices);
|
||||
|
||||
static struct hid_driver tpkbd_driver = {
|
||||
.name = "lenovo_tpkbd",
|
||||
.id_table = tpkbd_devices,
|
||||
.input_mapping = tpkbd_input_mapping,
|
||||
.probe = tpkbd_probe,
|
||||
.remove = tpkbd_remove,
|
||||
};
|
||||
|
||||
static int __init tpkbd_init(void)
|
||||
{
|
||||
return hid_register_driver(&tpkbd_driver);
|
||||
}
|
||||
|
||||
static void __exit tpkbd_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&tpkbd_driver);
|
||||
}
|
||||
|
||||
module_init(tpkbd_init);
|
||||
module_exit(tpkbd_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
@ -48,10 +49,6 @@ static bool scroll_acceleration = false;
|
||||
module_param(scroll_acceleration, bool, 0644);
|
||||
MODULE_PARM_DESC(scroll_acceleration, "Accelerate sequential scroll events");
|
||||
|
||||
static bool report_touches = true;
|
||||
module_param(report_touches, bool, 0644);
|
||||
MODULE_PARM_DESC(report_touches, "Emit touch records (otherwise, only use them for emulation)");
|
||||
|
||||
static bool report_undeciphered;
|
||||
module_param(report_undeciphered, bool, 0644);
|
||||
MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
|
||||
@ -72,15 +69,6 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
|
||||
|
||||
#define SCROLL_ACCEL_DEFAULT 7
|
||||
|
||||
/* Single touch emulation should only begin when no touches are currently down.
|
||||
* This is true when single_touch_id is equal to NO_TOUCHES. If multiple touches
|
||||
* are down and the touch providing for single touch emulation is lifted,
|
||||
* single_touch_id is equal to SINGLE_TOUCH_UP. While single touch emulation is
|
||||
* occurring, single_touch_id corresponds with the tracking id of the touch used.
|
||||
*/
|
||||
#define NO_TOUCHES -1
|
||||
#define SINGLE_TOUCH_UP -2
|
||||
|
||||
/* Touch surface information. Dimension is in hundredths of a mm, min and max
|
||||
* are in units. */
|
||||
#define MOUSE_DIMENSION_X (float)9056
|
||||
@ -129,7 +117,6 @@ struct magicmouse_sc {
|
||||
u8 size;
|
||||
} touches[16];
|
||||
int tracking_ids[16];
|
||||
int single_touch_id;
|
||||
};
|
||||
|
||||
static int magicmouse_firm_touch(struct magicmouse_sc *msc)
|
||||
@ -268,16 +255,14 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
|
||||
}
|
||||
}
|
||||
|
||||
if (down) {
|
||||
if (down)
|
||||
msc->ntouches++;
|
||||
if (msc->single_touch_id == NO_TOUCHES)
|
||||
msc->single_touch_id = id;
|
||||
} else if (msc->single_touch_id == id)
|
||||
msc->single_touch_id = SINGLE_TOUCH_UP;
|
||||
|
||||
input_mt_slot(input, id);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, down);
|
||||
|
||||
/* Generate the input events for this touch. */
|
||||
if (report_touches && down) {
|
||||
input_report_abs(input, ABS_MT_TRACKING_ID, id);
|
||||
if (down) {
|
||||
input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2);
|
||||
input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2);
|
||||
input_report_abs(input, ABS_MT_ORIENTATION, -orientation);
|
||||
@ -290,8 +275,6 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
|
||||
else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
input_event(input, EV_MSC, MSC_RAW, tdata[8]);
|
||||
}
|
||||
|
||||
input_mt_sync(input);
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,12 +295,6 @@ static int magicmouse_raw_event(struct hid_device *hdev,
|
||||
for (ii = 0; ii < npoints; ii++)
|
||||
magicmouse_emit_touch(msc, ii, data + ii * 9 + 4);
|
||||
|
||||
/* We don't need an MT sync here because trackpad emits a
|
||||
* BTN_TOUCH event in a new frame when all touches are released.
|
||||
*/
|
||||
if (msc->ntouches == 0)
|
||||
msc->single_touch_id = NO_TOUCHES;
|
||||
|
||||
clicks = data[1];
|
||||
|
||||
/* The following bits provide a device specific timestamp. They
|
||||
@ -335,9 +312,6 @@ static int magicmouse_raw_event(struct hid_device *hdev,
|
||||
for (ii = 0; ii < npoints; ii++)
|
||||
magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);
|
||||
|
||||
if (report_touches && msc->ntouches == 0)
|
||||
input_mt_sync(input);
|
||||
|
||||
/* When emulating three-button mode, it is important
|
||||
* to have the current touch information before
|
||||
* generating a click event.
|
||||
@ -370,25 +344,17 @@ static int magicmouse_raw_event(struct hid_device *hdev,
|
||||
input_report_rel(input, REL_Y, y);
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
input_report_key(input, BTN_MOUSE, clicks & 1);
|
||||
input_report_key(input, BTN_TOUCH, msc->ntouches > 0);
|
||||
input_report_key(input, BTN_TOOL_FINGER, msc->ntouches == 1);
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP, msc->ntouches == 2);
|
||||
input_report_key(input, BTN_TOOL_TRIPLETAP, msc->ntouches == 3);
|
||||
input_report_key(input, BTN_TOOL_QUADTAP, msc->ntouches == 4);
|
||||
if (msc->single_touch_id >= 0) {
|
||||
input_report_abs(input, ABS_X,
|
||||
msc->touches[msc->single_touch_id].x);
|
||||
input_report_abs(input, ABS_Y,
|
||||
msc->touches[msc->single_touch_id].y);
|
||||
}
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
}
|
||||
|
||||
input_sync(input);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev)
|
||||
static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev)
|
||||
{
|
||||
int error;
|
||||
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
||||
@ -417,62 +383,66 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
|
||||
__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
|
||||
__set_bit(BTN_TOOL_QUADTAP, input->keybit);
|
||||
__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
__set_bit(INPUT_PROP_POINTER, input->propbit);
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
||||
}
|
||||
|
||||
if (report_touches) {
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
|
||||
input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
|
||||
4, 0);
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2,
|
||||
4, 0);
|
||||
input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
|
||||
/* Note: Touch Y position from the device is inverted relative
|
||||
* to how pointer motion is reported (and relative to how USB
|
||||
* HID recommends the coordinates work). This driver keeps
|
||||
* the origin at the same position, and just uses the additive
|
||||
* inverse of the reported Y.
|
||||
*/
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X,
|
||||
MOUSE_MIN_X, MOUSE_MAX_X, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y,
|
||||
MOUSE_MIN_Y, MOUSE_MAX_Y, 4, 0);
|
||||
error = input_mt_init_slots(input, 16);
|
||||
if (error)
|
||||
return error;
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
|
||||
4, 0);
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2,
|
||||
4, 0);
|
||||
input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
|
||||
|
||||
input_abs_set_res(input, ABS_MT_POSITION_X,
|
||||
MOUSE_RES_X);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_Y,
|
||||
MOUSE_RES_Y);
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X,
|
||||
TRACKPAD_MAX_X, 4, 0);
|
||||
input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y,
|
||||
TRACKPAD_MAX_Y, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X,
|
||||
TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y,
|
||||
TRACKPAD_MIN_Y, TRACKPAD_MAX_Y, 4, 0);
|
||||
/* Note: Touch Y position from the device is inverted relative
|
||||
* to how pointer motion is reported (and relative to how USB
|
||||
* HID recommends the coordinates work). This driver keeps
|
||||
* the origin at the same position, and just uses the additive
|
||||
* inverse of the reported Y.
|
||||
*/
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X,
|
||||
MOUSE_MIN_X, MOUSE_MAX_X, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y,
|
||||
MOUSE_MIN_Y, MOUSE_MAX_Y, 4, 0);
|
||||
|
||||
input_abs_set_res(input, ABS_X, TRACKPAD_RES_X);
|
||||
input_abs_set_res(input, ABS_Y, TRACKPAD_RES_Y);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_X,
|
||||
TRACKPAD_RES_X);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_Y,
|
||||
TRACKPAD_RES_Y);
|
||||
}
|
||||
input_abs_set_res(input, ABS_MT_POSITION_X,
|
||||
MOUSE_RES_X);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_Y,
|
||||
MOUSE_RES_Y);
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X,
|
||||
TRACKPAD_MAX_X, 4, 0);
|
||||
input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y,
|
||||
TRACKPAD_MAX_Y, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X,
|
||||
TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y,
|
||||
TRACKPAD_MIN_Y, TRACKPAD_MAX_Y, 4, 0);
|
||||
|
||||
input_set_events_per_packet(input, 60);
|
||||
input_abs_set_res(input, ABS_X, TRACKPAD_RES_X);
|
||||
input_abs_set_res(input, ABS_Y, TRACKPAD_RES_Y);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_X,
|
||||
TRACKPAD_RES_X);
|
||||
input_abs_set_res(input, ABS_MT_POSITION_Y,
|
||||
TRACKPAD_RES_Y);
|
||||
}
|
||||
|
||||
input_set_events_per_packet(input, 60);
|
||||
|
||||
if (report_undeciphered) {
|
||||
__set_bit(EV_MSC, input->evbit);
|
||||
__set_bit(MSC_RAW, input->mscbit);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int magicmouse_input_mapping(struct hid_device *hdev,
|
||||
@ -511,8 +481,6 @@ static int magicmouse_probe(struct hid_device *hdev,
|
||||
msc->quirks = id->driver_data;
|
||||
hid_set_drvdata(hdev, msc);
|
||||
|
||||
msc->single_touch_id = NO_TOUCHES;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "magicmouse hid parse failed\n");
|
||||
@ -528,8 +496,13 @@ static int magicmouse_probe(struct hid_device *hdev,
|
||||
/* We do this after hid-input is done parsing reports so that
|
||||
* hid-input uses the most natural button and axis IDs.
|
||||
*/
|
||||
if (msc->input)
|
||||
magicmouse_setup_input(msc->input, hdev);
|
||||
if (msc->input) {
|
||||
ret = magicmouse_setup_input(msc->input, hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "magicmouse setup input failed (%d)\n", ret);
|
||||
goto err_stop_hw;
|
||||
}
|
||||
}
|
||||
|
||||
if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
|
||||
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
||||
|
@ -83,6 +83,7 @@ struct mt_device {
|
||||
unsigned last_field_index; /* last field index of the report */
|
||||
unsigned last_slot_field; /* the last field of a slot */
|
||||
__s8 inputmode; /* InputMode HID feature, -1 if non-existent */
|
||||
__s8 inputmode_index; /* InputMode HID feature index in the report */
|
||||
__s8 maxcontact_report_id; /* Maximum Contact Number HID feature,
|
||||
-1 if non-existent */
|
||||
__u8 num_received; /* how many contacts we received */
|
||||
@ -260,10 +261,20 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case HID_DG_CONTACTMAX:
|
||||
td->maxcontact_report_id = field->report->id;
|
||||
@ -618,7 +629,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[0] = 0x02;
|
||||
r->field[0]->value[td->inputmode_index] = 0x02;
|
||||
usbhid_submit_report(hdev, r, USB_DIR_OUT);
|
||||
}
|
||||
}
|
||||
@ -951,6 +962,11 @@ static const struct hid_device_id mt_devices[] = {
|
||||
MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC,
|
||||
USB_DEVICE_ID_PANABOARD_UBT880) },
|
||||
|
||||
/* Novatek Panel */
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
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,
|
||||
|
@ -2613,11 +2613,7 @@ static int picolcd_probe(struct hid_device *hdev,
|
||||
goto err_cleanup_data;
|
||||
}
|
||||
|
||||
/* We don't use hidinput but hid_hw_start() fails if nothing is
|
||||
* claimed. So spoof claimed input. */
|
||||
hdev->claimed = HID_CLAIMED_INPUT;
|
||||
error = hid_hw_start(hdev, 0);
|
||||
hdev->claimed = 0;
|
||||
if (error) {
|
||||
hid_err(hdev, "hardware start failed\n");
|
||||
goto err_cleanup_data;
|
||||
|
@ -39,7 +39,7 @@ static ssize_t arvo_sysfs_show_mode_key(struct device *dev,
|
||||
int retval;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_receive(usb_dev, ARVO_COMMAND_MODE_KEY,
|
||||
retval = roccat_common2_receive(usb_dev, ARVO_COMMAND_MODE_KEY,
|
||||
&temp_buf, sizeof(struct arvo_mode_key));
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
if (retval)
|
||||
@ -67,7 +67,7 @@ static ssize_t arvo_sysfs_set_mode_key(struct device *dev,
|
||||
temp_buf.state = state;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_send(usb_dev, ARVO_COMMAND_MODE_KEY,
|
||||
retval = roccat_common2_send(usb_dev, ARVO_COMMAND_MODE_KEY,
|
||||
&temp_buf, sizeof(struct arvo_mode_key));
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
if (retval)
|
||||
@ -87,7 +87,7 @@ static ssize_t arvo_sysfs_show_key_mask(struct device *dev,
|
||||
int retval;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_receive(usb_dev, ARVO_COMMAND_KEY_MASK,
|
||||
retval = roccat_common2_receive(usb_dev, ARVO_COMMAND_KEY_MASK,
|
||||
&temp_buf, sizeof(struct arvo_key_mask));
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
if (retval)
|
||||
@ -115,7 +115,7 @@ static ssize_t arvo_sysfs_set_key_mask(struct device *dev,
|
||||
temp_buf.key_mask = key_mask;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_send(usb_dev, ARVO_COMMAND_KEY_MASK,
|
||||
retval = roccat_common2_send(usb_dev, ARVO_COMMAND_KEY_MASK,
|
||||
&temp_buf, sizeof(struct arvo_key_mask));
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
if (retval)
|
||||
@ -130,7 +130,7 @@ static int arvo_get_actual_profile(struct usb_device *usb_dev)
|
||||
struct arvo_actual_profile temp_buf;
|
||||
int retval;
|
||||
|
||||
retval = roccat_common_receive(usb_dev, ARVO_COMMAND_ACTUAL_PROFILE,
|
||||
retval = roccat_common2_receive(usb_dev, ARVO_COMMAND_ACTUAL_PROFILE,
|
||||
&temp_buf, sizeof(struct arvo_actual_profile));
|
||||
|
||||
if (retval)
|
||||
@ -170,7 +170,7 @@ static ssize_t arvo_sysfs_set_actual_profile(struct device *dev,
|
||||
temp_buf.actual_profile = profile;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_send(usb_dev, ARVO_COMMAND_ACTUAL_PROFILE,
|
||||
retval = roccat_common2_send(usb_dev, ARVO_COMMAND_ACTUAL_PROFILE,
|
||||
&temp_buf, sizeof(struct arvo_actual_profile));
|
||||
if (!retval) {
|
||||
arvo->actual_profile = profile;
|
||||
@ -194,7 +194,7 @@ static ssize_t arvo_sysfs_write(struct file *fp,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_send(usb_dev, command, buf, real_size);
|
||||
retval = roccat_common2_send(usb_dev, command, buf, real_size);
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
|
||||
return (retval ? retval : real_size);
|
||||
@ -217,7 +217,7 @@ static ssize_t arvo_sysfs_read(struct file *fp,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_receive(usb_dev, command, buf, real_size);
|
||||
retval = roccat_common2_receive(usb_dev, command, buf, real_size);
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
|
||||
return (retval ? retval : real_size);
|
||||
|
@ -16,12 +16,12 @@
|
||||
#include <linux/module.h>
|
||||
#include "hid-roccat-common.h"
|
||||
|
||||
static inline uint16_t roccat_common_feature_report(uint8_t report_id)
|
||||
static inline uint16_t roccat_common2_feature_report(uint8_t report_id)
|
||||
{
|
||||
return 0x300 | report_id;
|
||||
}
|
||||
|
||||
int roccat_common_receive(struct usb_device *usb_dev, uint report_id,
|
||||
int roccat_common2_receive(struct usb_device *usb_dev, uint report_id,
|
||||
void *data, uint size)
|
||||
{
|
||||
char *buf;
|
||||
@ -34,16 +34,16 @@ int roccat_common_receive(struct usb_device *usb_dev, uint report_id,
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
HID_REQ_GET_REPORT,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
roccat_common_feature_report(report_id),
|
||||
roccat_common2_feature_report(report_id),
|
||||
0, buf, size, USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
memcpy(data, buf, size);
|
||||
kfree(buf);
|
||||
return ((len < 0) ? len : ((len != size) ? -EIO : 0));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(roccat_common_receive);
|
||||
EXPORT_SYMBOL_GPL(roccat_common2_receive);
|
||||
|
||||
int roccat_common_send(struct usb_device *usb_dev, uint report_id,
|
||||
int roccat_common2_send(struct usb_device *usb_dev, uint report_id,
|
||||
void const *data, uint size)
|
||||
{
|
||||
char *buf;
|
||||
@ -56,13 +56,71 @@ int roccat_common_send(struct usb_device *usb_dev, uint report_id,
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
HID_REQ_SET_REPORT,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
roccat_common_feature_report(report_id),
|
||||
roccat_common2_feature_report(report_id),
|
||||
0, buf, size, USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
kfree(buf);
|
||||
return ((len < 0) ? len : ((len != size) ? -EIO : 0));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(roccat_common_send);
|
||||
EXPORT_SYMBOL_GPL(roccat_common2_send);
|
||||
|
||||
enum roccat_common2_control_states {
|
||||
ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD = 0,
|
||||
ROCCAT_COMMON_CONTROL_STATUS_OK = 1,
|
||||
ROCCAT_COMMON_CONTROL_STATUS_INVALID = 2,
|
||||
ROCCAT_COMMON_CONTROL_STATUS_WAIT = 3,
|
||||
};
|
||||
|
||||
static int roccat_common2_receive_control_status(struct usb_device *usb_dev)
|
||||
{
|
||||
int retval;
|
||||
struct roccat_common2_control control;
|
||||
|
||||
do {
|
||||
msleep(50);
|
||||
retval = roccat_common2_receive(usb_dev,
|
||||
ROCCAT_COMMON_COMMAND_CONTROL,
|
||||
&control, sizeof(struct roccat_common2_control));
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
switch (control.value) {
|
||||
case ROCCAT_COMMON_CONTROL_STATUS_OK:
|
||||
return 0;
|
||||
case ROCCAT_COMMON_CONTROL_STATUS_WAIT:
|
||||
msleep(500);
|
||||
continue;
|
||||
case ROCCAT_COMMON_CONTROL_STATUS_INVALID:
|
||||
|
||||
case ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD:
|
||||
/* seems to be critical - replug necessary */
|
||||
return -EINVAL;
|
||||
default:
|
||||
dev_err(&usb_dev->dev,
|
||||
"roccat_common2_receive_control_status: "
|
||||
"unknown response value 0x%x\n",
|
||||
control.value);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
} while (1);
|
||||
}
|
||||
|
||||
int roccat_common2_send_with_status(struct usb_device *usb_dev,
|
||||
uint command, void const *buf, uint size)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = roccat_common2_send(usb_dev, command, buf, size);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
msleep(100);
|
||||
|
||||
return roccat_common2_receive_control_status(usb_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(roccat_common2_send_with_status);
|
||||
|
||||
MODULE_AUTHOR("Stefan Achatz");
|
||||
MODULE_DESCRIPTION("USB Roccat common driver");
|
||||
|
@ -15,9 +15,21 @@
|
||||
#include <linux/usb.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
int roccat_common_receive(struct usb_device *usb_dev, uint report_id,
|
||||
enum roccat_common2_commands {
|
||||
ROCCAT_COMMON_COMMAND_CONTROL = 0x4,
|
||||
};
|
||||
|
||||
struct roccat_common2_control {
|
||||
uint8_t command;
|
||||
uint8_t value;
|
||||
uint8_t request; /* always 0 on requesting write check */
|
||||
} __packed;
|
||||
|
||||
int roccat_common2_receive(struct usb_device *usb_dev, uint report_id,
|
||||
void *data, uint size);
|
||||
int roccat_common_send(struct usb_device *usb_dev, uint report_id,
|
||||
int roccat_common2_send(struct usb_device *usb_dev, uint report_id,
|
||||
void const *data, uint size);
|
||||
int roccat_common2_send_with_status(struct usb_device *usb_dev,
|
||||
uint command, void const *buf, uint size);
|
||||
|
||||
#endif
|
||||
|
@ -36,51 +36,7 @@ static void isku_profile_activated(struct isku_device *isku, uint new_profile)
|
||||
static int isku_receive(struct usb_device *usb_dev, uint command,
|
||||
void *buf, uint size)
|
||||
{
|
||||
return roccat_common_receive(usb_dev, command, buf, size);
|
||||
}
|
||||
|
||||
static int isku_receive_control_status(struct usb_device *usb_dev)
|
||||
{
|
||||
int retval;
|
||||
struct isku_control control;
|
||||
|
||||
do {
|
||||
msleep(50);
|
||||
retval = isku_receive(usb_dev, ISKU_COMMAND_CONTROL,
|
||||
&control, sizeof(struct isku_control));
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
switch (control.value) {
|
||||
case ISKU_CONTROL_VALUE_STATUS_OK:
|
||||
return 0;
|
||||
case ISKU_CONTROL_VALUE_STATUS_WAIT:
|
||||
continue;
|
||||
case ISKU_CONTROL_VALUE_STATUS_INVALID:
|
||||
/* seems to be critical - replug necessary */
|
||||
case ISKU_CONTROL_VALUE_STATUS_OVERLOAD:
|
||||
return -EINVAL;
|
||||
default:
|
||||
hid_err(usb_dev, "isku_receive_control_status: "
|
||||
"unknown response value 0x%x\n",
|
||||
control.value);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int isku_send(struct usb_device *usb_dev, uint command,
|
||||
void const *buf, uint size)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = roccat_common_send(usb_dev, command, buf, size);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return isku_receive_control_status(usb_dev);
|
||||
return roccat_common2_receive(usb_dev, command, buf, size);
|
||||
}
|
||||
|
||||
static int isku_get_actual_profile(struct usb_device *usb_dev)
|
||||
@ -100,7 +56,8 @@ static int isku_set_actual_profile(struct usb_device *usb_dev, int new_profile)
|
||||
buf.command = ISKU_COMMAND_ACTUAL_PROFILE;
|
||||
buf.size = sizeof(struct isku_actual_profile);
|
||||
buf.actual_profile = new_profile;
|
||||
return isku_send(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE, &buf,
|
||||
return roccat_common2_send_with_status(usb_dev,
|
||||
ISKU_COMMAND_ACTUAL_PROFILE, &buf,
|
||||
sizeof(struct isku_actual_profile));
|
||||
}
|
||||
|
||||
@ -197,7 +154,8 @@ static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&isku->isku_lock);
|
||||
retval = isku_send(usb_dev, command, (void *)buf, real_size);
|
||||
retval = roccat_common2_send_with_status(usb_dev, command,
|
||||
(void *)buf, real_size);
|
||||
mutex_unlock(&isku->isku_lock);
|
||||
|
||||
return retval ? retval : real_size;
|
||||
|
@ -25,13 +25,6 @@ struct isku_control {
|
||||
uint8_t request;
|
||||
} __packed;
|
||||
|
||||
enum isku_control_values {
|
||||
ISKU_CONTROL_VALUE_STATUS_OVERLOAD = 0,
|
||||
ISKU_CONTROL_VALUE_STATUS_OK = 1,
|
||||
ISKU_CONTROL_VALUE_STATUS_INVALID = 2,
|
||||
ISKU_CONTROL_VALUE_STATUS_WAIT = 3,
|
||||
};
|
||||
|
||||
struct isku_actual_profile {
|
||||
uint8_t command; /* ISKU_COMMAND_ACTUAL_PROFILE */
|
||||
uint8_t size; /* always 3 */
|
||||
|
@ -138,7 +138,7 @@ static int kone_check_write(struct usb_device *usb_dev)
|
||||
return 0;
|
||||
|
||||
/* unknown answer */
|
||||
hid_err(usb_dev, "got retval %d when checking write\n", data);
|
||||
dev_err(&usb_dev->dev, "got retval %d when checking write\n", data);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -503,7 +503,7 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev,
|
||||
|
||||
retval = kone_set_settings(usb_dev, &kone->settings);
|
||||
if (retval) {
|
||||
hid_err(usb_dev, "couldn't set tcu state\n");
|
||||
dev_err(&usb_dev->dev, "couldn't set tcu state\n");
|
||||
/*
|
||||
* try to reread valid settings into buffer overwriting
|
||||
* first error code
|
||||
@ -519,7 +519,7 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev,
|
||||
|
||||
retval = size;
|
||||
exit_no_settings:
|
||||
hid_err(usb_dev, "couldn't read settings\n");
|
||||
dev_err(&usb_dev->dev, "couldn't read settings\n");
|
||||
exit_unlock:
|
||||
mutex_unlock(&kone->kone_lock);
|
||||
return retval;
|
||||
|
@ -39,88 +39,26 @@ static void koneplus_profile_activated(struct koneplus_device *koneplus,
|
||||
static int koneplus_send_control(struct usb_device *usb_dev, uint value,
|
||||
enum koneplus_control_requests request)
|
||||
{
|
||||
struct koneplus_control control;
|
||||
struct roccat_common2_control control;
|
||||
|
||||
if ((request == KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
||||
request == KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
||||
value > 4)
|
||||
return -EINVAL;
|
||||
|
||||
control.command = KONEPLUS_COMMAND_CONTROL;
|
||||
control.command = ROCCAT_COMMON_COMMAND_CONTROL;
|
||||
control.value = value;
|
||||
control.request = request;
|
||||
|
||||
return roccat_common_send(usb_dev, KONEPLUS_COMMAND_CONTROL,
|
||||
&control, sizeof(struct koneplus_control));
|
||||
}
|
||||
|
||||
static int koneplus_receive_control_status(struct usb_device *usb_dev)
|
||||
{
|
||||
int retval;
|
||||
struct koneplus_control control;
|
||||
|
||||
do {
|
||||
retval = roccat_common_receive(usb_dev, KONEPLUS_COMMAND_CONTROL,
|
||||
&control, sizeof(struct koneplus_control));
|
||||
|
||||
/* check if we get a completely wrong answer */
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_OK)
|
||||
return 0;
|
||||
|
||||
/* indicates that hardware needs some more time to complete action */
|
||||
if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_WAIT) {
|
||||
msleep(500); /* windows driver uses 1000 */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* seems to be critical - replug necessary */
|
||||
if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_OVERLOAD)
|
||||
return -EINVAL;
|
||||
|
||||
hid_err(usb_dev, "koneplus_receive_control_status: "
|
||||
"unknown response value 0x%x\n", control.value);
|
||||
return -EINVAL;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int koneplus_send(struct usb_device *usb_dev, uint command,
|
||||
void const *buf, uint size)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = roccat_common_send(usb_dev, command, buf, size);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return koneplus_receive_control_status(usb_dev);
|
||||
}
|
||||
|
||||
static int koneplus_select_profile(struct usb_device *usb_dev, uint number,
|
||||
enum koneplus_control_requests request)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = koneplus_send_control(usb_dev, number, request);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* allow time to settle things - windows driver uses 500 */
|
||||
msleep(100);
|
||||
|
||||
retval = koneplus_receive_control_status(usb_dev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return 0;
|
||||
return roccat_common2_send_with_status(usb_dev,
|
||||
ROCCAT_COMMON_COMMAND_CONTROL,
|
||||
&control, sizeof(struct roccat_common2_control));
|
||||
}
|
||||
|
||||
static int koneplus_get_info(struct usb_device *usb_dev,
|
||||
struct koneplus_info *buf)
|
||||
{
|
||||
return roccat_common_receive(usb_dev, KONEPLUS_COMMAND_INFO,
|
||||
return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_INFO,
|
||||
buf, sizeof(struct koneplus_info));
|
||||
}
|
||||
|
||||
@ -129,19 +67,20 @@ static int koneplus_get_profile_settings(struct usb_device *usb_dev,
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = koneplus_select_profile(usb_dev, number,
|
||||
retval = koneplus_send_control(usb_dev, number,
|
||||
KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return roccat_common_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_SETTINGS,
|
||||
return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_SETTINGS,
|
||||
buf, sizeof(struct koneplus_profile_settings));
|
||||
}
|
||||
|
||||
static int koneplus_set_profile_settings(struct usb_device *usb_dev,
|
||||
struct koneplus_profile_settings const *settings)
|
||||
{
|
||||
return koneplus_send(usb_dev, KONEPLUS_COMMAND_PROFILE_SETTINGS,
|
||||
return roccat_common2_send_with_status(usb_dev,
|
||||
KONEPLUS_COMMAND_PROFILE_SETTINGS,
|
||||
settings, sizeof(struct koneplus_profile_settings));
|
||||
}
|
||||
|
||||
@ -150,19 +89,20 @@ static int koneplus_get_profile_buttons(struct usb_device *usb_dev,
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = koneplus_select_profile(usb_dev, number,
|
||||
retval = koneplus_send_control(usb_dev, number,
|
||||
KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return roccat_common_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_BUTTONS,
|
||||
return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_BUTTONS,
|
||||
buf, sizeof(struct koneplus_profile_buttons));
|
||||
}
|
||||
|
||||
static int koneplus_set_profile_buttons(struct usb_device *usb_dev,
|
||||
struct koneplus_profile_buttons const *buttons)
|
||||
{
|
||||
return koneplus_send(usb_dev, KONEPLUS_COMMAND_PROFILE_BUTTONS,
|
||||
return roccat_common2_send_with_status(usb_dev,
|
||||
KONEPLUS_COMMAND_PROFILE_BUTTONS,
|
||||
buttons, sizeof(struct koneplus_profile_buttons));
|
||||
}
|
||||
|
||||
@ -172,7 +112,7 @@ static int koneplus_get_actual_profile(struct usb_device *usb_dev)
|
||||
struct koneplus_actual_profile buf;
|
||||
int retval;
|
||||
|
||||
retval = roccat_common_receive(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE,
|
||||
retval = roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE,
|
||||
&buf, sizeof(struct koneplus_actual_profile));
|
||||
|
||||
return retval ? retval : buf.actual_profile;
|
||||
@ -187,7 +127,8 @@ static int koneplus_set_actual_profile(struct usb_device *usb_dev,
|
||||
buf.size = sizeof(struct koneplus_actual_profile);
|
||||
buf.actual_profile = new_profile;
|
||||
|
||||
return koneplus_send(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE,
|
||||
return roccat_common2_send_with_status(usb_dev,
|
||||
KONEPLUS_COMMAND_ACTUAL_PROFILE,
|
||||
&buf, sizeof(struct koneplus_actual_profile));
|
||||
}
|
||||
|
||||
@ -208,7 +149,7 @@ static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&koneplus->koneplus_lock);
|
||||
retval = roccat_common_receive(usb_dev, command, buf, real_size);
|
||||
retval = roccat_common2_receive(usb_dev, command, buf, real_size);
|
||||
mutex_unlock(&koneplus->koneplus_lock);
|
||||
|
||||
if (retval)
|
||||
@ -231,7 +172,8 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&koneplus->koneplus_lock);
|
||||
retval = koneplus_send(usb_dev, command, buf, real_size);
|
||||
retval = roccat_common2_send_with_status(usb_dev, command,
|
||||
buf, real_size);
|
||||
mutex_unlock(&koneplus->koneplus_lock);
|
||||
|
||||
if (retval)
|
||||
|
@ -20,32 +20,11 @@ struct koneplus_talk {
|
||||
uint8_t data[14];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* case 1: writes request 80 and reads value 1
|
||||
*
|
||||
*/
|
||||
struct koneplus_control {
|
||||
uint8_t command; /* KONEPLUS_COMMAND_CONTROL */
|
||||
/*
|
||||
* value is profile number in range 0-4 for requesting settings and buttons
|
||||
* 1 if status ok for requesting status
|
||||
*/
|
||||
uint8_t value;
|
||||
uint8_t request;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
enum koneplus_control_requests {
|
||||
KONEPLUS_CONTROL_REQUEST_STATUS = 0x00,
|
||||
KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x80,
|
||||
KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS = 0x90,
|
||||
};
|
||||
|
||||
enum koneplus_control_values {
|
||||
KONEPLUS_CONTROL_REQUEST_STATUS_OVERLOAD = 0,
|
||||
KONEPLUS_CONTROL_REQUEST_STATUS_OK = 1,
|
||||
KONEPLUS_CONTROL_REQUEST_STATUS_WAIT = 3,
|
||||
};
|
||||
|
||||
struct koneplus_actual_profile {
|
||||
uint8_t command; /* KONEPLUS_COMMAND_ACTUAL_PROFILE */
|
||||
uint8_t size; /* always 3 */
|
||||
@ -137,7 +116,6 @@ struct koneplus_tcu_image {
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
enum koneplus_commands {
|
||||
KONEPLUS_COMMAND_CONTROL = 0x4,
|
||||
KONEPLUS_COMMAND_ACTUAL_PROFILE = 0x5,
|
||||
KONEPLUS_COMMAND_PROFILE_SETTINGS = 0x6,
|
||||
KONEPLUS_COMMAND_PROFILE_BUTTONS = 0x7,
|
||||
|
@ -47,69 +47,23 @@ static int kovaplus_send_control(struct usb_device *usb_dev, uint value,
|
||||
enum kovaplus_control_requests request)
|
||||
{
|
||||
int retval;
|
||||
struct kovaplus_control control;
|
||||
struct roccat_common2_control control;
|
||||
|
||||
if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
||||
request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
||||
value > 4)
|
||||
return -EINVAL;
|
||||
|
||||
control.command = KOVAPLUS_COMMAND_CONTROL;
|
||||
control.command = ROCCAT_COMMON_COMMAND_CONTROL;
|
||||
control.value = value;
|
||||
control.request = request;
|
||||
|
||||
retval = roccat_common_send(usb_dev, KOVAPLUS_COMMAND_CONTROL,
|
||||
&control, sizeof(struct kovaplus_control));
|
||||
retval = roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
|
||||
&control, sizeof(struct roccat_common2_control));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int kovaplus_receive_control_status(struct usb_device *usb_dev)
|
||||
{
|
||||
int retval;
|
||||
struct kovaplus_control control;
|
||||
|
||||
do {
|
||||
retval = roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_CONTROL,
|
||||
&control, sizeof(struct kovaplus_control));
|
||||
|
||||
/* check if we get a completely wrong answer */
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OK)
|
||||
return 0;
|
||||
|
||||
/* indicates that hardware needs some more time to complete action */
|
||||
if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT) {
|
||||
msleep(500); /* windows driver uses 1000 */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* seems to be critical - replug necessary */
|
||||
if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD)
|
||||
return -EINVAL;
|
||||
|
||||
hid_err(usb_dev, "roccat_common_receive_control_status: "
|
||||
"unknown response value 0x%x\n", control.value);
|
||||
return -EINVAL;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int kovaplus_send(struct usb_device *usb_dev, uint command,
|
||||
void const *buf, uint size)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = roccat_common_send(usb_dev, command, buf, size);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
msleep(100);
|
||||
|
||||
return kovaplus_receive_control_status(usb_dev);
|
||||
}
|
||||
|
||||
static int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
|
||||
enum kovaplus_control_requests request)
|
||||
{
|
||||
@ -119,7 +73,7 @@ static int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
|
||||
static int kovaplus_get_info(struct usb_device *usb_dev,
|
||||
struct kovaplus_info *buf)
|
||||
{
|
||||
return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_INFO,
|
||||
return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_INFO,
|
||||
buf, sizeof(struct kovaplus_info));
|
||||
}
|
||||
|
||||
@ -133,14 +87,15 @@ static int kovaplus_get_profile_settings(struct usb_device *usb_dev,
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
|
||||
return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
|
||||
buf, sizeof(struct kovaplus_profile_settings));
|
||||
}
|
||||
|
||||
static int kovaplus_set_profile_settings(struct usb_device *usb_dev,
|
||||
struct kovaplus_profile_settings const *settings)
|
||||
{
|
||||
return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
|
||||
return roccat_common2_send_with_status(usb_dev,
|
||||
KOVAPLUS_COMMAND_PROFILE_SETTINGS,
|
||||
settings, sizeof(struct kovaplus_profile_settings));
|
||||
}
|
||||
|
||||
@ -154,14 +109,15 @@ static int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
|
||||
return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
|
||||
buf, sizeof(struct kovaplus_profile_buttons));
|
||||
}
|
||||
|
||||
static int kovaplus_set_profile_buttons(struct usb_device *usb_dev,
|
||||
struct kovaplus_profile_buttons const *buttons)
|
||||
{
|
||||
return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
|
||||
return roccat_common2_send_with_status(usb_dev,
|
||||
KOVAPLUS_COMMAND_PROFILE_BUTTONS,
|
||||
buttons, sizeof(struct kovaplus_profile_buttons));
|
||||
}
|
||||
|
||||
@ -171,7 +127,7 @@ static int kovaplus_get_actual_profile(struct usb_device *usb_dev)
|
||||
struct kovaplus_actual_profile buf;
|
||||
int retval;
|
||||
|
||||
retval = roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
|
||||
retval = roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
|
||||
&buf, sizeof(struct kovaplus_actual_profile));
|
||||
|
||||
return retval ? retval : buf.actual_profile;
|
||||
@ -186,7 +142,8 @@ static int kovaplus_set_actual_profile(struct usb_device *usb_dev,
|
||||
buf.size = sizeof(struct kovaplus_actual_profile);
|
||||
buf.actual_profile = new_profile;
|
||||
|
||||
return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
|
||||
return roccat_common2_send_with_status(usb_dev,
|
||||
KOVAPLUS_COMMAND_ACTUAL_PROFILE,
|
||||
&buf, sizeof(struct kovaplus_actual_profile));
|
||||
}
|
||||
|
||||
|
@ -14,27 +14,13 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct kovaplus_control {
|
||||
uint8_t command; /* KOVAPLUS_COMMAND_CONTROL */
|
||||
uint8_t value;
|
||||
uint8_t request;
|
||||
} __packed;
|
||||
|
||||
enum kovaplus_control_requests {
|
||||
/* read after write; value = 1 */
|
||||
KOVAPLUS_CONTROL_REQUEST_STATUS = 0x0,
|
||||
/* write; value = profile number range 0-4 */
|
||||
KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
|
||||
/* write; value = profile number range 0-4 */
|
||||
KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20,
|
||||
};
|
||||
|
||||
enum kovaplus_control_values {
|
||||
KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD = 0, /* supposed */
|
||||
KOVAPLUS_CONTROL_REQUEST_STATUS_OK = 1,
|
||||
KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT = 3, /* supposed */
|
||||
};
|
||||
|
||||
struct kovaplus_actual_profile {
|
||||
uint8_t command; /* KOVAPLUS_COMMAND_ACTUAL_PROFILE */
|
||||
uint8_t size; /* always 3 */
|
||||
@ -75,7 +61,6 @@ struct kovaplus_a {
|
||||
} __packed;
|
||||
|
||||
enum kovaplus_commands {
|
||||
KOVAPLUS_COMMAND_CONTROL = 0x4,
|
||||
KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5,
|
||||
KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6,
|
||||
KOVAPLUS_COMMAND_PROFILE_BUTTONS = 0x7,
|
||||
|
@ -42,43 +42,19 @@ static void profile_activated(struct pyra_device *pyra,
|
||||
static int pyra_send_control(struct usb_device *usb_dev, int value,
|
||||
enum pyra_control_requests request)
|
||||
{
|
||||
struct pyra_control control;
|
||||
struct roccat_common2_control control;
|
||||
|
||||
if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
||||
request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
||||
(value < 0 || value > 4))
|
||||
return -EINVAL;
|
||||
|
||||
control.command = PYRA_COMMAND_CONTROL;
|
||||
control.command = ROCCAT_COMMON_COMMAND_CONTROL;
|
||||
control.value = value;
|
||||
control.request = request;
|
||||
|
||||
return roccat_common_send(usb_dev, PYRA_COMMAND_CONTROL,
|
||||
&control, sizeof(struct pyra_control));
|
||||
}
|
||||
|
||||
static int pyra_receive_control_status(struct usb_device *usb_dev)
|
||||
{
|
||||
int retval;
|
||||
struct pyra_control control;
|
||||
|
||||
do {
|
||||
msleep(10);
|
||||
retval = roccat_common_receive(usb_dev, PYRA_COMMAND_CONTROL,
|
||||
&control, sizeof(struct pyra_control));
|
||||
|
||||
/* requested too early, try again */
|
||||
} while (retval == -EPROTO);
|
||||
|
||||
if (!retval && control.command == PYRA_COMMAND_CONTROL &&
|
||||
control.request == PYRA_CONTROL_REQUEST_STATUS &&
|
||||
control.value == 1)
|
||||
return 0;
|
||||
else {
|
||||
hid_err(usb_dev, "receive control status: unknown response 0x%x 0x%x\n",
|
||||
control.request, control.value);
|
||||
return retval ? retval : -EINVAL;
|
||||
}
|
||||
return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
|
||||
&control, sizeof(struct roccat_common2_control));
|
||||
}
|
||||
|
||||
static int pyra_get_profile_settings(struct usb_device *usb_dev,
|
||||
@ -89,7 +65,7 @@ static int pyra_get_profile_settings(struct usb_device *usb_dev,
|
||||
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
|
||||
if (retval)
|
||||
return retval;
|
||||
return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
|
||||
return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
|
||||
buf, sizeof(struct pyra_profile_settings));
|
||||
}
|
||||
|
||||
@ -101,51 +77,44 @@ static int pyra_get_profile_buttons(struct usb_device *usb_dev,
|
||||
PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
|
||||
if (retval)
|
||||
return retval;
|
||||
return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS,
|
||||
return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS,
|
||||
buf, sizeof(struct pyra_profile_buttons));
|
||||
}
|
||||
|
||||
static int pyra_get_settings(struct usb_device *usb_dev,
|
||||
struct pyra_settings *buf)
|
||||
{
|
||||
return roccat_common_receive(usb_dev, PYRA_COMMAND_SETTINGS,
|
||||
return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
|
||||
buf, sizeof(struct pyra_settings));
|
||||
}
|
||||
|
||||
static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf)
|
||||
{
|
||||
return roccat_common_receive(usb_dev, PYRA_COMMAND_INFO,
|
||||
return roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO,
|
||||
buf, sizeof(struct pyra_info));
|
||||
}
|
||||
|
||||
static int pyra_send(struct usb_device *usb_dev, uint command,
|
||||
void const *buf, uint size)
|
||||
{
|
||||
int retval;
|
||||
retval = roccat_common_send(usb_dev, command, buf, size);
|
||||
if (retval)
|
||||
return retval;
|
||||
return pyra_receive_control_status(usb_dev);
|
||||
}
|
||||
|
||||
static int pyra_set_profile_settings(struct usb_device *usb_dev,
|
||||
struct pyra_profile_settings const *settings)
|
||||
{
|
||||
return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, settings,
|
||||
return roccat_common2_send_with_status(usb_dev,
|
||||
PYRA_COMMAND_PROFILE_SETTINGS, settings,
|
||||
sizeof(struct pyra_profile_settings));
|
||||
}
|
||||
|
||||
static int pyra_set_profile_buttons(struct usb_device *usb_dev,
|
||||
struct pyra_profile_buttons const *buttons)
|
||||
{
|
||||
return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, buttons,
|
||||
return roccat_common2_send_with_status(usb_dev,
|
||||
PYRA_COMMAND_PROFILE_BUTTONS, buttons,
|
||||
sizeof(struct pyra_profile_buttons));
|
||||
}
|
||||
|
||||
static int pyra_set_settings(struct usb_device *usb_dev,
|
||||
struct pyra_settings const *settings)
|
||||
{
|
||||
return pyra_send(usb_dev, PYRA_COMMAND_SETTINGS, settings,
|
||||
return roccat_common2_send_with_status(usb_dev,
|
||||
PYRA_COMMAND_SETTINGS, settings,
|
||||
sizeof(struct pyra_settings));
|
||||
}
|
||||
|
||||
|
@ -20,18 +20,7 @@ struct pyra_b {
|
||||
uint8_t unknown; /* 1 */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct pyra_control {
|
||||
uint8_t command; /* PYRA_COMMAND_CONTROL */
|
||||
/*
|
||||
* value is profile number for request_settings and request_buttons
|
||||
* 1 if status ok for request_status
|
||||
*/
|
||||
uint8_t value; /* Range 0-4 */
|
||||
uint8_t request;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
enum pyra_control_requests {
|
||||
PYRA_CONTROL_REQUEST_STATUS = 0x00,
|
||||
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
|
||||
PYRA_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20
|
||||
};
|
||||
@ -75,7 +64,6 @@ struct pyra_info {
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
enum pyra_commands {
|
||||
PYRA_COMMAND_CONTROL = 0x4,
|
||||
PYRA_COMMAND_SETTINGS = 0x5,
|
||||
PYRA_COMMAND_PROFILE_SETTINGS = 0x6,
|
||||
PYRA_COMMAND_PROFILE_BUTTONS = 0x7,
|
||||
|
316
drivers/hid/hid-roccat-savu.c
Normal file
316
drivers/hid/hid-roccat-savu.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Roccat Savu driver for Linux
|
||||
*
|
||||
* Copyright (c) 2012 Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
/* Roccat Savu is a gamer mouse with macro keys that can be configured in
|
||||
* 5 profiles.
|
||||
*/
|
||||
|
||||
#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"
|
||||
#include "hid-roccat-savu.h"
|
||||
|
||||
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_R(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_R(info, INFO),
|
||||
SAVU_BIN_ATTRIBUTE_RW(sensor, SENSOR),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static int savu_init_savu_device_struct(struct usb_device *usb_dev,
|
||||
struct savu_device *savu)
|
||||
{
|
||||
mutex_init(&savu->savu_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
int retval;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
!= USB_INTERFACE_PROTOCOL_MOUSE) {
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
savu = kzalloc(sizeof(*savu), GFP_KERNEL);
|
||||
if (!savu) {
|
||||
hid_err(hdev, "can't alloc device descriptor\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
hid_set_drvdata(hdev, savu);
|
||||
|
||||
retval = savu_init_savu_device_struct(usb_dev, savu);
|
||||
if (retval) {
|
||||
hid_err(hdev, "couldn't init struct savu_device\n");
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
retval = roccat_connect(savu_class, hdev,
|
||||
sizeof(struct savu_roccat_report));
|
||||
if (retval < 0) {
|
||||
hid_err(hdev, "couldn't init char dev\n");
|
||||
} else {
|
||||
savu->chrdev_minor = retval;
|
||||
savu->roccat_claimed = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit_free:
|
||||
kfree(savu);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void savu_remove_specials(struct hid_device *hdev)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
struct savu_device *savu;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
!= USB_INTERFACE_PROTOCOL_MOUSE)
|
||||
return;
|
||||
|
||||
savu = hid_get_drvdata(hdev);
|
||||
if (savu->roccat_claimed)
|
||||
roccat_disconnect(savu->chrdev_minor);
|
||||
kfree(savu);
|
||||
}
|
||||
|
||||
static int savu_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 = savu_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 savu_remove(struct hid_device *hdev)
|
||||
{
|
||||
savu_remove_specials(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static void savu_report_to_chrdev(struct savu_device const *savu,
|
||||
u8 const *data)
|
||||
{
|
||||
struct savu_roccat_report roccat_report;
|
||||
struct savu_mouse_report_special const *special_report;
|
||||
|
||||
if (data[0] != SAVU_MOUSE_REPORT_NUMBER_SPECIAL)
|
||||
return;
|
||||
|
||||
special_report = (struct savu_mouse_report_special const *)data;
|
||||
|
||||
roccat_report.type = special_report->type;
|
||||
roccat_report.data[0] = special_report->data[0];
|
||||
roccat_report.data[1] = special_report->data[1];
|
||||
roccat_report_event(savu->chrdev_minor,
|
||||
(uint8_t const *)&roccat_report);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
!= USB_INTERFACE_PROTOCOL_MOUSE)
|
||||
return 0;
|
||||
|
||||
if (savu == NULL)
|
||||
return 0;
|
||||
|
||||
if (savu->roccat_claimed)
|
||||
savu_report_to_chrdev(savu, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id savu_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(hid, savu_devices);
|
||||
|
||||
static struct hid_driver savu_driver = {
|
||||
.name = "savu",
|
||||
.id_table = savu_devices,
|
||||
.probe = savu_probe,
|
||||
.remove = savu_remove,
|
||||
.raw_event = savu_raw_event
|
||||
};
|
||||
|
||||
static int __init savu_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
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;
|
||||
|
||||
retval = hid_register_driver(&savu_driver);
|
||||
if (retval)
|
||||
class_destroy(savu_class);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit savu_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&savu_driver);
|
||||
class_destroy(savu_class);
|
||||
}
|
||||
|
||||
module_init(savu_init);
|
||||
module_exit(savu_exit);
|
||||
|
||||
MODULE_AUTHOR("Stefan Achatz");
|
||||
MODULE_DESCRIPTION("USB Roccat Savu driver");
|
||||
MODULE_LICENSE("GPL v2");
|
87
drivers/hid/hid-roccat-savu.h
Normal file
87
drivers/hid/hid-roccat-savu.h
Normal file
@ -0,0 +1,87 @@
|
||||
#ifndef __HID_ROCCAT_SAVU_H
|
||||
#define __HID_ROCCAT_SAVU_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 {
|
||||
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;
|
||||
uint8_t type;
|
||||
uint8_t data[2];
|
||||
} __packed;
|
||||
|
||||
enum {
|
||||
SAVU_MOUSE_REPORT_NUMBER_SPECIAL = 3,
|
||||
};
|
||||
|
||||
enum savu_mouse_report_button_types {
|
||||
/* data1 = new profile range 1-5 */
|
||||
SAVU_MOUSE_REPORT_BUTTON_TYPE_PROFILE = 0x20,
|
||||
|
||||
/* data1 = button number range 1-24; data2 = action */
|
||||
SAVU_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH = 0x60,
|
||||
|
||||
/* data1 = button number range 1-24; data2 = action */
|
||||
SAVU_MOUSE_REPORT_BUTTON_TYPE_TIMER = 0x80,
|
||||
|
||||
/* data1 = setting number range 1-5 */
|
||||
SAVU_MOUSE_REPORT_BUTTON_TYPE_CPI = 0xb0,
|
||||
|
||||
/* data1 and data2 = range 0x1-0xb */
|
||||
SAVU_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY = 0xc0,
|
||||
|
||||
/* data1 = 22 = next track...
|
||||
* data2 = action
|
||||
*/
|
||||
SAVU_MOUSE_REPORT_BUTTON_TYPE_MULTIMEDIA = 0xf0,
|
||||
};
|
||||
|
||||
struct savu_roccat_report {
|
||||
uint8_t type;
|
||||
uint8_t data[2];
|
||||
} __packed;
|
||||
|
||||
struct savu_device {
|
||||
int roccat_claimed;
|
||||
int chrdev_minor;
|
||||
|
||||
struct mutex savu_lock;
|
||||
};
|
||||
|
||||
#endif
|
@ -77,7 +77,7 @@ static __u16 wiiext_keymap[] = {
|
||||
BTN_TR, /* WIIEXT_KEY_RT */
|
||||
};
|
||||
|
||||
/* diable all extensions */
|
||||
/* disable all extensions */
|
||||
static void ext_disable(struct wiimote_ext *ext)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -84,7 +84,7 @@ static int hid_start_in(struct hid_device *hid)
|
||||
spin_lock_irqsave(&usbhid->lock, flags);
|
||||
if (hid->open > 0 &&
|
||||
!test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
|
||||
!test_bit(HID_REPORTED_IDLE, &usbhid->iofl) &&
|
||||
!test_bit(HID_SUSPENDED, &usbhid->iofl) &&
|
||||
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
|
||||
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
|
||||
if (rc != 0) {
|
||||
@ -207,15 +207,27 @@ static int usbhid_restart_out_queue(struct usbhid_device *usbhid)
|
||||
int kicked;
|
||||
int r;
|
||||
|
||||
if (!hid)
|
||||
if (!hid || test_bit(HID_RESET_PENDING, &usbhid->iofl) ||
|
||||
test_bit(HID_SUSPENDED, &usbhid->iofl))
|
||||
return 0;
|
||||
|
||||
if ((kicked = (usbhid->outhead != usbhid->outtail))) {
|
||||
hid_dbg(hid, "Kicking head %d tail %d", usbhid->outhead, usbhid->outtail);
|
||||
|
||||
/* Try to wake up from autosuspend... */
|
||||
r = usb_autopm_get_interface_async(usbhid->intf);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/*
|
||||
* If still suspended, don't submit. Submission will
|
||||
* occur if/when resume drains the queue.
|
||||
*/
|
||||
if (test_bit(HID_SUSPENDED, &usbhid->iofl)) {
|
||||
usb_autopm_put_interface_no_suspend(usbhid->intf);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Asynchronously flush queue. */
|
||||
set_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||
if (hid_submit_out(hid)) {
|
||||
@ -234,15 +246,27 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
|
||||
int r;
|
||||
|
||||
WARN_ON(hid == NULL);
|
||||
if (!hid)
|
||||
if (!hid || test_bit(HID_RESET_PENDING, &usbhid->iofl) ||
|
||||
test_bit(HID_SUSPENDED, &usbhid->iofl))
|
||||
return 0;
|
||||
|
||||
if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) {
|
||||
hid_dbg(hid, "Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail);
|
||||
|
||||
/* Try to wake up from autosuspend... */
|
||||
r = usb_autopm_get_interface_async(usbhid->intf);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/*
|
||||
* If still suspended, don't submit. Submission will
|
||||
* occur if/when resume drains the queue.
|
||||
*/
|
||||
if (test_bit(HID_SUSPENDED, &usbhid->iofl)) {
|
||||
usb_autopm_put_interface_no_suspend(usbhid->intf);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Asynchronously flush queue. */
|
||||
set_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||
if (hid_submit_ctrl(hid)) {
|
||||
@ -331,9 +355,12 @@ static int hid_submit_out(struct hid_device *hid)
|
||||
usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) +
|
||||
1 + (report->id > 0);
|
||||
usbhid->urbout->dev = hid_to_usb_dev(hid);
|
||||
memcpy(usbhid->outbuf, raw_report,
|
||||
usbhid->urbout->transfer_buffer_length);
|
||||
kfree(raw_report);
|
||||
if (raw_report) {
|
||||
memcpy(usbhid->outbuf, raw_report,
|
||||
usbhid->urbout->transfer_buffer_length);
|
||||
kfree(raw_report);
|
||||
usbhid->out[usbhid->outtail].raw_report = NULL;
|
||||
}
|
||||
|
||||
dbg_hid("submitting out urb\n");
|
||||
|
||||
@ -362,8 +389,11 @@ static int hid_submit_ctrl(struct hid_device *hid)
|
||||
if (dir == USB_DIR_OUT) {
|
||||
usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
|
||||
usbhid->urbctrl->transfer_buffer_length = len;
|
||||
memcpy(usbhid->ctrlbuf, raw_report, len);
|
||||
kfree(raw_report);
|
||||
if (raw_report) {
|
||||
memcpy(usbhid->ctrlbuf, raw_report, len);
|
||||
kfree(raw_report);
|
||||
usbhid->ctrl[usbhid->ctrltail].raw_report = NULL;
|
||||
}
|
||||
} else {
|
||||
int maxpacket, padlen;
|
||||
|
||||
@ -407,16 +437,6 @@ static int hid_submit_ctrl(struct hid_device *hid)
|
||||
* Output interrupt completion handler.
|
||||
*/
|
||||
|
||||
static int irq_out_pump_restart(struct hid_device *hid)
|
||||
{
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
|
||||
if (usbhid->outhead != usbhid->outtail)
|
||||
return hid_submit_out(hid);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void hid_irq_out(struct urb *urb)
|
||||
{
|
||||
struct hid_device *hid = urb->context;
|
||||
@ -441,15 +461,17 @@ static void hid_irq_out(struct urb *urb)
|
||||
|
||||
spin_lock_irqsave(&usbhid->lock, flags);
|
||||
|
||||
if (unplug)
|
||||
if (unplug) {
|
||||
usbhid->outtail = usbhid->outhead;
|
||||
else
|
||||
} else {
|
||||
usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
|
||||
|
||||
if (!irq_out_pump_restart(hid)) {
|
||||
/* Successfully submitted next urb in queue */
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
return;
|
||||
if (usbhid->outhead != usbhid->outtail &&
|
||||
hid_submit_out(hid) == 0) {
|
||||
/* Successfully submitted next urb in queue */
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||
@ -461,15 +483,6 @@ static void hid_irq_out(struct urb *urb)
|
||||
/*
|
||||
* Control pipe completion handler.
|
||||
*/
|
||||
static int ctrl_pump_restart(struct hid_device *hid)
|
||||
{
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
|
||||
if (usbhid->ctrlhead != usbhid->ctrltail)
|
||||
return hid_submit_ctrl(hid);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void hid_ctrl(struct urb *urb)
|
||||
{
|
||||
@ -498,15 +511,17 @@ static void hid_ctrl(struct urb *urb)
|
||||
hid_warn(urb->dev, "ctrl urb status %d received\n", status);
|
||||
}
|
||||
|
||||
if (unplug)
|
||||
if (unplug) {
|
||||
usbhid->ctrltail = usbhid->ctrlhead;
|
||||
else
|
||||
} else {
|
||||
usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
|
||||
|
||||
if (!ctrl_pump_restart(hid)) {
|
||||
/* Successfully submitted next urb in queue */
|
||||
spin_unlock(&usbhid->lock);
|
||||
return;
|
||||
if (usbhid->ctrlhead != usbhid->ctrltail &&
|
||||
hid_submit_ctrl(hid) == 0) {
|
||||
/* Successfully submitted next urb in queue */
|
||||
spin_unlock(&usbhid->lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||
@ -540,49 +555,36 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
|
||||
usbhid->out[usbhid->outhead].report = report;
|
||||
usbhid->outhead = head;
|
||||
|
||||
/* Try to awake from autosuspend... */
|
||||
if (usb_autopm_get_interface_async(usbhid->intf) < 0)
|
||||
return;
|
||||
/* If the queue isn't running, restart it */
|
||||
if (!test_bit(HID_OUT_RUNNING, &usbhid->iofl)) {
|
||||
usbhid_restart_out_queue(usbhid);
|
||||
|
||||
/*
|
||||
* But if still suspended, leave urb enqueued, don't submit.
|
||||
* Submission will occur if/when resume() drains the queue.
|
||||
*/
|
||||
if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl))
|
||||
return;
|
||||
/* Otherwise see if an earlier request has timed out */
|
||||
} else if (time_after(jiffies, usbhid->last_out + HZ * 5)) {
|
||||
|
||||
/* Prevent autosuspend following the unlink */
|
||||
usb_autopm_get_interface_no_resume(usbhid->intf);
|
||||
|
||||
if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) {
|
||||
if (hid_submit_out(hid)) {
|
||||
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||
usb_autopm_put_interface_async(usbhid->intf);
|
||||
}
|
||||
wake_up(&usbhid->wait);
|
||||
} else {
|
||||
/*
|
||||
* the queue is known to run
|
||||
* but an earlier request may be stuck
|
||||
* we may need to time out
|
||||
* no race because the URB is blocked under
|
||||
* spinlock
|
||||
* Prevent resubmission in case the URB completes
|
||||
* before we can unlink it. We don't want to cancel
|
||||
* the wrong transfer!
|
||||
*/
|
||||
if (time_after(jiffies, usbhid->last_out + HZ * 5)) {
|
||||
usb_block_urb(usbhid->urbout);
|
||||
/* drop lock to not deadlock if the callback is called */
|
||||
spin_unlock(&usbhid->lock);
|
||||
usb_unlink_urb(usbhid->urbout);
|
||||
spin_lock(&usbhid->lock);
|
||||
usb_unblock_urb(usbhid->urbout);
|
||||
/*
|
||||
* if the unlinking has already completed
|
||||
* the pump will have been stopped
|
||||
* it must be restarted now
|
||||
*/
|
||||
if (!test_bit(HID_OUT_RUNNING, &usbhid->iofl))
|
||||
if (!irq_out_pump_restart(hid))
|
||||
set_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||
usb_block_urb(usbhid->urbout);
|
||||
|
||||
/* Drop lock to avoid deadlock if the callback runs */
|
||||
spin_unlock(&usbhid->lock);
|
||||
|
||||
}
|
||||
usb_unlink_urb(usbhid->urbout);
|
||||
spin_lock(&usbhid->lock);
|
||||
usb_unblock_urb(usbhid->urbout);
|
||||
|
||||
/* Unlink might have stopped the queue */
|
||||
if (!test_bit(HID_OUT_RUNNING, &usbhid->iofl))
|
||||
usbhid_restart_out_queue(usbhid);
|
||||
|
||||
/* Now we can allow autosuspend again */
|
||||
usb_autopm_put_interface_async(usbhid->intf);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -604,47 +606,36 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
|
||||
usbhid->ctrl[usbhid->ctrlhead].dir = dir;
|
||||
usbhid->ctrlhead = head;
|
||||
|
||||
/* Try to awake from autosuspend... */
|
||||
if (usb_autopm_get_interface_async(usbhid->intf) < 0)
|
||||
return;
|
||||
/* If the queue isn't running, restart it */
|
||||
if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl)) {
|
||||
usbhid_restart_ctrl_queue(usbhid);
|
||||
|
||||
/*
|
||||
* If already suspended, leave urb enqueued, but don't submit.
|
||||
* Submission will occur if/when resume() drains the queue.
|
||||
*/
|
||||
if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl))
|
||||
return;
|
||||
/* Otherwise see if an earlier request has timed out */
|
||||
} else if (time_after(jiffies, usbhid->last_ctrl + HZ * 5)) {
|
||||
|
||||
/* Prevent autosuspend following the unlink */
|
||||
usb_autopm_get_interface_no_resume(usbhid->intf);
|
||||
|
||||
if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) {
|
||||
if (hid_submit_ctrl(hid)) {
|
||||
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||
usb_autopm_put_interface_async(usbhid->intf);
|
||||
}
|
||||
wake_up(&usbhid->wait);
|
||||
} else {
|
||||
/*
|
||||
* the queue is known to run
|
||||
* but an earlier request may be stuck
|
||||
* we may need to time out
|
||||
* no race because the URB is blocked under
|
||||
* spinlock
|
||||
* Prevent resubmission in case the URB completes
|
||||
* before we can unlink it. We don't want to cancel
|
||||
* the wrong transfer!
|
||||
*/
|
||||
if (time_after(jiffies, usbhid->last_ctrl + HZ * 5)) {
|
||||
usb_block_urb(usbhid->urbctrl);
|
||||
/* drop lock to not deadlock if the callback is called */
|
||||
spin_unlock(&usbhid->lock);
|
||||
usb_unlink_urb(usbhid->urbctrl);
|
||||
spin_lock(&usbhid->lock);
|
||||
usb_unblock_urb(usbhid->urbctrl);
|
||||
/*
|
||||
* if the unlinking has already completed
|
||||
* the pump will have been stopped
|
||||
* it must be restarted now
|
||||
*/
|
||||
if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
|
||||
if (!ctrl_pump_restart(hid))
|
||||
set_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||
}
|
||||
usb_block_urb(usbhid->urbctrl);
|
||||
|
||||
/* Drop lock to avoid deadlock if the callback runs */
|
||||
spin_unlock(&usbhid->lock);
|
||||
|
||||
usb_unlink_urb(usbhid->urbctrl);
|
||||
spin_lock(&usbhid->lock);
|
||||
usb_unblock_urb(usbhid->urbctrl);
|
||||
|
||||
/* Unlink might have stopped the queue */
|
||||
if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
|
||||
usbhid_restart_ctrl_queue(usbhid);
|
||||
|
||||
/* Now we can allow autosuspend again */
|
||||
usb_autopm_put_interface_async(usbhid->intf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1002,9 +993,10 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co
|
||||
|
||||
static void usbhid_restart_queues(struct usbhid_device *usbhid)
|
||||
{
|
||||
if (usbhid->urbout)
|
||||
if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
|
||||
usbhid_restart_out_queue(usbhid);
|
||||
usbhid_restart_ctrl_queue(usbhid);
|
||||
if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
|
||||
usbhid_restart_ctrl_queue(usbhid);
|
||||
}
|
||||
|
||||
static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
|
||||
@ -1471,11 +1463,38 @@ void usbhid_put_power(struct hid_device *hid)
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int hid_resume_common(struct hid_device *hid, bool driver_suspended)
|
||||
{
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
int status;
|
||||
|
||||
spin_lock_irq(&usbhid->lock);
|
||||
clear_bit(HID_SUSPENDED, &usbhid->iofl);
|
||||
usbhid_mark_busy(usbhid);
|
||||
|
||||
if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) ||
|
||||
test_bit(HID_RESET_PENDING, &usbhid->iofl))
|
||||
schedule_work(&usbhid->reset_work);
|
||||
usbhid->retry_delay = 0;
|
||||
|
||||
usbhid_restart_queues(usbhid);
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
|
||||
status = hid_start_in(hid);
|
||||
if (status < 0)
|
||||
hid_io_error(hid);
|
||||
|
||||
if (driver_suspended && hid->driver && hid->driver->resume)
|
||||
status = hid->driver->resume(hid);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct hid_device *hid = usb_get_intfdata(intf);
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
int status;
|
||||
bool driver_suspended = false;
|
||||
|
||||
if (PMSG_IS_AUTO(message)) {
|
||||
spin_lock_irq(&usbhid->lock); /* Sync with error handler */
|
||||
@ -1486,13 +1505,14 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
&& !test_bit(HID_KEYS_PRESSED, &usbhid->iofl)
|
||||
&& (!usbhid->ledcount || ignoreled))
|
||||
{
|
||||
set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
|
||||
set_bit(HID_SUSPENDED, &usbhid->iofl);
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
if (hid->driver && hid->driver->suspend) {
|
||||
status = hid->driver->suspend(hid, message);
|
||||
if (status < 0)
|
||||
return status;
|
||||
goto failed;
|
||||
}
|
||||
driver_suspended = true;
|
||||
} else {
|
||||
usbhid_mark_busy(usbhid);
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
@ -1505,11 +1525,14 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
driver_suspended = true;
|
||||
spin_lock_irq(&usbhid->lock);
|
||||
set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
|
||||
set_bit(HID_SUSPENDED, &usbhid->iofl);
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
if (usbhid_wait_io(hid) < 0)
|
||||
return -EIO;
|
||||
if (usbhid_wait_io(hid) < 0) {
|
||||
status = -EIO;
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
hid_cancel_delayed_stuff(usbhid);
|
||||
@ -1517,14 +1540,15 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
|
||||
if (PMSG_IS_AUTO(message) && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
|
||||
/* lost race against keypresses */
|
||||
status = hid_start_in(hid);
|
||||
if (status < 0)
|
||||
hid_io_error(hid);
|
||||
usbhid_mark_busy(usbhid);
|
||||
return -EBUSY;
|
||||
status = -EBUSY;
|
||||
goto failed;
|
||||
}
|
||||
dev_dbg(&intf->dev, "suspend\n");
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
hid_resume_common(hid, driver_suspended);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int hid_resume(struct usb_interface *intf)
|
||||
@ -1536,23 +1560,7 @@ static int hid_resume(struct usb_interface *intf)
|
||||
if (!test_bit(HID_STARTED, &usbhid->iofl))
|
||||
return 0;
|
||||
|
||||
clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
|
||||
usbhid_mark_busy(usbhid);
|
||||
|
||||
if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) ||
|
||||
test_bit(HID_RESET_PENDING, &usbhid->iofl))
|
||||
schedule_work(&usbhid->reset_work);
|
||||
usbhid->retry_delay = 0;
|
||||
status = hid_start_in(hid);
|
||||
if (status < 0)
|
||||
hid_io_error(hid);
|
||||
usbhid_restart_queues(usbhid);
|
||||
|
||||
if (status >= 0 && hid->driver && hid->driver->resume) {
|
||||
int ret = hid->driver->resume(hid);
|
||||
if (ret < 0)
|
||||
status = ret;
|
||||
}
|
||||
status = hid_resume_common(hid, true);
|
||||
dev_dbg(&intf->dev, "resume status %d\n", status);
|
||||
return 0;
|
||||
}
|
||||
@ -1563,7 +1571,7 @@ static int hid_reset_resume(struct usb_interface *intf)
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
int status;
|
||||
|
||||
clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
|
||||
clear_bit(HID_SUSPENDED, &usbhid->iofl);
|
||||
status = hid_post_reset(intf);
|
||||
if (status >= 0 && hid->driver && hid->driver->reset_resume) {
|
||||
int ret = hid->driver->reset_resume(hid);
|
||||
|
@ -53,7 +53,6 @@ struct usb_interface *usbhid_find_interface(int minor);
|
||||
#define HID_CLEAR_HALT 6
|
||||
#define HID_DISCONNECTED 7
|
||||
#define HID_STARTED 8
|
||||
#define HID_REPORTED_IDLE 9
|
||||
#define HID_KEYS_PRESSED 10
|
||||
#define HID_NO_BANDWIDTH 11
|
||||
|
||||
|
@ -200,6 +200,7 @@ struct hid_item {
|
||||
#define HID_UP_DIGITIZER 0x000d0000
|
||||
#define HID_UP_PID 0x000f0000
|
||||
#define HID_UP_HPVENDOR 0xff7f0000
|
||||
#define HID_UP_HPVENDOR2 0xff010000
|
||||
#define HID_UP_MSVENDOR 0xff000000
|
||||
#define HID_UP_CUSTOM 0x00ff0000
|
||||
#define HID_UP_LOGIVENDOR 0xffbc0000
|
||||
|
Loading…
Reference in New Issue
Block a user