Merge branches 'hidraw', 'magicmouse', 'multitouch', 'roccat', 'suspend-fixes' and 'upstream' into for-linus
This commit is contained in:
commit
929bd380b7
32 changed files with 1693 additions and 546 deletions
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.
|
Support for Ezkey BTC 8193 keyboard.
|
||||||
|
|
||||||
config HID_HOLTEK
|
config HID_HOLTEK
|
||||||
tristate "Holtek On Line Grip based game controller support"
|
tristate "Holtek HID devices"
|
||||||
depends on USB_HID
|
depends on USB_HID
|
||||||
---help---
|
---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
|
config HOLTEK_FF
|
||||||
bool "Holtek On Line Grip force feedback support"
|
bool "Holtek On Line Grip force feedback support"
|
||||||
|
@ -261,6 +263,19 @@ config HID_LCPOWER
|
||||||
---help---
|
---help---
|
||||||
Support for LC-Power RC1000MCE RF remote control.
|
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
|
config HID_LOGITECH
|
||||||
tristate "Logitech devices" if EXPERT
|
tristate "Logitech devices" if EXPERT
|
||||||
depends on USB_HID
|
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_ELECOM) += hid-elecom.o
|
||||||
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
|
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
|
||||||
obj-$(CONFIG_HID_GYRATION) += hid-gyration.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_HOLTEK) += hid-holtekff.o
|
||||||
obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o
|
obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o
|
||||||
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
|
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
|
||||||
obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
|
obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
|
||||||
obj-$(CONFIG_HID_KYE) += hid-kye.o
|
obj-$(CONFIG_HID_KYE) += hid-kye.o
|
||||||
obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.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) += hid-logitech.o
|
||||||
obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
|
obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
|
||||||
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.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_PRIMAX) += hid-primax.o
|
||||||
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.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-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_SAITEK) += hid-saitek.o
|
||||||
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
|
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
|
||||||
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.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[] = {
|
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_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_WIRELESS2) },
|
||||||
|
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(hid, ch_devices);
|
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))
|
if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev))
|
||||||
hdev->claimed |= HID_CLAIMED_HIDRAW;
|
hdev->claimed |= HID_CLAIMED_HIDRAW;
|
||||||
|
|
||||||
if (!hdev->claimed) {
|
/* Drivers with the ->raw_event callback set are not required to connect
|
||||||
hid_err(hdev, "claimed by neither input, hiddev nor hidraw\n");
|
* to any other listener. */
|
||||||
|
if (!hdev->claimed && !hdev->driver->raw_event) {
|
||||||
|
hid_err(hdev, "device has no listeners, quitting\n");
|
||||||
return -ENODEV;
|
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_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_WIRELESS) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) },
|
{ 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_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_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_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_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_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
|
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
|
{ 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_2) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
|
{ 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, 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_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },
|
{ 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) },
|
{ 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_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_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_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_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) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
|
{ 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_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_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_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_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_IR_REMOTE) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
|
{ 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 },
|
.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
|
||||||
{ 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_3),
|
||||||
.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
|
.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),
|
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE),
|
||||||
.driver_data = CP_2WHEEL_MOUSE_HACK },
|
.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_MULTI_TOUCH 0xb19d
|
||||||
#define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618
|
#define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618
|
||||||
#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123
|
#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123
|
||||||
|
#define USB_DEVICE_ID_CHICONY_AK1D 0x1125
|
||||||
|
|
||||||
#define USB_VENDOR_ID_CHUNGHWAT 0x2247
|
#define USB_VENDOR_ID_CHUNGHWAT 0x2247
|
||||||
#define USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH 0x0001
|
#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_1 0xde61
|
||||||
#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64
|
#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64
|
||||||
#define USB_DEVICE_ID_CYPRESS_BARCODE_3 0xbca1
|
#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_DEVICE_ID_CYPRESS_TRUETOUCH 0xc001
|
||||||
|
|
||||||
#define USB_VENDOR_ID_DEALEXTREAME 0x10c5
|
#define USB_VENDOR_ID_DEALEXTREAME 0x10c5
|
||||||
|
@ -410,6 +412,9 @@
|
||||||
#define USB_VENDOR_ID_HOLTEK 0x1241
|
#define USB_VENDOR_ID_HOLTEK 0x1241
|
||||||
#define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP 0x5015
|
#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_VENDOR_ID_IMATION 0x0718
|
||||||
#define USB_DEVICE_ID_DISC_STAKKA 0xd000
|
#define USB_DEVICE_ID_DISC_STAKKA 0xd000
|
||||||
|
|
||||||
|
@ -479,6 +484,9 @@
|
||||||
#define USB_DEVICE_ID_LD_HYBRID 0x2090
|
#define USB_DEVICE_ID_LD_HYBRID 0x2090
|
||||||
#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0
|
#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_VENDOR_ID_LG 0x1fd2
|
||||||
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
|
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
|
||||||
|
|
||||||
|
@ -573,6 +581,9 @@
|
||||||
#define USB_VENDOR_ID_NINTENDO 0x057e
|
#define USB_VENDOR_ID_NINTENDO 0x057e
|
||||||
#define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306
|
#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_VENDOR_ID_NTRIG 0x1b96
|
||||||
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
|
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
|
||||||
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1 0x0003
|
#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_KOVAPLUS 0x2d50
|
||||||
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24
|
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24
|
||||||
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6
|
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6
|
||||||
|
#define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a
|
||||||
|
|
||||||
#define USB_VENDOR_ID_SAITEK 0x06a3
|
#define USB_VENDOR_ID_SAITEK 0x06a3
|
||||||
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
|
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
|
||||||
|
|
|
@ -837,6 +837,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case HID_UP_MSVENDOR:
|
||||||
goto ignore;
|
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/device.h>
|
||||||
#include <linux/hid.h>
|
#include <linux/hid.h>
|
||||||
|
#include <linux/input/mt.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
|
@ -48,10 +49,6 @@ static bool scroll_acceleration = false;
|
||||||
module_param(scroll_acceleration, bool, 0644);
|
module_param(scroll_acceleration, bool, 0644);
|
||||||
MODULE_PARM_DESC(scroll_acceleration, "Accelerate sequential scroll events");
|
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;
|
static bool report_undeciphered;
|
||||||
module_param(report_undeciphered, bool, 0644);
|
module_param(report_undeciphered, bool, 0644);
|
||||||
MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
|
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
|
#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
|
/* Touch surface information. Dimension is in hundredths of a mm, min and max
|
||||||
* are in units. */
|
* are in units. */
|
||||||
#define MOUSE_DIMENSION_X (float)9056
|
#define MOUSE_DIMENSION_X (float)9056
|
||||||
|
@ -129,7 +117,6 @@ struct magicmouse_sc {
|
||||||
u8 size;
|
u8 size;
|
||||||
} touches[16];
|
} touches[16];
|
||||||
int tracking_ids[16];
|
int tracking_ids[16];
|
||||||
int single_touch_id;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int magicmouse_firm_touch(struct magicmouse_sc *msc)
|
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++;
|
msc->ntouches++;
|
||||||
if (msc->single_touch_id == NO_TOUCHES)
|
|
||||||
msc->single_touch_id = id;
|
input_mt_slot(input, id);
|
||||||
} else if (msc->single_touch_id == id)
|
input_mt_report_slot_state(input, MT_TOOL_FINGER, down);
|
||||||
msc->single_touch_id = SINGLE_TOUCH_UP;
|
|
||||||
|
|
||||||
/* Generate the input events for this touch. */
|
/* Generate the input events for this touch. */
|
||||||
if (report_touches && down) {
|
if (down) {
|
||||||
input_report_abs(input, ABS_MT_TRACKING_ID, id);
|
|
||||||
input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2);
|
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_TOUCH_MINOR, touch_minor << 2);
|
||||||
input_report_abs(input, ABS_MT_ORIENTATION, -orientation);
|
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 */
|
else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||||
input_event(input, EV_MSC, MSC_RAW, tdata[8]);
|
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++)
|
for (ii = 0; ii < npoints; ii++)
|
||||||
magicmouse_emit_touch(msc, ii, data + ii * 9 + 4);
|
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];
|
clicks = data[1];
|
||||||
|
|
||||||
/* The following bits provide a device specific timestamp. They
|
/* 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++)
|
for (ii = 0; ii < npoints; ii++)
|
||||||
magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);
|
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
|
/* When emulating three-button mode, it is important
|
||||||
* to have the current touch information before
|
* to have the current touch information before
|
||||||
* generating a click event.
|
* generating a click event.
|
||||||
|
@ -370,25 +344,17 @@ static int magicmouse_raw_event(struct hid_device *hdev,
|
||||||
input_report_rel(input, REL_Y, y);
|
input_report_rel(input, REL_Y, y);
|
||||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||||
input_report_key(input, BTN_MOUSE, clicks & 1);
|
input_report_key(input, BTN_MOUSE, clicks & 1);
|
||||||
input_report_key(input, BTN_TOUCH, msc->ntouches > 0);
|
input_mt_report_pointer_emulation(input, true);
|
||||||
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_sync(input);
|
input_sync(input);
|
||||||
return 1;
|
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);
|
__set_bit(EV_KEY, input->evbit);
|
||||||
|
|
||||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
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_DOUBLETAP, input->keybit);
|
||||||
__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
|
__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
|
||||||
__set_bit(BTN_TOOL_QUADTAP, input->keybit);
|
__set_bit(BTN_TOOL_QUADTAP, input->keybit);
|
||||||
|
__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
|
||||||
__set_bit(BTN_TOUCH, input->keybit);
|
__set_bit(BTN_TOUCH, input->keybit);
|
||||||
__set_bit(INPUT_PROP_POINTER, input->propbit);
|
__set_bit(INPUT_PROP_POINTER, input->propbit);
|
||||||
__set_bit(INPUT_PROP_BUTTONPAD, 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);
|
__set_bit(EV_ABS, input->evbit);
|
||||||
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);
|
|
||||||
|
|
||||||
/* Note: Touch Y position from the device is inverted relative
|
error = input_mt_init_slots(input, 16);
|
||||||
* to how pointer motion is reported (and relative to how USB
|
if (error)
|
||||||
* HID recommends the coordinates work). This driver keeps
|
return error;
|
||||||
* the origin at the same position, and just uses the additive
|
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
|
||||||
* inverse of the reported Y.
|
4, 0);
|
||||||
*/
|
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2,
|
||||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
4, 0);
|
||||||
input_set_abs_params(input, ABS_MT_POSITION_X,
|
input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
|
||||||
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_MT_POSITION_X,
|
/* Note: Touch Y position from the device is inverted relative
|
||||||
MOUSE_RES_X);
|
* to how pointer motion is reported (and relative to how USB
|
||||||
input_abs_set_res(input, ABS_MT_POSITION_Y,
|
* HID recommends the coordinates work). This driver keeps
|
||||||
MOUSE_RES_Y);
|
* the origin at the same position, and just uses the additive
|
||||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
* inverse of the reported Y.
|
||||||
input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X,
|
*/
|
||||||
TRACKPAD_MAX_X, 4, 0);
|
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
||||||
input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y,
|
input_set_abs_params(input, ABS_MT_POSITION_X,
|
||||||
TRACKPAD_MAX_Y, 4, 0);
|
MOUSE_MIN_X, MOUSE_MAX_X, 4, 0);
|
||||||
input_set_abs_params(input, ABS_MT_POSITION_X,
|
input_set_abs_params(input, ABS_MT_POSITION_Y,
|
||||||
TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0);
|
MOUSE_MIN_Y, MOUSE_MAX_Y, 4, 0);
|
||||||
input_set_abs_params(input, ABS_MT_POSITION_Y,
|
|
||||||
TRACKPAD_MIN_Y, TRACKPAD_MAX_Y, 4, 0);
|
|
||||||
|
|
||||||
input_abs_set_res(input, ABS_X, TRACKPAD_RES_X);
|
input_abs_set_res(input, ABS_MT_POSITION_X,
|
||||||
input_abs_set_res(input, ABS_Y, TRACKPAD_RES_Y);
|
MOUSE_RES_X);
|
||||||
input_abs_set_res(input, ABS_MT_POSITION_X,
|
input_abs_set_res(input, ABS_MT_POSITION_Y,
|
||||||
TRACKPAD_RES_X);
|
MOUSE_RES_Y);
|
||||||
input_abs_set_res(input, ABS_MT_POSITION_Y,
|
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||||
TRACKPAD_RES_Y);
|
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) {
|
if (report_undeciphered) {
|
||||||
__set_bit(EV_MSC, input->evbit);
|
__set_bit(EV_MSC, input->evbit);
|
||||||
__set_bit(MSC_RAW, input->mscbit);
|
__set_bit(MSC_RAW, input->mscbit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int magicmouse_input_mapping(struct hid_device *hdev,
|
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;
|
msc->quirks = id->driver_data;
|
||||||
hid_set_drvdata(hdev, msc);
|
hid_set_drvdata(hdev, msc);
|
||||||
|
|
||||||
msc->single_touch_id = NO_TOUCHES;
|
|
||||||
|
|
||||||
ret = hid_parse(hdev);
|
ret = hid_parse(hdev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
hid_err(hdev, "magicmouse hid parse failed\n");
|
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
|
/* We do this after hid-input is done parsing reports so that
|
||||||
* hid-input uses the most natural button and axis IDs.
|
* hid-input uses the most natural button and axis IDs.
|
||||||
*/
|
*/
|
||||||
if (msc->input)
|
if (msc->input) {
|
||||||
magicmouse_setup_input(msc->input, hdev);
|
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)
|
if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
|
||||||
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
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_field_index; /* last field index of the report */
|
||||||
unsigned last_slot_field; /* the last field of a slot */
|
unsigned last_slot_field; /* the last field of a slot */
|
||||||
__s8 inputmode; /* InputMode HID feature, -1 if non-existent */
|
__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,
|
__s8 maxcontact_report_id; /* Maximum Contact Number HID feature,
|
||||||
-1 if non-existent */
|
-1 if non-existent */
|
||||||
__u8 num_received; /* how many contacts we received */
|
__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 hid_field *field, struct hid_usage *usage)
|
||||||
{
|
{
|
||||||
struct mt_device *td = hid_get_drvdata(hdev);
|
struct mt_device *td = hid_get_drvdata(hdev);
|
||||||
|
int i;
|
||||||
|
|
||||||
switch (usage->hid) {
|
switch (usage->hid) {
|
||||||
case HID_DG_INPUTMODE:
|
case HID_DG_INPUTMODE:
|
||||||
td->inputmode = field->report->id;
|
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;
|
break;
|
||||||
case HID_DG_CONTACTMAX:
|
case HID_DG_CONTACTMAX:
|
||||||
td->maxcontact_report_id = field->report->id;
|
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]);
|
re = &(hdev->report_enum[HID_FEATURE_REPORT]);
|
||||||
r = re->report_id_hash[td->inputmode];
|
r = re->report_id_hash[td->inputmode];
|
||||||
if (r) {
|
if (r) {
|
||||||
r->field[0]->value[0] = 0x02;
|
r->field[0]->value[td->inputmode_index] = 0x02;
|
||||||
usbhid_submit_report(hdev, r, USB_DIR_OUT);
|
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,
|
MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC,
|
||||||
USB_DEVICE_ID_PANABOARD_UBT880) },
|
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 */
|
/* PenMount panels */
|
||||||
{ .driver_data = MT_CLS_CONFIDENCE,
|
{ .driver_data = MT_CLS_CONFIDENCE,
|
||||||
MT_USB_DEVICE(USB_VENDOR_ID_PENMOUNT,
|
MT_USB_DEVICE(USB_VENDOR_ID_PENMOUNT,
|
||||||
|
|
|
@ -2613,11 +2613,7 @@ static int picolcd_probe(struct hid_device *hdev,
|
||||||
goto err_cleanup_data;
|
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);
|
error = hid_hw_start(hdev, 0);
|
||||||
hdev->claimed = 0;
|
|
||||||
if (error) {
|
if (error) {
|
||||||
hid_err(hdev, "hardware start failed\n");
|
hid_err(hdev, "hardware start failed\n");
|
||||||
goto err_cleanup_data;
|
goto err_cleanup_data;
|
||||||
|
|
|
@ -39,7 +39,7 @@ static ssize_t arvo_sysfs_show_mode_key(struct device *dev,
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
mutex_lock(&arvo->arvo_lock);
|
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));
|
&temp_buf, sizeof(struct arvo_mode_key));
|
||||||
mutex_unlock(&arvo->arvo_lock);
|
mutex_unlock(&arvo->arvo_lock);
|
||||||
if (retval)
|
if (retval)
|
||||||
|
@ -67,7 +67,7 @@ static ssize_t arvo_sysfs_set_mode_key(struct device *dev,
|
||||||
temp_buf.state = state;
|
temp_buf.state = state;
|
||||||
|
|
||||||
mutex_lock(&arvo->arvo_lock);
|
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));
|
&temp_buf, sizeof(struct arvo_mode_key));
|
||||||
mutex_unlock(&arvo->arvo_lock);
|
mutex_unlock(&arvo->arvo_lock);
|
||||||
if (retval)
|
if (retval)
|
||||||
|
@ -87,7 +87,7 @@ static ssize_t arvo_sysfs_show_key_mask(struct device *dev,
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
mutex_lock(&arvo->arvo_lock);
|
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));
|
&temp_buf, sizeof(struct arvo_key_mask));
|
||||||
mutex_unlock(&arvo->arvo_lock);
|
mutex_unlock(&arvo->arvo_lock);
|
||||||
if (retval)
|
if (retval)
|
||||||
|
@ -115,7 +115,7 @@ static ssize_t arvo_sysfs_set_key_mask(struct device *dev,
|
||||||
temp_buf.key_mask = key_mask;
|
temp_buf.key_mask = key_mask;
|
||||||
|
|
||||||
mutex_lock(&arvo->arvo_lock);
|
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));
|
&temp_buf, sizeof(struct arvo_key_mask));
|
||||||
mutex_unlock(&arvo->arvo_lock);
|
mutex_unlock(&arvo->arvo_lock);
|
||||||
if (retval)
|
if (retval)
|
||||||
|
@ -130,7 +130,7 @@ static int arvo_get_actual_profile(struct usb_device *usb_dev)
|
||||||
struct arvo_actual_profile temp_buf;
|
struct arvo_actual_profile temp_buf;
|
||||||
int retval;
|
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));
|
&temp_buf, sizeof(struct arvo_actual_profile));
|
||||||
|
|
||||||
if (retval)
|
if (retval)
|
||||||
|
@ -170,7 +170,7 @@ static ssize_t arvo_sysfs_set_actual_profile(struct device *dev,
|
||||||
temp_buf.actual_profile = profile;
|
temp_buf.actual_profile = profile;
|
||||||
|
|
||||||
mutex_lock(&arvo->arvo_lock);
|
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));
|
&temp_buf, sizeof(struct arvo_actual_profile));
|
||||||
if (!retval) {
|
if (!retval) {
|
||||||
arvo->actual_profile = profile;
|
arvo->actual_profile = profile;
|
||||||
|
@ -194,7 +194,7 @@ static ssize_t arvo_sysfs_write(struct file *fp,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&arvo->arvo_lock);
|
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);
|
mutex_unlock(&arvo->arvo_lock);
|
||||||
|
|
||||||
return (retval ? retval : real_size);
|
return (retval ? retval : real_size);
|
||||||
|
@ -217,7 +217,7 @@ static ssize_t arvo_sysfs_read(struct file *fp,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&arvo->arvo_lock);
|
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);
|
mutex_unlock(&arvo->arvo_lock);
|
||||||
|
|
||||||
return (retval ? retval : real_size);
|
return (retval ? retval : real_size);
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include "hid-roccat-common.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;
|
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)
|
void *data, uint size)
|
||||||
{
|
{
|
||||||
char *buf;
|
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),
|
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||||
HID_REQ_GET_REPORT,
|
HID_REQ_GET_REPORT,
|
||||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
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);
|
0, buf, size, USB_CTRL_SET_TIMEOUT);
|
||||||
|
|
||||||
memcpy(data, buf, size);
|
memcpy(data, buf, size);
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
return ((len < 0) ? len : ((len != size) ? -EIO : 0));
|
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)
|
void const *data, uint size)
|
||||||
{
|
{
|
||||||
char *buf;
|
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),
|
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||||
HID_REQ_SET_REPORT,
|
HID_REQ_SET_REPORT,
|
||||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
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);
|
0, buf, size, USB_CTRL_SET_TIMEOUT);
|
||||||
|
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
return ((len < 0) ? len : ((len != size) ? -EIO : 0));
|
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_AUTHOR("Stefan Achatz");
|
||||||
MODULE_DESCRIPTION("USB Roccat common driver");
|
MODULE_DESCRIPTION("USB Roccat common driver");
|
||||||
|
|
|
@ -15,9 +15,21 @@
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/types.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);
|
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);
|
void const *data, uint size);
|
||||||
|
int roccat_common2_send_with_status(struct usb_device *usb_dev,
|
||||||
|
uint command, void const *buf, uint size);
|
||||||
|
|
||||||
#endif
|
#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,
|
static int isku_receive(struct usb_device *usb_dev, uint command,
|
||||||
void *buf, uint size)
|
void *buf, uint size)
|
||||||
{
|
{
|
||||||
return roccat_common_receive(usb_dev, command, buf, size);
|
return roccat_common2_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int isku_get_actual_profile(struct usb_device *usb_dev)
|
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.command = ISKU_COMMAND_ACTUAL_PROFILE;
|
||||||
buf.size = sizeof(struct isku_actual_profile);
|
buf.size = sizeof(struct isku_actual_profile);
|
||||||
buf.actual_profile = new_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));
|
sizeof(struct isku_actual_profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +154,8 @@ static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&isku->isku_lock);
|
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);
|
mutex_unlock(&isku->isku_lock);
|
||||||
|
|
||||||
return retval ? retval : real_size;
|
return retval ? retval : real_size;
|
||||||
|
|
|
@ -25,13 +25,6 @@ struct isku_control {
|
||||||
uint8_t request;
|
uint8_t request;
|
||||||
} __packed;
|
} __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 {
|
struct isku_actual_profile {
|
||||||
uint8_t command; /* ISKU_COMMAND_ACTUAL_PROFILE */
|
uint8_t command; /* ISKU_COMMAND_ACTUAL_PROFILE */
|
||||||
uint8_t size; /* always 3 */
|
uint8_t size; /* always 3 */
|
||||||
|
|
|
@ -138,7 +138,7 @@ static int kone_check_write(struct usb_device *usb_dev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* unknown answer */
|
/* 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;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,7 +503,7 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev,
|
||||||
|
|
||||||
retval = kone_set_settings(usb_dev, &kone->settings);
|
retval = kone_set_settings(usb_dev, &kone->settings);
|
||||||
if (retval) {
|
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
|
* try to reread valid settings into buffer overwriting
|
||||||
* first error code
|
* first error code
|
||||||
|
@ -519,7 +519,7 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev,
|
||||||
|
|
||||||
retval = size;
|
retval = size;
|
||||||
exit_no_settings:
|
exit_no_settings:
|
||||||
hid_err(usb_dev, "couldn't read settings\n");
|
dev_err(&usb_dev->dev, "couldn't read settings\n");
|
||||||
exit_unlock:
|
exit_unlock:
|
||||||
mutex_unlock(&kone->kone_lock);
|
mutex_unlock(&kone->kone_lock);
|
||||||
return retval;
|
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,
|
static int koneplus_send_control(struct usb_device *usb_dev, uint value,
|
||||||
enum koneplus_control_requests request)
|
enum koneplus_control_requests request)
|
||||||
{
|
{
|
||||||
struct koneplus_control control;
|
struct roccat_common2_control control;
|
||||||
|
|
||||||
if ((request == KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
if ((request == KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
||||||
request == KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
request == KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
||||||
value > 4)
|
value > 4)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
control.command = KONEPLUS_COMMAND_CONTROL;
|
control.command = ROCCAT_COMMON_COMMAND_CONTROL;
|
||||||
control.value = value;
|
control.value = value;
|
||||||
control.request = request;
|
control.request = request;
|
||||||
|
|
||||||
return roccat_common_send(usb_dev, KONEPLUS_COMMAND_CONTROL,
|
return roccat_common2_send_with_status(usb_dev,
|
||||||
&control, sizeof(struct koneplus_control));
|
ROCCAT_COMMON_COMMAND_CONTROL,
|
||||||
}
|
&control, sizeof(struct roccat_common2_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int koneplus_get_info(struct usb_device *usb_dev,
|
static int koneplus_get_info(struct usb_device *usb_dev,
|
||||||
struct koneplus_info *buf)
|
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));
|
buf, sizeof(struct koneplus_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,19 +67,20 @@ static int koneplus_get_profile_settings(struct usb_device *usb_dev,
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
retval = koneplus_select_profile(usb_dev, number,
|
retval = koneplus_send_control(usb_dev, number,
|
||||||
KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
|
KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
|
||||||
if (retval)
|
if (retval)
|
||||||
return 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));
|
buf, sizeof(struct koneplus_profile_settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int koneplus_set_profile_settings(struct usb_device *usb_dev,
|
static int koneplus_set_profile_settings(struct usb_device *usb_dev,
|
||||||
struct koneplus_profile_settings const *settings)
|
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));
|
settings, sizeof(struct koneplus_profile_settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,19 +89,20 @@ static int koneplus_get_profile_buttons(struct usb_device *usb_dev,
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
retval = koneplus_select_profile(usb_dev, number,
|
retval = koneplus_send_control(usb_dev, number,
|
||||||
KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
|
KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
|
||||||
if (retval)
|
if (retval)
|
||||||
return 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));
|
buf, sizeof(struct koneplus_profile_buttons));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int koneplus_set_profile_buttons(struct usb_device *usb_dev,
|
static int koneplus_set_profile_buttons(struct usb_device *usb_dev,
|
||||||
struct koneplus_profile_buttons const *buttons)
|
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));
|
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;
|
struct koneplus_actual_profile buf;
|
||||||
int retval;
|
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));
|
&buf, sizeof(struct koneplus_actual_profile));
|
||||||
|
|
||||||
return retval ? retval : buf.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.size = sizeof(struct koneplus_actual_profile);
|
||||||
buf.actual_profile = new_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));
|
&buf, sizeof(struct koneplus_actual_profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +149,7 @@ static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&koneplus->koneplus_lock);
|
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);
|
mutex_unlock(&koneplus->koneplus_lock);
|
||||||
|
|
||||||
if (retval)
|
if (retval)
|
||||||
|
@ -231,7 +172,8 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&koneplus->koneplus_lock);
|
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);
|
mutex_unlock(&koneplus->koneplus_lock);
|
||||||
|
|
||||||
if (retval)
|
if (retval)
|
||||||
|
|
|
@ -20,32 +20,11 @@ struct koneplus_talk {
|
||||||
uint8_t data[14];
|
uint8_t data[14];
|
||||||
} __packed;
|
} __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 {
|
enum koneplus_control_requests {
|
||||||
KONEPLUS_CONTROL_REQUEST_STATUS = 0x00,
|
|
||||||
KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x80,
|
KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x80,
|
||||||
KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS = 0x90,
|
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 {
|
struct koneplus_actual_profile {
|
||||||
uint8_t command; /* KONEPLUS_COMMAND_ACTUAL_PROFILE */
|
uint8_t command; /* KONEPLUS_COMMAND_ACTUAL_PROFILE */
|
||||||
uint8_t size; /* always 3 */
|
uint8_t size; /* always 3 */
|
||||||
|
@ -137,7 +116,6 @@ struct koneplus_tcu_image {
|
||||||
} __attribute__ ((__packed__));
|
} __attribute__ ((__packed__));
|
||||||
|
|
||||||
enum koneplus_commands {
|
enum koneplus_commands {
|
||||||
KONEPLUS_COMMAND_CONTROL = 0x4,
|
|
||||||
KONEPLUS_COMMAND_ACTUAL_PROFILE = 0x5,
|
KONEPLUS_COMMAND_ACTUAL_PROFILE = 0x5,
|
||||||
KONEPLUS_COMMAND_PROFILE_SETTINGS = 0x6,
|
KONEPLUS_COMMAND_PROFILE_SETTINGS = 0x6,
|
||||||
KONEPLUS_COMMAND_PROFILE_BUTTONS = 0x7,
|
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)
|
enum kovaplus_control_requests request)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
struct kovaplus_control control;
|
struct roccat_common2_control control;
|
||||||
|
|
||||||
if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
||||||
request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
||||||
value > 4)
|
value > 4)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
control.command = KOVAPLUS_COMMAND_CONTROL;
|
control.command = ROCCAT_COMMON_COMMAND_CONTROL;
|
||||||
control.value = value;
|
control.value = value;
|
||||||
control.request = request;
|
control.request = request;
|
||||||
|
|
||||||
retval = roccat_common_send(usb_dev, KOVAPLUS_COMMAND_CONTROL,
|
retval = roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
|
||||||
&control, sizeof(struct kovaplus_control));
|
&control, sizeof(struct roccat_common2_control));
|
||||||
|
|
||||||
return retval;
|
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,
|
static int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
|
||||||
enum kovaplus_control_requests request)
|
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,
|
static int kovaplus_get_info(struct usb_device *usb_dev,
|
||||||
struct kovaplus_info *buf)
|
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));
|
buf, sizeof(struct kovaplus_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,14 +87,15 @@ static int kovaplus_get_profile_settings(struct usb_device *usb_dev,
|
||||||
if (retval)
|
if (retval)
|
||||||
return 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));
|
buf, sizeof(struct kovaplus_profile_settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kovaplus_set_profile_settings(struct usb_device *usb_dev,
|
static int kovaplus_set_profile_settings(struct usb_device *usb_dev,
|
||||||
struct kovaplus_profile_settings const *settings)
|
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));
|
settings, sizeof(struct kovaplus_profile_settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,14 +109,15 @@ static int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
|
||||||
if (retval)
|
if (retval)
|
||||||
return 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));
|
buf, sizeof(struct kovaplus_profile_buttons));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kovaplus_set_profile_buttons(struct usb_device *usb_dev,
|
static int kovaplus_set_profile_buttons(struct usb_device *usb_dev,
|
||||||
struct kovaplus_profile_buttons const *buttons)
|
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));
|
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;
|
struct kovaplus_actual_profile buf;
|
||||||
int retval;
|
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));
|
&buf, sizeof(struct kovaplus_actual_profile));
|
||||||
|
|
||||||
return retval ? retval : buf.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.size = sizeof(struct kovaplus_actual_profile);
|
||||||
buf.actual_profile = new_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));
|
&buf, sizeof(struct kovaplus_actual_profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,27 +14,13 @@
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
struct kovaplus_control {
|
|
||||||
uint8_t command; /* KOVAPLUS_COMMAND_CONTROL */
|
|
||||||
uint8_t value;
|
|
||||||
uint8_t request;
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
enum kovaplus_control_requests {
|
enum kovaplus_control_requests {
|
||||||
/* read after write; value = 1 */
|
|
||||||
KOVAPLUS_CONTROL_REQUEST_STATUS = 0x0,
|
|
||||||
/* write; value = profile number range 0-4 */
|
/* write; value = profile number range 0-4 */
|
||||||
KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
|
KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
|
||||||
/* write; value = profile number range 0-4 */
|
/* write; value = profile number range 0-4 */
|
||||||
KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20,
|
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 {
|
struct kovaplus_actual_profile {
|
||||||
uint8_t command; /* KOVAPLUS_COMMAND_ACTUAL_PROFILE */
|
uint8_t command; /* KOVAPLUS_COMMAND_ACTUAL_PROFILE */
|
||||||
uint8_t size; /* always 3 */
|
uint8_t size; /* always 3 */
|
||||||
|
@ -75,7 +61,6 @@ struct kovaplus_a {
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
enum kovaplus_commands {
|
enum kovaplus_commands {
|
||||||
KOVAPLUS_COMMAND_CONTROL = 0x4,
|
|
||||||
KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5,
|
KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5,
|
||||||
KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6,
|
KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6,
|
||||||
KOVAPLUS_COMMAND_PROFILE_BUTTONS = 0x7,
|
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,
|
static int pyra_send_control(struct usb_device *usb_dev, int value,
|
||||||
enum pyra_control_requests request)
|
enum pyra_control_requests request)
|
||||||
{
|
{
|
||||||
struct pyra_control control;
|
struct roccat_common2_control control;
|
||||||
|
|
||||||
if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
||||||
request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
||||||
(value < 0 || value > 4))
|
(value < 0 || value > 4))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
control.command = PYRA_COMMAND_CONTROL;
|
control.command = ROCCAT_COMMON_COMMAND_CONTROL;
|
||||||
control.value = value;
|
control.value = value;
|
||||||
control.request = request;
|
control.request = request;
|
||||||
|
|
||||||
return roccat_common_send(usb_dev, PYRA_COMMAND_CONTROL,
|
return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
|
||||||
&control, sizeof(struct pyra_control));
|
&control, sizeof(struct roccat_common2_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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pyra_get_profile_settings(struct usb_device *usb_dev,
|
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);
|
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
|
||||||
if (retval)
|
if (retval)
|
||||||
return 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));
|
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);
|
PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
|
||||||
if (retval)
|
if (retval)
|
||||||
return 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));
|
buf, sizeof(struct pyra_profile_buttons));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pyra_get_settings(struct usb_device *usb_dev,
|
static int pyra_get_settings(struct usb_device *usb_dev,
|
||||||
struct pyra_settings *buf)
|
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));
|
buf, sizeof(struct pyra_settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf)
|
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));
|
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,
|
static int pyra_set_profile_settings(struct usb_device *usb_dev,
|
||||||
struct pyra_profile_settings const *settings)
|
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));
|
sizeof(struct pyra_profile_settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pyra_set_profile_buttons(struct usb_device *usb_dev,
|
static int pyra_set_profile_buttons(struct usb_device *usb_dev,
|
||||||
struct pyra_profile_buttons const *buttons)
|
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));
|
sizeof(struct pyra_profile_buttons));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pyra_set_settings(struct usb_device *usb_dev,
|
static int pyra_set_settings(struct usb_device *usb_dev,
|
||||||
struct pyra_settings const *settings)
|
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));
|
sizeof(struct pyra_settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,18 +20,7 @@ struct pyra_b {
|
||||||
uint8_t unknown; /* 1 */
|
uint8_t unknown; /* 1 */
|
||||||
} __attribute__ ((__packed__));
|
} __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 {
|
enum pyra_control_requests {
|
||||||
PYRA_CONTROL_REQUEST_STATUS = 0x00,
|
|
||||||
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
|
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
|
||||||
PYRA_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20
|
PYRA_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20
|
||||||
};
|
};
|
||||||
|
@ -75,7 +64,6 @@ struct pyra_info {
|
||||||
} __attribute__ ((__packed__));
|
} __attribute__ ((__packed__));
|
||||||
|
|
||||||
enum pyra_commands {
|
enum pyra_commands {
|
||||||
PYRA_COMMAND_CONTROL = 0x4,
|
|
||||||
PYRA_COMMAND_SETTINGS = 0x5,
|
PYRA_COMMAND_SETTINGS = 0x5,
|
||||||
PYRA_COMMAND_PROFILE_SETTINGS = 0x6,
|
PYRA_COMMAND_PROFILE_SETTINGS = 0x6,
|
||||||
PYRA_COMMAND_PROFILE_BUTTONS = 0x7,
|
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 */
|
BTN_TR, /* WIIEXT_KEY_RT */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* diable all extensions */
|
/* disable all extensions */
|
||||||
static void ext_disable(struct wiimote_ext *ext)
|
static void ext_disable(struct wiimote_ext *ext)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
|
@ -84,7 +84,7 @@ static int hid_start_in(struct hid_device *hid)
|
||||||
spin_lock_irqsave(&usbhid->lock, flags);
|
spin_lock_irqsave(&usbhid->lock, flags);
|
||||||
if (hid->open > 0 &&
|
if (hid->open > 0 &&
|
||||||
!test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
|
!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)) {
|
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
|
||||||
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
|
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
|
@ -207,15 +207,27 @@ static int usbhid_restart_out_queue(struct usbhid_device *usbhid)
|
||||||
int kicked;
|
int kicked;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (!hid)
|
if (!hid || test_bit(HID_RESET_PENDING, &usbhid->iofl) ||
|
||||||
|
test_bit(HID_SUSPENDED, &usbhid->iofl))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ((kicked = (usbhid->outhead != usbhid->outtail))) {
|
if ((kicked = (usbhid->outhead != usbhid->outtail))) {
|
||||||
hid_dbg(hid, "Kicking head %d tail %d", 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);
|
r = usb_autopm_get_interface_async(usbhid->intf);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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. */
|
/* Asynchronously flush queue. */
|
||||||
set_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
set_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||||
if (hid_submit_out(hid)) {
|
if (hid_submit_out(hid)) {
|
||||||
|
@ -234,15 +246,27 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
WARN_ON(hid == NULL);
|
WARN_ON(hid == NULL);
|
||||||
if (!hid)
|
if (!hid || test_bit(HID_RESET_PENDING, &usbhid->iofl) ||
|
||||||
|
test_bit(HID_SUSPENDED, &usbhid->iofl))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) {
|
if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) {
|
||||||
hid_dbg(hid, "Kicking head %d tail %d", 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);
|
r = usb_autopm_get_interface_async(usbhid->intf);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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. */
|
/* Asynchronously flush queue. */
|
||||||
set_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
set_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||||
if (hid_submit_ctrl(hid)) {
|
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) +
|
usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) +
|
||||||
1 + (report->id > 0);
|
1 + (report->id > 0);
|
||||||
usbhid->urbout->dev = hid_to_usb_dev(hid);
|
usbhid->urbout->dev = hid_to_usb_dev(hid);
|
||||||
memcpy(usbhid->outbuf, raw_report,
|
if (raw_report) {
|
||||||
usbhid->urbout->transfer_buffer_length);
|
memcpy(usbhid->outbuf, raw_report,
|
||||||
kfree(raw_report);
|
usbhid->urbout->transfer_buffer_length);
|
||||||
|
kfree(raw_report);
|
||||||
|
usbhid->out[usbhid->outtail].raw_report = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
dbg_hid("submitting out urb\n");
|
dbg_hid("submitting out urb\n");
|
||||||
|
|
||||||
|
@ -362,8 +389,11 @@ static int hid_submit_ctrl(struct hid_device *hid)
|
||||||
if (dir == USB_DIR_OUT) {
|
if (dir == USB_DIR_OUT) {
|
||||||
usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
|
usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
|
||||||
usbhid->urbctrl->transfer_buffer_length = len;
|
usbhid->urbctrl->transfer_buffer_length = len;
|
||||||
memcpy(usbhid->ctrlbuf, raw_report, len);
|
if (raw_report) {
|
||||||
kfree(raw_report);
|
memcpy(usbhid->ctrlbuf, raw_report, len);
|
||||||
|
kfree(raw_report);
|
||||||
|
usbhid->ctrl[usbhid->ctrltail].raw_report = NULL;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
int maxpacket, padlen;
|
int maxpacket, padlen;
|
||||||
|
|
||||||
|
@ -407,16 +437,6 @@ static int hid_submit_ctrl(struct hid_device *hid)
|
||||||
* Output interrupt completion handler.
|
* 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)
|
static void hid_irq_out(struct urb *urb)
|
||||||
{
|
{
|
||||||
struct hid_device *hid = urb->context;
|
struct hid_device *hid = urb->context;
|
||||||
|
@ -441,15 +461,17 @@ static void hid_irq_out(struct urb *urb)
|
||||||
|
|
||||||
spin_lock_irqsave(&usbhid->lock, flags);
|
spin_lock_irqsave(&usbhid->lock, flags);
|
||||||
|
|
||||||
if (unplug)
|
if (unplug) {
|
||||||
usbhid->outtail = usbhid->outhead;
|
usbhid->outtail = usbhid->outhead;
|
||||||
else
|
} else {
|
||||||
usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
|
usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
|
||||||
|
|
||||||
if (!irq_out_pump_restart(hid)) {
|
if (usbhid->outhead != usbhid->outtail &&
|
||||||
/* Successfully submitted next urb in queue */
|
hid_submit_out(hid) == 0) {
|
||||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
/* Successfully submitted next urb in queue */
|
||||||
return;
|
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||||
|
@ -461,15 +483,6 @@ static void hid_irq_out(struct urb *urb)
|
||||||
/*
|
/*
|
||||||
* Control pipe completion handler.
|
* 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)
|
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);
|
hid_warn(urb->dev, "ctrl urb status %d received\n", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unplug)
|
if (unplug) {
|
||||||
usbhid->ctrltail = usbhid->ctrlhead;
|
usbhid->ctrltail = usbhid->ctrlhead;
|
||||||
else
|
} else {
|
||||||
usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
|
usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
|
||||||
|
|
||||||
if (!ctrl_pump_restart(hid)) {
|
if (usbhid->ctrlhead != usbhid->ctrltail &&
|
||||||
/* Successfully submitted next urb in queue */
|
hid_submit_ctrl(hid) == 0) {
|
||||||
spin_unlock(&usbhid->lock);
|
/* Successfully submitted next urb in queue */
|
||||||
return;
|
spin_unlock(&usbhid->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
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->out[usbhid->outhead].report = report;
|
||||||
usbhid->outhead = head;
|
usbhid->outhead = head;
|
||||||
|
|
||||||
/* Try to awake from autosuspend... */
|
/* If the queue isn't running, restart it */
|
||||||
if (usb_autopm_get_interface_async(usbhid->intf) < 0)
|
if (!test_bit(HID_OUT_RUNNING, &usbhid->iofl)) {
|
||||||
return;
|
usbhid_restart_out_queue(usbhid);
|
||||||
|
|
||||||
/*
|
/* Otherwise see if an earlier request has timed out */
|
||||||
* But if still suspended, leave urb enqueued, don't submit.
|
} else if (time_after(jiffies, usbhid->last_out + HZ * 5)) {
|
||||||
* Submission will occur if/when resume() drains the queue.
|
|
||||||
*/
|
/* Prevent autosuspend following the unlink */
|
||||||
if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl))
|
usb_autopm_get_interface_no_resume(usbhid->intf);
|
||||||
return;
|
|
||||||
|
|
||||||
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
|
* Prevent resubmission in case the URB completes
|
||||||
* but an earlier request may be stuck
|
* before we can unlink it. We don't want to cancel
|
||||||
* we may need to time out
|
* the wrong transfer!
|
||||||
* no race because the URB is blocked under
|
|
||||||
* spinlock
|
|
||||||
*/
|
*/
|
||||||
if (time_after(jiffies, usbhid->last_out + HZ * 5)) {
|
usb_block_urb(usbhid->urbout);
|
||||||
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);
|
|
||||||
|
|
||||||
|
/* 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;
|
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->ctrl[usbhid->ctrlhead].dir = dir;
|
||||||
usbhid->ctrlhead = head;
|
usbhid->ctrlhead = head;
|
||||||
|
|
||||||
/* Try to awake from autosuspend... */
|
/* If the queue isn't running, restart it */
|
||||||
if (usb_autopm_get_interface_async(usbhid->intf) < 0)
|
if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl)) {
|
||||||
return;
|
usbhid_restart_ctrl_queue(usbhid);
|
||||||
|
|
||||||
/*
|
/* Otherwise see if an earlier request has timed out */
|
||||||
* If already suspended, leave urb enqueued, but don't submit.
|
} else if (time_after(jiffies, usbhid->last_ctrl + HZ * 5)) {
|
||||||
* Submission will occur if/when resume() drains the queue.
|
|
||||||
*/
|
/* Prevent autosuspend following the unlink */
|
||||||
if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl))
|
usb_autopm_get_interface_no_resume(usbhid->intf);
|
||||||
return;
|
|
||||||
|
|
||||||
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
|
* Prevent resubmission in case the URB completes
|
||||||
* but an earlier request may be stuck
|
* before we can unlink it. We don't want to cancel
|
||||||
* we may need to time out
|
* the wrong transfer!
|
||||||
* no race because the URB is blocked under
|
|
||||||
* spinlock
|
|
||||||
*/
|
*/
|
||||||
if (time_after(jiffies, usbhid->last_ctrl + HZ * 5)) {
|
usb_block_urb(usbhid->urbctrl);
|
||||||
usb_block_urb(usbhid->urbctrl);
|
|
||||||
/* drop lock to not deadlock if the callback is called */
|
/* Drop lock to avoid deadlock if the callback runs */
|
||||||
spin_unlock(&usbhid->lock);
|
spin_unlock(&usbhid->lock);
|
||||||
usb_unlink_urb(usbhid->urbctrl);
|
|
||||||
spin_lock(&usbhid->lock);
|
usb_unlink_urb(usbhid->urbctrl);
|
||||||
usb_unblock_urb(usbhid->urbctrl);
|
spin_lock(&usbhid->lock);
|
||||||
/*
|
usb_unblock_urb(usbhid->urbctrl);
|
||||||
* if the unlinking has already completed
|
|
||||||
* the pump will have been stopped
|
/* Unlink might have stopped the queue */
|
||||||
* it must be restarted now
|
if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
|
||||||
*/
|
usbhid_restart_ctrl_queue(usbhid);
|
||||||
if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
|
|
||||||
if (!ctrl_pump_restart(hid))
|
/* Now we can allow autosuspend again */
|
||||||
set_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
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)
|
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_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)
|
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
|
#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)
|
static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
||||||
{
|
{
|
||||||
struct hid_device *hid = usb_get_intfdata(intf);
|
struct hid_device *hid = usb_get_intfdata(intf);
|
||||||
struct usbhid_device *usbhid = hid->driver_data;
|
struct usbhid_device *usbhid = hid->driver_data;
|
||||||
int status;
|
int status;
|
||||||
|
bool driver_suspended = false;
|
||||||
|
|
||||||
if (PMSG_IS_AUTO(message)) {
|
if (PMSG_IS_AUTO(message)) {
|
||||||
spin_lock_irq(&usbhid->lock); /* Sync with error handler */
|
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)
|
&& !test_bit(HID_KEYS_PRESSED, &usbhid->iofl)
|
||||||
&& (!usbhid->ledcount || ignoreled))
|
&& (!usbhid->ledcount || ignoreled))
|
||||||
{
|
{
|
||||||
set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
|
set_bit(HID_SUSPENDED, &usbhid->iofl);
|
||||||
spin_unlock_irq(&usbhid->lock);
|
spin_unlock_irq(&usbhid->lock);
|
||||||
if (hid->driver && hid->driver->suspend) {
|
if (hid->driver && hid->driver->suspend) {
|
||||||
status = hid->driver->suspend(hid, message);
|
status = hid->driver->suspend(hid, message);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return status;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
driver_suspended = true;
|
||||||
} else {
|
} else {
|
||||||
usbhid_mark_busy(usbhid);
|
usbhid_mark_busy(usbhid);
|
||||||
spin_unlock_irq(&usbhid->lock);
|
spin_unlock_irq(&usbhid->lock);
|
||||||
|
@ -1505,11 +1525,14 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
driver_suspended = true;
|
||||||
spin_lock_irq(&usbhid->lock);
|
spin_lock_irq(&usbhid->lock);
|
||||||
set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
|
set_bit(HID_SUSPENDED, &usbhid->iofl);
|
||||||
spin_unlock_irq(&usbhid->lock);
|
spin_unlock_irq(&usbhid->lock);
|
||||||
if (usbhid_wait_io(hid) < 0)
|
if (usbhid_wait_io(hid) < 0) {
|
||||||
return -EIO;
|
status = -EIO;
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hid_cancel_delayed_stuff(usbhid);
|
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)) {
|
if (PMSG_IS_AUTO(message) && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
|
||||||
/* lost race against keypresses */
|
/* lost race against keypresses */
|
||||||
status = hid_start_in(hid);
|
status = -EBUSY;
|
||||||
if (status < 0)
|
goto failed;
|
||||||
hid_io_error(hid);
|
|
||||||
usbhid_mark_busy(usbhid);
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
}
|
||||||
dev_dbg(&intf->dev, "suspend\n");
|
dev_dbg(&intf->dev, "suspend\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
hid_resume_common(hid, driver_suspended);
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hid_resume(struct usb_interface *intf)
|
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))
|
if (!test_bit(HID_STARTED, &usbhid->iofl))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
|
status = hid_resume_common(hid, true);
|
||||||
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;
|
|
||||||
}
|
|
||||||
dev_dbg(&intf->dev, "resume status %d\n", status);
|
dev_dbg(&intf->dev, "resume status %d\n", status);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1563,7 +1571,7 @@ static int hid_reset_resume(struct usb_interface *intf)
|
||||||
struct usbhid_device *usbhid = hid->driver_data;
|
struct usbhid_device *usbhid = hid->driver_data;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
|
clear_bit(HID_SUSPENDED, &usbhid->iofl);
|
||||||
status = hid_post_reset(intf);
|
status = hid_post_reset(intf);
|
||||||
if (status >= 0 && hid->driver && hid->driver->reset_resume) {
|
if (status >= 0 && hid->driver && hid->driver->reset_resume) {
|
||||||
int ret = hid->driver->reset_resume(hid);
|
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_CLEAR_HALT 6
|
||||||
#define HID_DISCONNECTED 7
|
#define HID_DISCONNECTED 7
|
||||||
#define HID_STARTED 8
|
#define HID_STARTED 8
|
||||||
#define HID_REPORTED_IDLE 9
|
|
||||||
#define HID_KEYS_PRESSED 10
|
#define HID_KEYS_PRESSED 10
|
||||||
#define HID_NO_BANDWIDTH 11
|
#define HID_NO_BANDWIDTH 11
|
||||||
|
|
||||||
|
|
|
@ -200,6 +200,7 @@ struct hid_item {
|
||||||
#define HID_UP_DIGITIZER 0x000d0000
|
#define HID_UP_DIGITIZER 0x000d0000
|
||||||
#define HID_UP_PID 0x000f0000
|
#define HID_UP_PID 0x000f0000
|
||||||
#define HID_UP_HPVENDOR 0xff7f0000
|
#define HID_UP_HPVENDOR 0xff7f0000
|
||||||
|
#define HID_UP_HPVENDOR2 0xff010000
|
||||||
#define HID_UP_MSVENDOR 0xff000000
|
#define HID_UP_MSVENDOR 0xff000000
|
||||||
#define HID_UP_CUSTOM 0x00ff0000
|
#define HID_UP_CUSTOM 0x00ff0000
|
||||||
#define HID_UP_LOGIVENDOR 0xffbc0000
|
#define HID_UP_LOGIVENDOR 0xffbc0000
|
||||||
|
|
Loading…
Add table
Reference in a new issue