mirror of https://gitee.com/openkylin/linux.git
Merge branches 'dragonrise', 'hidraw-feature', 'multitouch', 'ntrig', 'roccat', 'upstream' and 'upstream-fixes' into for-linus
This commit is contained in:
commit
65b06194c9
|
@ -0,0 +1,10 @@
|
|||
What: For USB devices : /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/report_descriptor
|
||||
For BT devices : /sys/class/bluetooth/hci<addr>/<hid-bus>:<vendor-id>:<product-id>.<num>/report_descriptor
|
||||
Symlink : /sys/class/hidraw/hidraw<num>/device/report_descriptor
|
||||
Date: Jan 2011
|
||||
KernelVersion: 2.0.39
|
||||
Contact: Alan Ott <alan@signal11.us>
|
||||
Description: When read, this file returns the device's raw binary HID
|
||||
report descriptor.
|
||||
This file cannot be written.
|
||||
Users: HIDAPI library (http://www.signal11.us/oss/hidapi)
|
|
@ -0,0 +1,53 @@
|
|||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/arvo/roccatarvo<minor>/actual_profile
|
||||
Date: Januar 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The integer value of this attribute ranges from 1-5.
|
||||
When read, this attribute returns the number of the actual
|
||||
profile which is also the profile that's active on device startup.
|
||||
When written this attribute activates the selected profile
|
||||
immediately.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/arvo/roccatarvo<minor>/button
|
||||
Date: Januar 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The keyboard can store short macros with consist of 1 button with
|
||||
several modifier keys internally.
|
||||
When written, this file lets one set the sequence for a specific
|
||||
button for a specific profile. Button and profile numbers are
|
||||
included in written data. The data has to be 24 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>/arvo/roccatarvo<minor>/info
|
||||
Date: Januar 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When read, this file returns some info about the device like the
|
||||
installed firmware version.
|
||||
The size of the data is 8 bytes in size.
|
||||
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>/arvo/roccatarvo<minor>/key_mask
|
||||
Date: Januar 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The keyboard lets the user deactivate 5 certain keys like the
|
||||
windows and application keys, to protect the user from the outcome
|
||||
of accidentally pressing them.
|
||||
The integer value of this attribute has bits 0-4 set depending
|
||||
on the state of the corresponding key.
|
||||
When read, this file returns the current state of the buttons.
|
||||
When written, the given buttons are activated/deactivated
|
||||
immediately.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/arvo/roccatarvo<minor>/mode_key
|
||||
Date: Januar 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The keyboard has a condensed layout without num-lock key.
|
||||
Instead it uses a mode-key which activates a gaming mode where
|
||||
the assignment of the number block changes.
|
||||
The integer value of this attribute ranges from 0 (OFF) to 1 (ON).
|
||||
When read, this file returns the actual state of the key.
|
||||
When written, the key is activated/deactivated immediately.
|
||||
Users: http://roccat.sourceforge.net
|
|
@ -16,12 +16,14 @@ Description: It is possible to switch the dpi setting of the mouse with the
|
|||
6 3200
|
||||
|
||||
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>/kone/roccatkone<minor>/actual_profile
|
||||
Date: March 2010
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When read, this file returns the number of the actual profile.
|
||||
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>/kone/roccatkone<minor>/firmware_version
|
||||
Date: March 2010
|
||||
|
@ -32,6 +34,7 @@ Description: When read, this file returns the raw integer version number of the
|
|||
number the decimal point has to be shifted 2 positions to the
|
||||
left. E.g. a returned value of 138 means 1.38
|
||||
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>/kone/roccatkone<minor>/profile[1-5]
|
||||
Date: March 2010
|
||||
|
@ -47,6 +50,7 @@ Description: The mouse can store 5 profiles which can be switched by the
|
|||
The mouse will reject invalid data, whereas the profile number
|
||||
stored in the profile doesn't need to fit the number of the
|
||||
store.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kone/roccatkone<minor>/settings
|
||||
Date: March 2010
|
||||
|
@ -57,6 +61,7 @@ Description: When read, this file returns the settings stored in the mouse.
|
|||
When written, this file lets write settings back to the mouse.
|
||||
The data has to be 36 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>/kone/roccatkone<minor>/startup_profile
|
||||
Date: March 2010
|
||||
|
@ -66,6 +71,7 @@ Description: The integer value of this attribute ranges from 1 to 5.
|
|||
that's active when the mouse is powered on.
|
||||
When written, this file sets the number of the startup profile
|
||||
and the mouse activates this profile immediately.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kone/roccatkone<minor>/tcu
|
||||
Date: March 2010
|
||||
|
@ -77,6 +83,7 @@ Description: The mouse has a "Tracking Control Unit" which lets the user
|
|||
Writing 0 in this file will switch the TCU off.
|
||||
Writing 1 in this file will start the calibration which takes
|
||||
around 6 seconds to complete and activates the TCU.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kone/roccatkone<minor>/weight
|
||||
Date: March 2010
|
||||
|
@ -96,3 +103,4 @@ Description: The mouse can be equipped with one of four supplied weights
|
|||
4 20g
|
||||
|
||||
This file is readonly.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
|
|
@ -4,6 +4,7 @@ Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
|||
Description: When read, this file returns the number of the actual profile in
|
||||
range 0-4.
|
||||
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>/koneplus/roccatkoneplus<minor>/firmware_version
|
||||
Date: October 2010
|
||||
|
@ -14,6 +15,7 @@ Description: When read, this file returns the raw integer version number of the
|
|||
number the decimal point has to be shifted 2 positions to the
|
||||
left. E.g. a returned value of 121 means 1.21
|
||||
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>/koneplus/roccatkoneplus<minor>/macro
|
||||
Date: October 2010
|
||||
|
@ -24,6 +26,7 @@ Description: The mouse can store a macro with max 500 key/button strokes
|
|||
button for a specific profile. Button and profile numbers are
|
||||
included in written data. The data has to be 2082 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>/koneplus/roccatkoneplus<minor>/profile_buttons
|
||||
Date: August 2010
|
||||
|
@ -37,6 +40,7 @@ Description: The mouse can store 5 profiles which can be switched by the
|
|||
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>/koneplus/roccatkoneplus<minor>/profile[1-5]_buttons
|
||||
Date: August 2010
|
||||
|
@ -47,6 +51,7 @@ Description: The mouse can store 5 profiles which can be switched by the
|
|||
When read, these files return the respective profile buttons.
|
||||
The returned data is 77 bytes in size.
|
||||
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>/koneplus/roccatkoneplus<minor>/profile_settings
|
||||
Date: October 2010
|
||||
|
@ -61,6 +66,7 @@ Description: The mouse can store 5 profiles which can be switched by the
|
|||
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>/koneplus/roccatkoneplus<minor>/profile[1-5]_settings
|
||||
Date: August 2010
|
||||
|
@ -72,6 +78,7 @@ Description: The mouse can store 5 profiles which can be switched by the
|
|||
When read, these files return the respective profile settings.
|
||||
The returned data is 43 bytes in size.
|
||||
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>/koneplus/roccatkoneplus<minor>/sensor
|
||||
Date: October 2010
|
||||
|
@ -80,6 +87,7 @@ Description: The mouse has a tracking- and a distance-control-unit. These
|
|||
can be activated/deactivated and the lift-off distance can be
|
||||
set. The data has to be 6 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>/koneplus/roccatkoneplus<minor>/startup_profile
|
||||
Date: October 2010
|
||||
|
@ -89,6 +97,7 @@ Description: The integer value of this attribute ranges from 0-4.
|
|||
that's active when the mouse is powered on.
|
||||
When written, this file sets the number of the startup profile
|
||||
and the mouse activates this profile immediately.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/koneplus/roccatkoneplus<minor>/tcu
|
||||
Date: October 2010
|
||||
|
@ -97,6 +106,7 @@ Description: When written a calibration process for the tracking control unit
|
|||
can be initiated/cancelled.
|
||||
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>/koneplus/roccatkoneplus<minor>/tcu_image
|
||||
Date: October 2010
|
||||
|
@ -106,3 +116,4 @@ Description: When read the mouse returns a 30x30 pixel image of the
|
|||
calibration process initiated with tcu.
|
||||
The returned data is 1028 bytes in size.
|
||||
This file is readonly.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kovaplus/roccatkovaplus<minor>/actual_cpi
|
||||
Date: January 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The integer value of this attribute ranges from 1-4.
|
||||
When read, this attribute returns the number of the active
|
||||
cpi level.
|
||||
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>/kovaplus/roccatkovaplus<minor>/actual_profile
|
||||
Date: January 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The integer value of this attribute ranges from 0-4.
|
||||
When read, this attribute returns the number of the active
|
||||
profile.
|
||||
When written, the mouse activates this profile immediately.
|
||||
The profile that's active when powered down is the same that's
|
||||
active when the mouse is powered on.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kovaplus/roccatkovaplus<minor>/actual_sensitivity_x
|
||||
Date: January 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The integer value of this attribute ranges from 1-10.
|
||||
When read, this attribute returns the number of the actual
|
||||
sensitivity in x direction.
|
||||
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>/kovaplus/roccatkovaplus<minor>/actual_sensitivity_y
|
||||
Date: January 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The integer value of this attribute ranges from 1-10.
|
||||
When read, this attribute returns the number of the actual
|
||||
sensitivity in y direction.
|
||||
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>/kovaplus/roccatkovaplus<minor>/firmware_version
|
||||
Date: January 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When read, this file returns the raw integer version number of the
|
||||
firmware reported by the mouse. Using the integer value eases
|
||||
further usage in other programs. To receive the real version
|
||||
number the decimal point has to be shifted 2 positions to the
|
||||
left. E.g. a returned value of 121 means 1.21
|
||||
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>/kovaplus/roccatkovaplus<minor>/profile_buttons
|
||||
Date: January 2011
|
||||
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 in settings and buttons.
|
||||
profile_buttons holds informations about button layout.
|
||||
When written, this file lets one write the respective profile
|
||||
buttons back to the mouse. The data has to be 23 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>/kovaplus/roccatkovaplus<minor>/profile[1-5]_buttons
|
||||
Date: January 2011
|
||||
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 in settings and buttons.
|
||||
profile_buttons holds informations about button layout.
|
||||
When read, these files return the respective profile buttons.
|
||||
The returned data is 23 bytes in size.
|
||||
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>/kovaplus/roccatkovaplus<minor>/profile_settings
|
||||
Date: January 2011
|
||||
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 in settings and buttons.
|
||||
profile_settings 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 16 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>/kovaplus/roccatkovaplus<minor>/profile[1-5]_settings
|
||||
Date: January 2011
|
||||
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 in settings and buttons.
|
||||
profile_settings holds informations like resolution, sensitivity
|
||||
and light effects.
|
||||
When read, these files return the respective profile settings.
|
||||
The returned data is 16 bytes in size.
|
||||
This file is readonly.
|
||||
Users: http://roccat.sourceforge.net
|
|
@ -13,6 +13,7 @@ Description: It is possible to switch the cpi setting of the mouse with the
|
|||
4 1600
|
||||
|
||||
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>/pyra/roccatpyra<minor>/actual_profile
|
||||
Date: August 2010
|
||||
|
@ -20,6 +21,7 @@ Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
|||
Description: When read, this file returns the number of the actual profile in
|
||||
range 0-4.
|
||||
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>/pyra/roccatpyra<minor>/firmware_version
|
||||
Date: August 2010
|
||||
|
@ -30,6 +32,7 @@ Description: When read, this file returns the raw integer version number of the
|
|||
number the decimal point has to be shifted 2 positions to the
|
||||
left. E.g. a returned value of 138 means 1.38
|
||||
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>/pyra/roccatpyra<minor>/profile_settings
|
||||
Date: August 2010
|
||||
|
@ -44,6 +47,7 @@ Description: The mouse can store 5 profiles which can be switched by the
|
|||
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>/pyra/roccatpyra<minor>/profile[1-5]_settings
|
||||
Date: August 2010
|
||||
|
@ -55,6 +59,7 @@ Description: The mouse can store 5 profiles which can be switched by the
|
|||
When read, these files return the respective profile settings.
|
||||
The returned data is 13 bytes in size.
|
||||
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>/pyra/roccatpyra<minor>/profile_buttons
|
||||
Date: August 2010
|
||||
|
@ -68,6 +73,7 @@ Description: The mouse can store 5 profiles which can be switched by the
|
|||
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>/pyra/roccatpyra<minor>/profile[1-5]_buttons
|
||||
Date: August 2010
|
||||
|
@ -78,6 +84,7 @@ Description: The mouse can store 5 profiles which can be switched by the
|
|||
When read, these files return the respective profile buttons.
|
||||
The returned data is 19 bytes in size.
|
||||
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>/pyra/roccatpyra<minor>/startup_profile
|
||||
Date: August 2010
|
||||
|
@ -86,6 +93,7 @@ Description: The integer value of this attribute ranges from 0-4.
|
|||
When read, this attribute returns the number of the profile
|
||||
that's active when the mouse is powered on.
|
||||
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>/pyra/roccatpyra<minor>/settings
|
||||
Date: August 2010
|
||||
|
@ -96,3 +104,4 @@ Description: When read, this file returns the settings stored in the mouse.
|
|||
When written, this file lets write settings back to the mouse.
|
||||
The data has to be 3 bytes long. The mouse will reject invalid
|
||||
data.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
|
|
@ -133,6 +133,7 @@ Code Seq#(hex) Include File Comments
|
|||
'H' C0-DF net/bluetooth/hidp/hidp.h conflict!
|
||||
'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict!
|
||||
'H' C0-DF net/bluetooth/bnep/bnep.h conflict!
|
||||
'H' F1 linux/hid-roccat.h <mailto:erazor_de@users.sourceforge.net>
|
||||
'I' all linux/isdn.h conflict!
|
||||
'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict!
|
||||
'I' 40-4F linux/mISDNif.h conflict!
|
||||
|
|
|
@ -68,9 +68,15 @@ config HID_A4TECH
|
|||
---help---
|
||||
Support for A4 tech X5 and WOP-35 / Trust 450L mice.
|
||||
|
||||
config HID_ACRUX_FF
|
||||
tristate "ACRUX force feedback"
|
||||
config HID_ACRUX
|
||||
tristate "ACRUX game controller support"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Say Y here if you want to enable support for ACRUX game controllers.
|
||||
|
||||
config HID_ACRUX_FF
|
||||
tristate "ACRUX force feedback support"
|
||||
depends on HID_ACRUX
|
||||
select INPUT_FF_MEMLESS
|
||||
---help---
|
||||
Say Y here if you want to enable force feedback support for ACRUX
|
||||
|
@ -165,13 +171,6 @@ config HID_EMS_FF
|
|||
Currently the following devices are known to be supported:
|
||||
- Trio Linker Plus II
|
||||
|
||||
config HID_EGALAX
|
||||
tristate "eGalax multi-touch panel"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for the eGalax dual-touch panels, including the
|
||||
Joojoo and Wetab tablets.
|
||||
|
||||
config HID_ELECOM
|
||||
tristate "ELECOM BM084 bluetooth mouse"
|
||||
depends on BT_HIDP
|
||||
|
@ -185,6 +184,14 @@ config HID_EZKEY
|
|||
---help---
|
||||
Support for Ezkey BTC 8193 keyboard.
|
||||
|
||||
config HID_KEYTOUCH
|
||||
tristate "Keyoutch HID devices"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Keytouch HID devices not fully compliant with
|
||||
the specification. Currently supported:
|
||||
- Keytouch IEC 60945
|
||||
|
||||
config HID_KYE
|
||||
tristate "Kye/Genius Ergo Mouse" if EMBEDDED
|
||||
depends on USB_HID
|
||||
|
@ -223,6 +230,12 @@ config HID_KENSINGTON
|
|||
---help---
|
||||
Support for Kensington Slimblade Trackball.
|
||||
|
||||
config HID_LCPOWER
|
||||
tristate "LC-Power"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for LC-Power RC1000MCE RF remote control.
|
||||
|
||||
config HID_LOGITECH
|
||||
tristate "Logitech devices" if EMBEDDED
|
||||
depends on USB_HID
|
||||
|
@ -309,8 +322,11 @@ config HID_MULTITOUCH
|
|||
Say Y here if you have one of the following devices:
|
||||
- Cypress TrueTouch panels
|
||||
- Hanvon dual touch panels
|
||||
- IrTouch Infrared USB panels
|
||||
- Pixcir dual touch panels
|
||||
- 'Sensing Win7-TwoFinger' panel by GeneralTouch
|
||||
- eGalax dual-touch panels, including the
|
||||
Joojoo and Wetab tablets
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
|
@ -324,10 +340,10 @@ config HID_NTRIG
|
|||
Support for N-Trig touch screen.
|
||||
|
||||
config HID_ORTEK
|
||||
tristate "Ortek WKB-2000 wireless keyboard and mouse trackpad"
|
||||
tristate "Ortek PKB-1700/WKB-2000 wireless keyboard and mouse trackpad"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Ortek WKB-2000 wireless keyboard + mouse trackpad.
|
||||
Support for Ortek PKB-1700/WKB-2000 wireless keyboard + mouse trackpad.
|
||||
|
||||
config HID_PANTHERLORD
|
||||
tristate "Pantherlord/GreenAsia game controller"
|
||||
|
@ -422,10 +438,22 @@ config HID_ROCCAT
|
|||
Say Y here if you have a Roccat mouse or keyboard and want OSD or
|
||||
macro execution support.
|
||||
|
||||
config HID_ROCCAT_COMMON
|
||||
tristate
|
||||
|
||||
config HID_ROCCAT_ARVO
|
||||
tristate "Roccat Arvo keyboard support"
|
||||
depends on USB_HID
|
||||
select HID_ROCCAT
|
||||
select HID_ROCCAT_COMMON
|
||||
---help---
|
||||
Support for Roccat Arvo keyboard.
|
||||
|
||||
config HID_ROCCAT_KONE
|
||||
tristate "Roccat Kone Mouse support"
|
||||
depends on USB_HID
|
||||
select HID_ROCCAT
|
||||
select HID_ROCCAT_COMMON
|
||||
---help---
|
||||
Support for Roccat Kone mouse.
|
||||
|
||||
|
@ -433,13 +461,23 @@ config HID_ROCCAT_KONEPLUS
|
|||
tristate "Roccat Kone[+] mouse support"
|
||||
depends on USB_HID
|
||||
select HID_ROCCAT
|
||||
select HID_ROCCAT_COMMON
|
||||
---help---
|
||||
Support for Roccat Kone[+] mouse.
|
||||
|
||||
config HID_ROCCAT_KOVAPLUS
|
||||
tristate "Roccat Kova[+] mouse support"
|
||||
depends on USB_HID
|
||||
select HID_ROCCAT
|
||||
select HID_ROCCAT_COMMON
|
||||
---help---
|
||||
Support for Roccat Kova[+] mouse.
|
||||
|
||||
config HID_ROCCAT_PYRA
|
||||
tristate "Roccat Pyra mouse support"
|
||||
depends on USB_HID
|
||||
select HID_ROCCAT
|
||||
select HID_ROCCAT_COMMON
|
||||
---help---
|
||||
Support for Roccat Pyra mouse.
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ endif
|
|||
|
||||
obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o
|
||||
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
|
||||
obj-$(CONFIG_HID_ACRUX_FF) += hid-axff.o
|
||||
obj-$(CONFIG_HID_ACRUX) += hid-axff.o
|
||||
obj-$(CONFIG_HID_APPLE) += hid-apple.o
|
||||
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
|
||||
obj-$(CONFIG_HID_CANDO) += hid-cando.o
|
||||
|
@ -36,12 +36,13 @@ obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
|
|||
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
|
||||
obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o
|
||||
obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
|
||||
obj-$(CONFIG_HID_EGALAX) += hid-egalax.o
|
||||
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
|
||||
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
|
||||
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
|
||||
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
|
||||
obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
|
||||
obj-$(CONFIG_HID_KYE) += hid-kye.o
|
||||
obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
|
||||
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
|
||||
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
|
||||
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
|
||||
|
@ -56,8 +57,11 @@ obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
|
|||
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
|
||||
obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o
|
||||
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o
|
||||
obj-$(CONFIG_HID_ROCCAT_COMMON) += hid-roccat-common.o
|
||||
obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o
|
||||
obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o
|
||||
obj-$(CONFIG_HID_ROCCAT_KONEPLUS) += hid-roccat-koneplus.o
|
||||
obj-$(CONFIG_HID_ROCCAT_KOVAPLUS) += hid-roccat-kovaplus.o
|
||||
obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o
|
||||
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
|
||||
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include <linux/hid.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#ifdef CONFIG_HID_ACRUX_FF
|
||||
#include "usbhid/usbhid.h"
|
||||
|
||||
struct axff_device {
|
||||
|
@ -109,6 +111,12 @@ static int axff_init(struct hid_device *hid)
|
|||
kfree(axff);
|
||||
return error;
|
||||
}
|
||||
#else
|
||||
static inline int axff_init(struct hid_device *hid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
|
@ -139,9 +147,25 @@ static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
error);
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to start polling device right away, otherwise
|
||||
* it will go into a coma.
|
||||
*/
|
||||
error = hid_hw_open(hdev);
|
||||
if (error) {
|
||||
dev_err(&hdev->dev, "hw open failed\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ax_remove(struct hid_device *hdev)
|
||||
{
|
||||
hid_hw_close(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static const struct hid_device_id ax_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), },
|
||||
{ }
|
||||
|
@ -149,9 +173,10 @@ static const struct hid_device_id ax_devices[] = {
|
|||
MODULE_DEVICE_TABLE(hid, ax_devices);
|
||||
|
||||
static struct hid_driver ax_driver = {
|
||||
.name = "acrux",
|
||||
.id_table = ax_devices,
|
||||
.probe = ax_probe,
|
||||
.name = "acrux",
|
||||
.id_table = ax_devices,
|
||||
.probe = ax_probe,
|
||||
.remove = ax_remove,
|
||||
};
|
||||
|
||||
static int __init ax_init(void)
|
||||
|
|
|
@ -1159,6 +1159,32 @@ static bool hid_hiddev(struct hid_device *hdev)
|
|||
return !!hid_match_id(hdev, hid_hiddev_list);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
read_report_descriptor(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
|
||||
if (off >= hdev->rsize)
|
||||
return 0;
|
||||
|
||||
if (off + count > hdev->rsize)
|
||||
count = hdev->rsize - off;
|
||||
|
||||
memcpy(buf, hdev->rdesc + off, count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct bin_attribute dev_bin_attr_report_desc = {
|
||||
.attr = { .name = "report_descriptor", .mode = 0444 },
|
||||
.read = read_report_descriptor,
|
||||
.size = HID_MAX_DESCRIPTOR_SIZE,
|
||||
};
|
||||
|
||||
int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
|
||||
{
|
||||
static const char *types[] = { "Device", "Pointer", "Mouse", "Device",
|
||||
|
@ -1169,6 +1195,7 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
|
|||
char buf[64];
|
||||
unsigned int i;
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE)
|
||||
connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV);
|
||||
|
@ -1230,6 +1257,11 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
|
|||
bus = "<UNKNOWN>";
|
||||
}
|
||||
|
||||
ret = device_create_bin_file(&hdev->dev, &dev_bin_attr_report_desc);
|
||||
if (ret)
|
||||
hid_warn(hdev,
|
||||
"can't create sysfs report descriptor attribute err: %d\n", ret);
|
||||
|
||||
hid_info(hdev, "%s: %s HID v%x.%02x %s [%s] on %s\n",
|
||||
buf, bus, hdev->version >> 8, hdev->version & 0xff,
|
||||
type, hdev->name, hdev->phys);
|
||||
|
@ -1240,6 +1272,7 @@ EXPORT_SYMBOL_GPL(hid_connect);
|
|||
|
||||
void hid_disconnect(struct hid_device *hdev)
|
||||
{
|
||||
device_remove_bin_file(&hdev->dev, &dev_bin_attr_report_desc);
|
||||
if (hdev->claimed & HID_CLAIMED_INPUT)
|
||||
hidinput_disconnect(hdev);
|
||||
if (hdev->claimed & HID_CLAIMED_HIDDEV)
|
||||
|
@ -1256,9 +1289,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) },
|
||||
#if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE)
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) },
|
||||
#endif
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
|
||||
|
@ -1346,9 +1377,12 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, USB_DEVICE_ID_IRTOUCH_INFRARED_USB) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
|
||||
{ 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_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
|
||||
|
@ -1369,6 +1403,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
|
||||
|
@ -1401,12 +1436,15 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
|
||||
{ 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_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
|
||||
|
|
|
@ -1,279 +0,0 @@
|
|||
/*
|
||||
* HID driver for eGalax dual-touch panels
|
||||
*
|
||||
* Copyright (c) 2010 Stephane Chatty <chatty@enac.fr>
|
||||
* Copyright (c) 2010 Henrik Rydberg <rydberg@euromail.se>
|
||||
* Copyright (c) 2010 Canonical, Ltd.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <linux/input/mt.h>
|
||||
#include <linux/slab.h>
|
||||
#include "usbhid/usbhid.h"
|
||||
|
||||
MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
|
||||
MODULE_DESCRIPTION("eGalax dual-touch panel");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define MAX_SLOTS 2
|
||||
|
||||
/* estimated signal-to-noise ratios */
|
||||
#define SN_MOVE 4096
|
||||
#define SN_PRESSURE 32
|
||||
|
||||
struct egalax_data {
|
||||
int valid;
|
||||
int slot;
|
||||
int touch;
|
||||
int x, y, z;
|
||||
};
|
||||
|
||||
static void set_abs(struct input_dev *input, unsigned int code,
|
||||
struct hid_field *field, int snratio)
|
||||
{
|
||||
int fmin = field->logical_minimum;
|
||||
int fmax = field->logical_maximum;
|
||||
int fuzz = snratio ? (fmax - fmin) / snratio : 0;
|
||||
input_set_abs_params(input, code, fmin, fmax, fuzz, 0);
|
||||
}
|
||||
|
||||
static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
struct input_dev *input = hi->input;
|
||||
|
||||
switch (usage->hid & HID_USAGE_PAGE) {
|
||||
|
||||
case HID_UP_GENDESK:
|
||||
switch (usage->hid) {
|
||||
case HID_GD_X:
|
||||
field->logical_maximum = 32760;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_X);
|
||||
set_abs(input, ABS_MT_POSITION_X, field, SN_MOVE);
|
||||
/* touchscreen emulation */
|
||||
set_abs(input, ABS_X, field, SN_MOVE);
|
||||
return 1;
|
||||
case HID_GD_Y:
|
||||
field->logical_maximum = 32760;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_Y);
|
||||
set_abs(input, ABS_MT_POSITION_Y, field, SN_MOVE);
|
||||
/* touchscreen emulation */
|
||||
set_abs(input, ABS_Y, field, SN_MOVE);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case HID_UP_DIGITIZER:
|
||||
switch (usage->hid) {
|
||||
case HID_DG_TIPSWITCH:
|
||||
/* touchscreen emulation */
|
||||
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
|
||||
input_set_capability(input, EV_KEY, BTN_TOUCH);
|
||||
return 1;
|
||||
case HID_DG_INRANGE:
|
||||
case HID_DG_CONFIDENCE:
|
||||
case HID_DG_CONTACTCOUNT:
|
||||
case HID_DG_CONTACTMAX:
|
||||
return -1;
|
||||
case HID_DG_CONTACTID:
|
||||
input_mt_init_slots(input, MAX_SLOTS);
|
||||
return 1;
|
||||
case HID_DG_TIPPRESSURE:
|
||||
field->logical_minimum = 0;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_PRESSURE);
|
||||
set_abs(input, ABS_MT_PRESSURE, field, SN_PRESSURE);
|
||||
/* touchscreen emulation */
|
||||
set_abs(input, ABS_PRESSURE, field, SN_PRESSURE);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ignore others (from other reports we won't get anyway) */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int egalax_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
/* tell hid-input to skip setup of these event types */
|
||||
if (usage->type == EV_KEY || usage->type == EV_ABS)
|
||||
set_bit(usage->type, hi->input->evbit);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* this function is called when a whole finger has been parsed,
|
||||
* so that it can decide what to send to the input layer.
|
||||
*/
|
||||
static void egalax_filter_event(struct egalax_data *td, struct input_dev *input)
|
||||
{
|
||||
input_mt_slot(input, td->slot);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, td->touch);
|
||||
if (td->touch) {
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y);
|
||||
input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z);
|
||||
}
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
}
|
||||
|
||||
static int egalax_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct egalax_data *td = hid_get_drvdata(hid);
|
||||
|
||||
/* Note, eGalax has two product lines: the first is resistive and
|
||||
* uses a standard parallel multitouch protocol (product ID ==
|
||||
* 48xx). The second is capacitive and uses an unusual "serial"
|
||||
* protocol with a different message for each multitouch finger
|
||||
* (product ID == 72xx).
|
||||
*/
|
||||
if (hid->claimed & HID_CLAIMED_INPUT) {
|
||||
struct input_dev *input = field->hidinput->input;
|
||||
|
||||
switch (usage->hid) {
|
||||
case HID_DG_INRANGE:
|
||||
td->valid = value;
|
||||
break;
|
||||
case HID_DG_CONFIDENCE:
|
||||
/* avoid interference from generic hidinput handling */
|
||||
break;
|
||||
case HID_DG_TIPSWITCH:
|
||||
td->touch = value;
|
||||
break;
|
||||
case HID_DG_TIPPRESSURE:
|
||||
td->z = value;
|
||||
break;
|
||||
case HID_DG_CONTACTID:
|
||||
td->slot = clamp_val(value, 0, MAX_SLOTS - 1);
|
||||
break;
|
||||
case HID_GD_X:
|
||||
td->x = value;
|
||||
break;
|
||||
case HID_GD_Y:
|
||||
td->y = value;
|
||||
/* this is the last field in a finger */
|
||||
if (td->valid)
|
||||
egalax_filter_event(td, input);
|
||||
break;
|
||||
case HID_DG_CONTACTCOUNT:
|
||||
/* touch emulation: this is the last field in a frame */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* fallback to the generic hidinput handling */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* we have handled the hidinput part, now remains hiddev */
|
||||
if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
|
||||
hid->hiddev_hid_event(hid, field, usage, value);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int egalax_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct egalax_data *td;
|
||||
struct hid_report *report;
|
||||
|
||||
td = kzalloc(sizeof(struct egalax_data), GFP_KERNEL);
|
||||
if (!td) {
|
||||
hid_err(hdev, "cannot allocate eGalax data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
hid_set_drvdata(hdev, td);
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[5];
|
||||
if (report) {
|
||||
report->field[0]->value[0] = 2;
|
||||
usbhid_submit_report(hdev, report, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
end:
|
||||
if (ret)
|
||||
kfree(td);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void egalax_remove(struct hid_device *hdev)
|
||||
{
|
||||
hid_hw_stop(hdev);
|
||||
kfree(hid_get_drvdata(hdev));
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
}
|
||||
|
||||
static const struct hid_device_id egalax_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, egalax_devices);
|
||||
|
||||
static const struct hid_usage_id egalax_grabbed_usages[] = {
|
||||
{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
|
||||
{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
|
||||
};
|
||||
|
||||
static struct hid_driver egalax_driver = {
|
||||
.name = "egalax-touch",
|
||||
.id_table = egalax_devices,
|
||||
.probe = egalax_probe,
|
||||
.remove = egalax_remove,
|
||||
.input_mapping = egalax_input_mapping,
|
||||
.input_mapped = egalax_input_mapped,
|
||||
.usage_table = egalax_grabbed_usages,
|
||||
.event = egalax_event,
|
||||
};
|
||||
|
||||
static int __init egalax_init(void)
|
||||
{
|
||||
return hid_register_driver(&egalax_driver);
|
||||
}
|
||||
|
||||
static void __exit egalax_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&egalax_driver);
|
||||
}
|
||||
|
||||
module_init(egalax_init);
|
||||
module_exit(egalax_exit);
|
||||
|
|
@ -43,6 +43,11 @@ static int gyration_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
case 0x048: gy_map_key_clear(KEY_MEDIA); break;
|
||||
case 0x049: gy_map_key_clear(KEY_CAMERA); break;
|
||||
case 0x04a: gy_map_key_clear(KEY_VIDEO); break;
|
||||
case 0x05a: gy_map_key_clear(KEY_TEXT); break;
|
||||
case 0x05b: gy_map_key_clear(KEY_RED); break;
|
||||
case 0x05c: gy_map_key_clear(KEY_GREEN); break;
|
||||
case 0x05d: gy_map_key_clear(KEY_YELLOW); break;
|
||||
case 0x05e: gy_map_key_clear(KEY_BLUE); break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
|
|
|
@ -333,6 +333,9 @@
|
|||
#define USB_VENDOR_ID_IMATION 0x0718
|
||||
#define USB_DEVICE_ID_DISC_STAKKA 0xd000
|
||||
|
||||
#define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615
|
||||
#define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070
|
||||
|
||||
#define USB_VENDOR_ID_JESS 0x0c45
|
||||
#define USB_DEVICE_ID_JESS_YUREX 0x1010
|
||||
|
||||
|
@ -345,6 +348,9 @@
|
|||
#define USB_VENDOR_ID_KWORLD 0x1b80
|
||||
#define USB_DEVICE_ID_KWORLD_RADIO_FM700 0xd700
|
||||
|
||||
#define USB_VENDOR_ID_KEYTOUCH 0x0926
|
||||
#define USB_DEVICE_ID_KEYTOUCH_IEC 0x3333
|
||||
|
||||
#define USB_VENDOR_ID_KYE 0x0458
|
||||
#define USB_DEVICE_ID_KYE_ERGO_525V 0x0087
|
||||
#define USB_DEVICE_ID_KYE_GPEN_560 0x5003
|
||||
|
@ -352,6 +358,9 @@
|
|||
#define USB_VENDOR_ID_LABTEC 0x1020
|
||||
#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006
|
||||
|
||||
#define USB_VENDOR_ID_LCPOWER 0x1241
|
||||
#define USB_DEVICE_ID_LCPOWER_LC1000 0xf767
|
||||
|
||||
#define USB_VENDOR_ID_LD 0x0f11
|
||||
#define USB_DEVICE_ID_LD_CASSY 0x1000
|
||||
#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010
|
||||
|
@ -383,6 +392,7 @@
|
|||
#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294
|
||||
#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293
|
||||
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
|
||||
#define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298
|
||||
#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
|
||||
#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c
|
||||
#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
|
||||
|
@ -466,6 +476,7 @@
|
|||
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
|
||||
|
||||
#define USB_VENDOR_ID_ORTEK 0x05a4
|
||||
#define USB_DEVICE_ID_ORTEK_PKB1700 0x1700
|
||||
#define USB_DEVICE_ID_ORTEK_WKB2000 0x2000
|
||||
|
||||
#define USB_VENDOR_ID_PANJIT 0x134c
|
||||
|
@ -496,8 +507,10 @@
|
|||
#define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001
|
||||
|
||||
#define USB_VENDOR_ID_ROCCAT 0x1e7d
|
||||
#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4
|
||||
#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced
|
||||
#define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51
|
||||
#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50
|
||||
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24
|
||||
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6
|
||||
|
||||
|
|
|
@ -290,14 +290,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
goto ignore;
|
||||
}
|
||||
|
||||
if (field->report_type == HID_FEATURE_REPORT) {
|
||||
if (device->driver->feature_mapping) {
|
||||
device->driver->feature_mapping(device, hidinput, field,
|
||||
usage);
|
||||
}
|
||||
goto ignore;
|
||||
}
|
||||
|
||||
if (device->driver->input_mapping) {
|
||||
int ret = device->driver->input_mapping(device, hidinput, field,
|
||||
usage, &bit, &max);
|
||||
|
@ -835,6 +827,24 @@ static void hidinput_close(struct input_dev *dev)
|
|||
hid_hw_close(hid);
|
||||
}
|
||||
|
||||
static void report_features(struct hid_device *hid)
|
||||
{
|
||||
struct hid_driver *drv = hid->driver;
|
||||
struct hid_report_enum *rep_enum;
|
||||
struct hid_report *rep;
|
||||
int i, j;
|
||||
|
||||
if (!drv->feature_mapping)
|
||||
return;
|
||||
|
||||
rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
|
||||
list_for_each_entry(rep, &rep_enum->report_list, list)
|
||||
for (i = 0; i < rep->maxfield; i++)
|
||||
for (j = 0; j < rep->field[i]->maxusage; j++)
|
||||
drv->feature_mapping(hid, rep->field[i],
|
||||
rep->field[i]->usage + j);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register the input device; print a message.
|
||||
* Configure the input layer interface
|
||||
|
@ -863,7 +873,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
|
|||
return -1;
|
||||
}
|
||||
|
||||
for (k = HID_INPUT_REPORT; k <= HID_FEATURE_REPORT; k++) {
|
||||
report_features(hid);
|
||||
|
||||
for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
|
||||
if (k == HID_OUTPUT_REPORT &&
|
||||
hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
|
||||
continue;
|
||||
|
@ -928,6 +940,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
|
|||
return 0;
|
||||
|
||||
out_cleanup:
|
||||
list_del(&hidinput->list);
|
||||
input_free_device(hidinput->input);
|
||||
kfree(hidinput);
|
||||
out_unwind:
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* HID driver for Keytouch devices not fully compliant with HID standard
|
||||
*
|
||||
* Copyright (c) 2011 Jiri Kosina
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 "hid-ids.h"
|
||||
|
||||
/* Replace the broken report descriptor of this device with rather
|
||||
* a default one */
|
||||
static __u8 keytouch_fixed_rdesc[] = {
|
||||
0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15,
|
||||
0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08,
|
||||
0x81, 0x01, 0x95, 0x03, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x91,
|
||||
0x02, 0x95, 0x05, 0x75, 0x01, 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00,
|
||||
0x26, 0xff, 0x00, 0x05, 0x07, 0x19, 0x00, 0x2a, 0xff, 0x00, 0x81, 0x00, 0xc0
|
||||
};
|
||||
|
||||
static __u8 *keytouch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
hid_info(hdev, "fixing up Keytouch IEC report descriptor\n");
|
||||
|
||||
rdesc = keytouch_fixed_rdesc;
|
||||
*rsize = sizeof(keytouch_fixed_rdesc);
|
||||
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static const struct hid_device_id keytouch_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, keytouch_devices);
|
||||
|
||||
static struct hid_driver keytouch_driver = {
|
||||
.name = "keytouch",
|
||||
.id_table = keytouch_devices,
|
||||
.report_fixup = keytouch_report_fixup,
|
||||
};
|
||||
|
||||
static int __init keytouch_init(void)
|
||||
{
|
||||
return hid_register_driver(&keytouch_driver);
|
||||
}
|
||||
|
||||
static void __exit keytouch_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&keytouch_driver);
|
||||
}
|
||||
|
||||
module_init(keytouch_init);
|
||||
module_exit(keytouch_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Jiri Kosina");
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* HID driver for LC Power Model RC1000MCE
|
||||
*
|
||||
* Copyright (c) 2011 Chris Schlund
|
||||
* based on hid-topseed module
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 "hid-ids.h"
|
||||
|
||||
#define ts_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
|
||||
EV_KEY, (c))
|
||||
static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
if ((usage->hid & HID_USAGE_PAGE) != 0x0ffbc0000)
|
||||
return 0;
|
||||
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
case 0x046: ts_map_key_clear(KEY_YELLOW); break;
|
||||
case 0x047: ts_map_key_clear(KEY_GREEN); break;
|
||||
case 0x049: ts_map_key_clear(KEY_BLUE); break;
|
||||
case 0x04a: ts_map_key_clear(KEY_RED); break;
|
||||
case 0x00d: ts_map_key_clear(KEY_HOME); break;
|
||||
case 0x025: ts_map_key_clear(KEY_TV); break;
|
||||
case 0x048: ts_map_key_clear(KEY_VCR); break;
|
||||
case 0x024: ts_map_key_clear(KEY_MENU); break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct hid_device_id ts_devices[] = {
|
||||
{ HID_USB_DEVICE( USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, ts_devices);
|
||||
|
||||
static struct hid_driver ts_driver = {
|
||||
.name = "LC RC1000MCE",
|
||||
.id_table = ts_devices,
|
||||
.input_mapping = ts_input_mapping,
|
||||
};
|
||||
|
||||
static int __init ts_init(void)
|
||||
{
|
||||
return hid_register_driver(&ts_driver);
|
||||
}
|
||||
|
||||
static void __exit ts_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&ts_driver);
|
||||
}
|
||||
|
||||
module_init(ts_init);
|
||||
module_exit(ts_exit);
|
||||
MODULE_LICENSE("GPL");
|
|
@ -377,6 +377,8 @@ static const struct hid_device_id lg_devices[] = {
|
|||
.driver_data = LG_FF },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
|
||||
.driver_data = LG_FF },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
|
||||
.driver_data = LG_FF },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
|
||||
.driver_data = LG_FF4 },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
|
||||
|
|
|
@ -258,7 +258,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
|
|||
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_MINOR, touch_minor << 2);
|
||||
input_report_abs(input, ABS_MT_ORIENTATION, orientation);
|
||||
input_report_abs(input, ABS_MT_ORIENTATION, -orientation);
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
|
||||
|
@ -397,7 +397,7 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
|
|||
input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0);
|
||||
input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
|
||||
|
||||
/* Note: Touch Y position from the device is inverted relative
|
||||
* to how pointer motion is reported (and relative to how USB
|
||||
|
|
|
@ -5,6 +5,12 @@
|
|||
* Copyright (c) 2010-2011 Benjamin Tissoires <benjamin.tissoires@gmail.com>
|
||||
* Copyright (c) 2010-2011 Ecole Nationale de l'Aviation Civile, France
|
||||
*
|
||||
* This code is partly based on hid-egalax.c:
|
||||
*
|
||||
* Copyright (c) 2010 Stephane Chatty <chatty@enac.fr>
|
||||
* Copyright (c) 2010 Henrik Rydberg <rydberg@euromail.se>
|
||||
* Copyright (c) 2010 Canonical, Ltd.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -24,6 +30,7 @@
|
|||
|
||||
|
||||
MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
|
||||
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
|
||||
MODULE_DESCRIPTION("HID multitouch panels");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
@ -36,6 +43,7 @@ MODULE_LICENSE("GPL");
|
|||
#define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3)
|
||||
#define MT_QUIRK_VALID_IS_INRANGE (1 << 4)
|
||||
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 5)
|
||||
#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 6)
|
||||
|
||||
struct mt_slot {
|
||||
__s32 x, y, p, w, h;
|
||||
|
@ -65,10 +73,11 @@ struct mt_class {
|
|||
};
|
||||
|
||||
/* classes of device behavior */
|
||||
#define MT_CLS_DEFAULT 1
|
||||
#define MT_CLS_DUAL1 2
|
||||
#define MT_CLS_DUAL2 3
|
||||
#define MT_CLS_CYPRESS 4
|
||||
#define MT_CLS_DEFAULT 1
|
||||
#define MT_CLS_DUAL_INRANGE_CONTACTID 2
|
||||
#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 3
|
||||
#define MT_CLS_CYPRESS 4
|
||||
#define MT_CLS_EGALAX 5
|
||||
|
||||
/*
|
||||
* these device-dependent functions determine what slot corresponds
|
||||
|
@ -104,13 +113,13 @@ static int find_slot_from_contactid(struct mt_device *td)
|
|||
|
||||
struct mt_class mt_classes[] = {
|
||||
{ .name = MT_CLS_DEFAULT,
|
||||
.quirks = MT_QUIRK_VALID_IS_INRANGE,
|
||||
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP,
|
||||
.maxcontacts = 10 },
|
||||
{ .name = MT_CLS_DUAL1,
|
||||
{ .name = MT_CLS_DUAL_INRANGE_CONTACTID,
|
||||
.quirks = MT_QUIRK_VALID_IS_INRANGE |
|
||||
MT_QUIRK_SLOT_IS_CONTACTID,
|
||||
.maxcontacts = 2 },
|
||||
{ .name = MT_CLS_DUAL2,
|
||||
{ .name = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
|
||||
.quirks = MT_QUIRK_VALID_IS_INRANGE |
|
||||
MT_QUIRK_SLOT_IS_CONTACTNUMBER,
|
||||
.maxcontacts = 2 },
|
||||
|
@ -119,10 +128,18 @@ struct mt_class mt_classes[] = {
|
|||
MT_QUIRK_CYPRESS,
|
||||
.maxcontacts = 10 },
|
||||
|
||||
{ .name = MT_CLS_EGALAX,
|
||||
.quirks = MT_QUIRK_SLOT_IS_CONTACTID |
|
||||
MT_QUIRK_VALID_IS_INRANGE |
|
||||
MT_QUIRK_EGALAX_XYZ_FIXUP,
|
||||
.maxcontacts = 2,
|
||||
.sn_move = 4096,
|
||||
.sn_pressure = 32,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static void mt_feature_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
static void mt_feature_mapping(struct hid_device *hdev,
|
||||
struct hid_field *field, struct hid_usage *usage)
|
||||
{
|
||||
if (usage->hid == HID_DG_INPUTMODE) {
|
||||
|
@ -146,11 +163,15 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
{
|
||||
struct mt_device *td = hid_get_drvdata(hdev);
|
||||
struct mt_class *cls = td->mtclass;
|
||||
__s32 quirks = cls->quirks;
|
||||
|
||||
switch (usage->hid & HID_USAGE_PAGE) {
|
||||
|
||||
case HID_UP_GENDESK:
|
||||
switch (usage->hid) {
|
||||
case HID_GD_X:
|
||||
if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
|
||||
field->logical_maximum = 32760;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_X);
|
||||
set_abs(hi->input, ABS_MT_POSITION_X, field,
|
||||
|
@ -160,6 +181,8 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
td->last_slot_field = usage->hid;
|
||||
return 1;
|
||||
case HID_GD_Y:
|
||||
if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
|
||||
field->logical_maximum = 32760;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_Y);
|
||||
set_abs(hi->input, ABS_MT_POSITION_Y, field,
|
||||
|
@ -203,6 +226,8 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
td->last_slot_field = usage->hid;
|
||||
return 1;
|
||||
case HID_DG_TIPPRESSURE:
|
||||
if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
|
||||
field->logical_minimum = 0;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_PRESSURE);
|
||||
set_abs(hi->input, ABS_MT_PRESSURE, field,
|
||||
|
@ -363,8 +388,11 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (usage->hid == td->last_slot_field)
|
||||
if (usage->hid == td->last_slot_field) {
|
||||
mt_complete_slot(td);
|
||||
if (!td->last_field_index)
|
||||
mt_emit_event(td, field->hidinput->input);
|
||||
}
|
||||
|
||||
if (field->index == td->last_field_index
|
||||
&& td->num_received >= td->num_expected)
|
||||
|
@ -466,18 +494,42 @@ static const struct hid_device_id mt_devices[] = {
|
|||
USB_DEVICE_ID_CYPRESS_TRUETOUCH) },
|
||||
|
||||
/* GeneralTouch panel */
|
||||
{ .driver_data = MT_CLS_DUAL2,
|
||||
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
|
||||
USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) },
|
||||
|
||||
/* IRTOUCH panels */
|
||||
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS,
|
||||
USB_DEVICE_ID_IRTOUCH_INFRARED_USB) },
|
||||
|
||||
/* PixCir-based panels */
|
||||
{ .driver_data = MT_CLS_DUAL1,
|
||||
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_HANVON,
|
||||
USB_DEVICE_ID_HANVON_MULTITOUCH) },
|
||||
{ .driver_data = MT_CLS_DUAL1,
|
||||
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
|
||||
USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) },
|
||||
|
||||
/* Resistive eGalax devices */
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) },
|
||||
|
||||
/* Capacitive eGalax devices */
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) },
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) },
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) },
|
||||
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, mt_devices);
|
||||
|
|
|
@ -110,6 +110,36 @@ static int ntrig_version_string(unsigned char *raw, char *buf)
|
|||
return sprintf(buf, "%u.%u.%u.%u.%u", a, b, c, d, e);
|
||||
}
|
||||
|
||||
static inline int ntrig_get_mode(struct hid_device *hdev)
|
||||
{
|
||||
struct hid_report *report = hdev->report_enum[HID_FEATURE_REPORT].
|
||||
report_id_hash[0x0d];
|
||||
|
||||
if (!report)
|
||||
return -EINVAL;
|
||||
|
||||
usbhid_submit_report(hdev, report, USB_DIR_IN);
|
||||
usbhid_wait_io(hdev);
|
||||
return (int)report->field[0]->value[0];
|
||||
}
|
||||
|
||||
static inline void ntrig_set_mode(struct hid_device *hdev, const int mode)
|
||||
{
|
||||
struct hid_report *report;
|
||||
__u8 mode_commands[4] = { 0xe, 0xf, 0x1b, 0x10 };
|
||||
|
||||
if (mode < 0 || mode > 3)
|
||||
return;
|
||||
|
||||
report = hdev->report_enum[HID_FEATURE_REPORT].
|
||||
report_id_hash[mode_commands[mode]];
|
||||
|
||||
if (!report)
|
||||
return;
|
||||
|
||||
usbhid_submit_report(hdev, report, USB_DIR_IN);
|
||||
}
|
||||
|
||||
static void ntrig_report_version(struct hid_device *hdev)
|
||||
{
|
||||
int ret;
|
||||
|
@ -539,277 +569,288 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
|||
static int ntrig_event (struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct input_dev *input = field->hidinput->input;
|
||||
struct ntrig_data *nd = hid_get_drvdata(hid);
|
||||
struct input_dev *input;
|
||||
|
||||
/* Skip processing if not a claimed input */
|
||||
if (!(hid->claimed & HID_CLAIMED_INPUT))
|
||||
goto not_claimed_input;
|
||||
|
||||
/* This function is being called before the structures are fully
|
||||
* initialized */
|
||||
if(!(field->hidinput && field->hidinput->input))
|
||||
return -EINVAL;
|
||||
|
||||
input = field->hidinput->input;
|
||||
|
||||
/* No special handling needed for the pen */
|
||||
if (field->application == HID_DG_PEN)
|
||||
return 0;
|
||||
|
||||
if (hid->claimed & HID_CLAIMED_INPUT) {
|
||||
switch (usage->hid) {
|
||||
case 0xff000001:
|
||||
/* Tag indicating the start of a multitouch group */
|
||||
nd->reading_mt = 1;
|
||||
nd->first_contact_touch = 0;
|
||||
break;
|
||||
case HID_DG_TIPSWITCH:
|
||||
nd->tipswitch = value;
|
||||
/* Prevent emission of touch until validated */
|
||||
return 1;
|
||||
case HID_DG_CONFIDENCE:
|
||||
nd->confidence = value;
|
||||
break;
|
||||
case HID_GD_X:
|
||||
nd->x = value;
|
||||
/* Clear the contact footer */
|
||||
nd->mt_foot_count = 0;
|
||||
break;
|
||||
case HID_GD_Y:
|
||||
nd->y = value;
|
||||
break;
|
||||
case HID_DG_CONTACTID:
|
||||
nd->id = value;
|
||||
break;
|
||||
case HID_DG_WIDTH:
|
||||
nd->w = value;
|
||||
break;
|
||||
case HID_DG_HEIGHT:
|
||||
nd->h = value;
|
||||
switch (usage->hid) {
|
||||
case 0xff000001:
|
||||
/* Tag indicating the start of a multitouch group */
|
||||
nd->reading_mt = 1;
|
||||
nd->first_contact_touch = 0;
|
||||
break;
|
||||
case HID_DG_TIPSWITCH:
|
||||
nd->tipswitch = value;
|
||||
/* Prevent emission of touch until validated */
|
||||
return 1;
|
||||
case HID_DG_CONFIDENCE:
|
||||
nd->confidence = value;
|
||||
break;
|
||||
case HID_GD_X:
|
||||
nd->x = value;
|
||||
/* Clear the contact footer */
|
||||
nd->mt_foot_count = 0;
|
||||
break;
|
||||
case HID_GD_Y:
|
||||
nd->y = value;
|
||||
break;
|
||||
case HID_DG_CONTACTID:
|
||||
nd->id = value;
|
||||
break;
|
||||
case HID_DG_WIDTH:
|
||||
nd->w = value;
|
||||
break;
|
||||
case HID_DG_HEIGHT:
|
||||
nd->h = value;
|
||||
/*
|
||||
* when in single touch mode, this is the last
|
||||
* report received in a finger event. We want
|
||||
* to emit a normal (X, Y) position
|
||||
*/
|
||||
if (!nd->reading_mt) {
|
||||
/*
|
||||
* when in single touch mode, this is the last
|
||||
* report received in a finger event. We want
|
||||
* to emit a normal (X, Y) position
|
||||
* TipSwitch indicates the presence of a
|
||||
* finger in single touch mode.
|
||||
*/
|
||||
if (!nd->reading_mt) {
|
||||
/*
|
||||
* TipSwitch indicates the presence of a
|
||||
* finger in single touch mode.
|
||||
*/
|
||||
input_report_key(input, BTN_TOUCH,
|
||||
nd->tipswitch);
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP,
|
||||
nd->tipswitch);
|
||||
input_event(input, EV_ABS, ABS_X, nd->x);
|
||||
input_event(input, EV_ABS, ABS_Y, nd->y);
|
||||
}
|
||||
input_report_key(input, BTN_TOUCH,
|
||||
nd->tipswitch);
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP,
|
||||
nd->tipswitch);
|
||||
input_event(input, EV_ABS, ABS_X, nd->x);
|
||||
input_event(input, EV_ABS, ABS_Y, nd->y);
|
||||
}
|
||||
break;
|
||||
case 0xff000002:
|
||||
/*
|
||||
* we receive this when the device is in multitouch
|
||||
* mode. The first of the three values tagged with
|
||||
* this usage tells if the contact point is real
|
||||
* or a placeholder
|
||||
*/
|
||||
|
||||
/* Shouldn't get more than 4 footer packets, so skip */
|
||||
if (nd->mt_foot_count >= 4)
|
||||
break;
|
||||
case 0xff000002:
|
||||
|
||||
nd->mt_footer[nd->mt_foot_count++] = value;
|
||||
|
||||
/* if the footer isn't complete break */
|
||||
if (nd->mt_foot_count != 4)
|
||||
break;
|
||||
|
||||
/* Pen activity signal. */
|
||||
if (nd->mt_footer[2]) {
|
||||
/*
|
||||
* we receive this when the device is in multitouch
|
||||
* mode. The first of the three values tagged with
|
||||
* this usage tells if the contact point is real
|
||||
* or a placeholder
|
||||
* When the pen deactivates touch, we see a
|
||||
* bogus frame with ContactCount > 0.
|
||||
* We can
|
||||
* save a bit of work by ensuring act_state < 0
|
||||
* even if deactivation slack is turned off.
|
||||
*/
|
||||
nd->act_state = deactivate_slack - 1;
|
||||
nd->confidence = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Shouldn't get more than 4 footer packets, so skip */
|
||||
if (nd->mt_foot_count >= 4)
|
||||
break;
|
||||
|
||||
nd->mt_footer[nd->mt_foot_count++] = value;
|
||||
|
||||
/* if the footer isn't complete break */
|
||||
if (nd->mt_foot_count != 4)
|
||||
break;
|
||||
|
||||
/* Pen activity signal. */
|
||||
if (nd->mt_footer[2]) {
|
||||
/*
|
||||
* When the pen deactivates touch, we see a
|
||||
* bogus frame with ContactCount > 0.
|
||||
* We can
|
||||
* save a bit of work by ensuring act_state < 0
|
||||
* even if deactivation slack is turned off.
|
||||
*/
|
||||
nd->act_state = deactivate_slack - 1;
|
||||
/*
|
||||
* The first footer value indicates the presence of a
|
||||
* finger.
|
||||
*/
|
||||
if (nd->mt_footer[0]) {
|
||||
/*
|
||||
* We do not want to process contacts under
|
||||
* the size threshold, but do not want to
|
||||
* ignore them for activation state
|
||||
*/
|
||||
if (nd->w < nd->min_width ||
|
||||
nd->h < nd->min_height)
|
||||
nd->confidence = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The first footer value indicates the presence of a
|
||||
* finger.
|
||||
*/
|
||||
if (nd->mt_footer[0]) {
|
||||
/*
|
||||
* We do not want to process contacts under
|
||||
* the size threshold, but do not want to
|
||||
* ignore them for activation state
|
||||
*/
|
||||
if (nd->w < nd->min_width ||
|
||||
nd->h < nd->min_height)
|
||||
nd->confidence = 0;
|
||||
} else
|
||||
break;
|
||||
|
||||
if (nd->act_state > 0) {
|
||||
/*
|
||||
* Contact meets the activation size threshold
|
||||
*/
|
||||
if (nd->w >= nd->activation_width &&
|
||||
nd->h >= nd->activation_height) {
|
||||
if (nd->id)
|
||||
/*
|
||||
* first contact, activate now
|
||||
*/
|
||||
nd->act_state = 0;
|
||||
else {
|
||||
/*
|
||||
* avoid corrupting this frame
|
||||
* but ensure next frame will
|
||||
* be active
|
||||
*/
|
||||
nd->act_state = 1;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
/*
|
||||
* Defer adjusting the activation state
|
||||
* until the end of the frame.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
/* Discarding this contact */
|
||||
if (!nd->confidence)
|
||||
break;
|
||||
|
||||
/* emit a normal (X, Y) for the first point only */
|
||||
if (nd->id == 0) {
|
||||
/*
|
||||
* TipSwitch is superfluous in multitouch
|
||||
* mode. The footer events tell us
|
||||
* if there is a finger on the screen or
|
||||
* not.
|
||||
*/
|
||||
nd->first_contact_touch = nd->confidence;
|
||||
input_event(input, EV_ABS, ABS_X, nd->x);
|
||||
input_event(input, EV_ABS, ABS_Y, nd->y);
|
||||
}
|
||||
|
||||
/* Emit MT events */
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y);
|
||||
|
||||
/*
|
||||
* Translate from height and width to size
|
||||
* and orientation.
|
||||
*/
|
||||
if (nd->w > nd->h) {
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_ORIENTATION, 1);
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_TOUCH_MAJOR, nd->w);
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_TOUCH_MINOR, nd->h);
|
||||
} else {
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_ORIENTATION, 0);
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_TOUCH_MAJOR, nd->h);
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_TOUCH_MINOR, nd->w);
|
||||
}
|
||||
input_mt_sync(field->hidinput->input);
|
||||
} else
|
||||
break;
|
||||
|
||||
case HID_DG_CONTACTCOUNT: /* End of a multitouch group */
|
||||
if (!nd->reading_mt) /* Just to be sure */
|
||||
break;
|
||||
|
||||
nd->reading_mt = 0;
|
||||
|
||||
|
||||
if (nd->act_state > 0) {
|
||||
/*
|
||||
* Activation state machine logic:
|
||||
*
|
||||
* Fundamental states:
|
||||
* state > 0: Inactive
|
||||
* state <= 0: Active
|
||||
* state < -deactivate_slack:
|
||||
* Pen termination of touch
|
||||
*
|
||||
* Specific values of interest
|
||||
* state == activate_slack
|
||||
* no valid input since the last reset
|
||||
*
|
||||
* state == 0
|
||||
* general operational state
|
||||
*
|
||||
* state == -deactivate_slack
|
||||
* read sufficient empty frames to accept
|
||||
* the end of input and reset
|
||||
* Contact meets the activation size threshold
|
||||
*/
|
||||
|
||||
if (nd->act_state > 0) { /* Currently inactive */
|
||||
if (value)
|
||||
if (nd->w >= nd->activation_width &&
|
||||
nd->h >= nd->activation_height) {
|
||||
if (nd->id)
|
||||
/*
|
||||
* Consider each live contact as
|
||||
* evidence of intentional activity.
|
||||
*/
|
||||
nd->act_state = (nd->act_state > value)
|
||||
? nd->act_state - value
|
||||
: 0;
|
||||
else
|
||||
/*
|
||||
* Empty frame before we hit the
|
||||
* activity threshold, reset.
|
||||
*/
|
||||
nd->act_state = nd->activate_slack;
|
||||
|
||||
/*
|
||||
* Entered this block inactive and no
|
||||
* coordinates sent this frame, so hold off
|
||||
* on button state.
|
||||
*/
|
||||
break;
|
||||
} else { /* Currently active */
|
||||
if (value && nd->act_state >=
|
||||
nd->deactivate_slack)
|
||||
/*
|
||||
* Live point: clear accumulated
|
||||
* deactivation count.
|
||||
* first contact, activate now
|
||||
*/
|
||||
nd->act_state = 0;
|
||||
else if (nd->act_state <= nd->deactivate_slack)
|
||||
else {
|
||||
/*
|
||||
* We've consumed the deactivation
|
||||
* slack, time to deactivate and reset.
|
||||
* avoid corrupting this frame
|
||||
* but ensure next frame will
|
||||
* be active
|
||||
*/
|
||||
nd->act_state =
|
||||
nd->activate_slack;
|
||||
else { /* Move towards deactivation */
|
||||
nd->act_state--;
|
||||
nd->act_state = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nd->first_contact_touch && nd->act_state <= 0) {
|
||||
} else
|
||||
/*
|
||||
* Check to see if we're ready to start
|
||||
* emitting touch events.
|
||||
*
|
||||
* Note: activation slack will decrease over
|
||||
* the course of the frame, and it will be
|
||||
* inconsistent from the start to the end of
|
||||
* the frame. However if the frame starts
|
||||
* with slack, first_contact_touch will still
|
||||
* be 0 and we will not get to this point.
|
||||
* Defer adjusting the activation state
|
||||
* until the end of the frame.
|
||||
*/
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP, 1);
|
||||
input_report_key(input, BTN_TOUCH, 1);
|
||||
} else {
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP, 0);
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Discarding this contact */
|
||||
if (!nd->confidence)
|
||||
break;
|
||||
|
||||
default:
|
||||
/* fall-back to the generic hidinput handling */
|
||||
return 0;
|
||||
/* emit a normal (X, Y) for the first point only */
|
||||
if (nd->id == 0) {
|
||||
/*
|
||||
* TipSwitch is superfluous in multitouch
|
||||
* mode. The footer events tell us
|
||||
* if there is a finger on the screen or
|
||||
* not.
|
||||
*/
|
||||
nd->first_contact_touch = nd->confidence;
|
||||
input_event(input, EV_ABS, ABS_X, nd->x);
|
||||
input_event(input, EV_ABS, ABS_Y, nd->y);
|
||||
}
|
||||
|
||||
/* Emit MT events */
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y);
|
||||
|
||||
/*
|
||||
* Translate from height and width to size
|
||||
* and orientation.
|
||||
*/
|
||||
if (nd->w > nd->h) {
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_ORIENTATION, 1);
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_TOUCH_MAJOR, nd->w);
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_TOUCH_MINOR, nd->h);
|
||||
} else {
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_ORIENTATION, 0);
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_TOUCH_MAJOR, nd->h);
|
||||
input_event(input, EV_ABS,
|
||||
ABS_MT_TOUCH_MINOR, nd->w);
|
||||
}
|
||||
input_mt_sync(field->hidinput->input);
|
||||
break;
|
||||
|
||||
case HID_DG_CONTACTCOUNT: /* End of a multitouch group */
|
||||
if (!nd->reading_mt) /* Just to be sure */
|
||||
break;
|
||||
|
||||
nd->reading_mt = 0;
|
||||
|
||||
|
||||
/*
|
||||
* Activation state machine logic:
|
||||
*
|
||||
* Fundamental states:
|
||||
* state > 0: Inactive
|
||||
* state <= 0: Active
|
||||
* state < -deactivate_slack:
|
||||
* Pen termination of touch
|
||||
*
|
||||
* Specific values of interest
|
||||
* state == activate_slack
|
||||
* no valid input since the last reset
|
||||
*
|
||||
* state == 0
|
||||
* general operational state
|
||||
*
|
||||
* state == -deactivate_slack
|
||||
* read sufficient empty frames to accept
|
||||
* the end of input and reset
|
||||
*/
|
||||
|
||||
if (nd->act_state > 0) { /* Currently inactive */
|
||||
if (value)
|
||||
/*
|
||||
* Consider each live contact as
|
||||
* evidence of intentional activity.
|
||||
*/
|
||||
nd->act_state = (nd->act_state > value)
|
||||
? nd->act_state - value
|
||||
: 0;
|
||||
else
|
||||
/*
|
||||
* Empty frame before we hit the
|
||||
* activity threshold, reset.
|
||||
*/
|
||||
nd->act_state = nd->activate_slack;
|
||||
|
||||
/*
|
||||
* Entered this block inactive and no
|
||||
* coordinates sent this frame, so hold off
|
||||
* on button state.
|
||||
*/
|
||||
break;
|
||||
} else { /* Currently active */
|
||||
if (value && nd->act_state >=
|
||||
nd->deactivate_slack)
|
||||
/*
|
||||
* Live point: clear accumulated
|
||||
* deactivation count.
|
||||
*/
|
||||
nd->act_state = 0;
|
||||
else if (nd->act_state <= nd->deactivate_slack)
|
||||
/*
|
||||
* We've consumed the deactivation
|
||||
* slack, time to deactivate and reset.
|
||||
*/
|
||||
nd->act_state =
|
||||
nd->activate_slack;
|
||||
else { /* Move towards deactivation */
|
||||
nd->act_state--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nd->first_contact_touch && nd->act_state <= 0) {
|
||||
/*
|
||||
* Check to see if we're ready to start
|
||||
* emitting touch events.
|
||||
*
|
||||
* Note: activation slack will decrease over
|
||||
* the course of the frame, and it will be
|
||||
* inconsistent from the start to the end of
|
||||
* the frame. However if the frame starts
|
||||
* with slack, first_contact_touch will still
|
||||
* be 0 and we will not get to this point.
|
||||
*/
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP, 1);
|
||||
input_report_key(input, BTN_TOUCH, 1);
|
||||
} else {
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP, 0);
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* fall-back to the generic hidinput handling */
|
||||
return 0;
|
||||
}
|
||||
|
||||
not_claimed_input:
|
||||
|
||||
/* we have handled the hidinput part, now remains hiddev */
|
||||
if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_hid_event)
|
||||
hid->hiddev_hid_event(hid, field, usage, value);
|
||||
|
@ -826,7 +867,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
struct hid_report *report;
|
||||
|
||||
if (id->driver_data)
|
||||
hdev->quirks |= HID_QUIRK_MULTI_INPUT;
|
||||
hdev->quirks |= HID_QUIRK_MULTI_INPUT
|
||||
| HID_QUIRK_NO_INIT_REPORTS;
|
||||
|
||||
nd = kmalloc(sizeof(struct ntrig_data), GFP_KERNEL);
|
||||
if (!nd) {
|
||||
|
@ -893,8 +935,19 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
|
||||
/* This is needed for devices with more recent firmware versions */
|
||||
report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0x0a];
|
||||
if (report)
|
||||
usbhid_submit_report(hdev, report, USB_DIR_OUT);
|
||||
if (report) {
|
||||
/* Let the device settle to ensure the wakeup message gets
|
||||
* through */
|
||||
usbhid_wait_io(hdev);
|
||||
usbhid_submit_report(hdev, report, USB_DIR_IN);
|
||||
|
||||
/*
|
||||
* Sanity check: if the current mode is invalid reset it to
|
||||
* something reasonable.
|
||||
*/
|
||||
if (ntrig_get_mode(hdev) >= 4)
|
||||
ntrig_set_mode(hdev, 3);
|
||||
}
|
||||
|
||||
ntrig_report_version(hdev);
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
* HID driver for Ortek WKB-2000 (wireless keyboard + mouse trackpad).
|
||||
* Fixes LogicalMaximum error in USB report description, see
|
||||
* http://bugzilla.kernel.org/show_bug.cgi?id=14787
|
||||
* HID driver for Ortek PKB-1700/WKB-2000 (wireless keyboard + mouse trackpad).
|
||||
* Fixes LogicalMaximum error in HID report description.
|
||||
*
|
||||
* Copyright (c) 2010 Johnathon Harris <jmharris@gmail.com>
|
||||
*/
|
||||
|
@ -30,6 +29,7 @@ static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
}
|
||||
|
||||
static const struct hid_device_id ortek_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
* Roccat Arvo driver for Linux
|
||||
*
|
||||
* Copyright (c) 2011 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 Arvo is a gamer keyboard with 5 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-arvo.h"
|
||||
|
||||
static struct class *arvo_class;
|
||||
|
||||
static ssize_t arvo_sysfs_show_mode_key(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct arvo_device *arvo =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
struct usb_device *usb_dev =
|
||||
interface_to_usbdev(to_usb_interface(dev->parent->parent));
|
||||
struct arvo_mode_key temp_buf;
|
||||
int retval;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_MODE_KEY,
|
||||
&temp_buf, sizeof(struct arvo_mode_key));
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", temp_buf.state);
|
||||
}
|
||||
|
||||
static ssize_t arvo_sysfs_set_mode_key(struct device *dev,
|
||||
struct device_attribute *attr, char const *buf, size_t size)
|
||||
{
|
||||
struct arvo_device *arvo =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
struct usb_device *usb_dev =
|
||||
interface_to_usbdev(to_usb_interface(dev->parent->parent));
|
||||
struct arvo_mode_key temp_buf;
|
||||
unsigned long state;
|
||||
int retval;
|
||||
|
||||
retval = strict_strtoul(buf, 10, &state);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
temp_buf.command = ARVO_COMMAND_MODE_KEY;
|
||||
temp_buf.state = state;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_MODE_KEY,
|
||||
&temp_buf, sizeof(struct arvo_mode_key));
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t arvo_sysfs_show_key_mask(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct arvo_device *arvo =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
struct usb_device *usb_dev =
|
||||
interface_to_usbdev(to_usb_interface(dev->parent->parent));
|
||||
struct arvo_key_mask temp_buf;
|
||||
int retval;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_KEY_MASK,
|
||||
&temp_buf, sizeof(struct arvo_key_mask));
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", temp_buf.key_mask);
|
||||
}
|
||||
|
||||
static ssize_t arvo_sysfs_set_key_mask(struct device *dev,
|
||||
struct device_attribute *attr, char const *buf, size_t size)
|
||||
{
|
||||
struct arvo_device *arvo =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
struct usb_device *usb_dev =
|
||||
interface_to_usbdev(to_usb_interface(dev->parent->parent));
|
||||
struct arvo_key_mask temp_buf;
|
||||
unsigned long key_mask;
|
||||
int retval;
|
||||
|
||||
retval = strict_strtoul(buf, 10, &key_mask);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
temp_buf.command = ARVO_COMMAND_KEY_MASK;
|
||||
temp_buf.key_mask = key_mask;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_KEY_MASK,
|
||||
&temp_buf, sizeof(struct arvo_key_mask));
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/* retval is 1-5 on success, < 0 on error */
|
||||
static int arvo_get_actual_profile(struct usb_device *usb_dev)
|
||||
{
|
||||
struct arvo_actual_profile temp_buf;
|
||||
int retval;
|
||||
|
||||
retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE,
|
||||
&temp_buf, sizeof(struct arvo_actual_profile));
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return temp_buf.actual_profile;
|
||||
}
|
||||
|
||||
static ssize_t arvo_sysfs_show_actual_profile(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct arvo_device *arvo =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", arvo->actual_profile);
|
||||
}
|
||||
|
||||
static ssize_t arvo_sysfs_set_actual_profile(struct device *dev,
|
||||
struct device_attribute *attr, char const *buf, size_t size)
|
||||
{
|
||||
struct arvo_device *arvo =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
struct usb_device *usb_dev =
|
||||
interface_to_usbdev(to_usb_interface(dev->parent->parent));
|
||||
struct arvo_actual_profile temp_buf;
|
||||
unsigned long profile;
|
||||
int retval;
|
||||
|
||||
retval = strict_strtoul(buf, 10, &profile);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
temp_buf.command = ARVO_COMMAND_ACTUAL_PROFILE;
|
||||
temp_buf.actual_profile = profile;
|
||||
|
||||
mutex_lock(&arvo->arvo_lock);
|
||||
retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE,
|
||||
&temp_buf, sizeof(struct arvo_actual_profile));
|
||||
if (!retval) {
|
||||
arvo->actual_profile = profile;
|
||||
retval = size;
|
||||
}
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t arvo_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 arvo_device *arvo = 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(&arvo->arvo_lock);
|
||||
retval = roccat_common_send(usb_dev, command, buf, real_size);
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
|
||||
return (retval ? retval : real_size);
|
||||
}
|
||||
|
||||
static ssize_t arvo_sysfs_read(struct file *fp,
|
||||
struct kobject *kobj, void *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 arvo_device *arvo = 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(&arvo->arvo_lock);
|
||||
retval = roccat_common_receive(usb_dev, command, buf, real_size);
|
||||
mutex_unlock(&arvo->arvo_lock);
|
||||
|
||||
return (retval ? retval : real_size);
|
||||
}
|
||||
|
||||
static ssize_t arvo_sysfs_write_button(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return arvo_sysfs_write(fp, kobj, buf, off, count,
|
||||
sizeof(struct arvo_button), ARVO_USB_COMMAND_BUTTON);
|
||||
}
|
||||
|
||||
static ssize_t arvo_sysfs_read_info(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return arvo_sysfs_read(fp, kobj, buf, off, count,
|
||||
sizeof(struct arvo_info), ARVO_USB_COMMAND_INFO);
|
||||
}
|
||||
|
||||
|
||||
static struct device_attribute arvo_attributes[] = {
|
||||
__ATTR(mode_key, 0660,
|
||||
arvo_sysfs_show_mode_key, arvo_sysfs_set_mode_key),
|
||||
__ATTR(key_mask, 0660,
|
||||
arvo_sysfs_show_key_mask, arvo_sysfs_set_key_mask),
|
||||
__ATTR(actual_profile, 0660,
|
||||
arvo_sysfs_show_actual_profile,
|
||||
arvo_sysfs_set_actual_profile),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static struct bin_attribute arvo_bin_attributes[] = {
|
||||
{
|
||||
.attr = { .name = "button", .mode = 0220 },
|
||||
.size = sizeof(struct arvo_button),
|
||||
.write = arvo_sysfs_write_button
|
||||
},
|
||||
{
|
||||
.attr = { .name = "info", .mode = 0440 },
|
||||
.size = sizeof(struct arvo_info),
|
||||
.read = arvo_sysfs_read_info
|
||||
},
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static int arvo_init_arvo_device_struct(struct usb_device *usb_dev,
|
||||
struct arvo_device *arvo)
|
||||
{
|
||||
int retval;
|
||||
|
||||
mutex_init(&arvo->arvo_lock);
|
||||
|
||||
retval = arvo_get_actual_profile(usb_dev);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
arvo->actual_profile = retval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arvo_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 arvo_device *arvo;
|
||||
int retval;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
== USB_INTERFACE_PROTOCOL_KEYBOARD) {
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
arvo = kzalloc(sizeof(*arvo), GFP_KERNEL);
|
||||
if (!arvo) {
|
||||
hid_err(hdev, "can't alloc device descriptor\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
hid_set_drvdata(hdev, arvo);
|
||||
|
||||
retval = arvo_init_arvo_device_struct(usb_dev, arvo);
|
||||
if (retval) {
|
||||
hid_err(hdev, "couldn't init struct arvo_device\n");
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
retval = roccat_connect(arvo_class, hdev,
|
||||
sizeof(struct arvo_roccat_report));
|
||||
if (retval < 0) {
|
||||
hid_err(hdev, "couldn't init char dev\n");
|
||||
} else {
|
||||
arvo->chrdev_minor = retval;
|
||||
arvo->roccat_claimed = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit_free:
|
||||
kfree(arvo);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void arvo_remove_specials(struct hid_device *hdev)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
struct arvo_device *arvo;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
== USB_INTERFACE_PROTOCOL_KEYBOARD)
|
||||
return;
|
||||
|
||||
arvo = hid_get_drvdata(hdev);
|
||||
if (arvo->roccat_claimed)
|
||||
roccat_disconnect(arvo->chrdev_minor);
|
||||
kfree(arvo);
|
||||
}
|
||||
|
||||
static int arvo_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 = arvo_init_specials(hdev);
|
||||
if (retval) {
|
||||
hid_err(hdev, "couldn't install keyboard\n");
|
||||
goto exit_stop;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_stop:
|
||||
hid_hw_stop(hdev);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void arvo_remove(struct hid_device *hdev)
|
||||
{
|
||||
arvo_remove_specials(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static void arvo_report_to_chrdev(struct arvo_device const *arvo,
|
||||
u8 const *data)
|
||||
{
|
||||
struct arvo_special_report const *special_report;
|
||||
struct arvo_roccat_report roccat_report;
|
||||
|
||||
special_report = (struct arvo_special_report const *)data;
|
||||
|
||||
roccat_report.profile = arvo->actual_profile;
|
||||
roccat_report.button = special_report->event &
|
||||
ARVO_SPECIAL_REPORT_EVENT_MASK_BUTTON;
|
||||
if ((special_report->event & ARVO_SPECIAL_REPORT_EVENT_MASK_ACTION) ==
|
||||
ARVO_SPECIAL_REPORT_EVENT_ACTION_PRESS)
|
||||
roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_PRESS;
|
||||
else
|
||||
roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_RELEASE;
|
||||
|
||||
roccat_report_event(arvo->chrdev_minor,
|
||||
(uint8_t const *)&roccat_report);
|
||||
}
|
||||
|
||||
static int arvo_raw_event(struct hid_device *hdev,
|
||||
struct hid_report *report, u8 *data, int size)
|
||||
{
|
||||
struct arvo_device *arvo = hid_get_drvdata(hdev);
|
||||
|
||||
if (size != 3)
|
||||
return 0;
|
||||
|
||||
if (arvo->roccat_claimed)
|
||||
arvo_report_to_chrdev(arvo, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id arvo_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(hid, arvo_devices);
|
||||
|
||||
static struct hid_driver arvo_driver = {
|
||||
.name = "arvo",
|
||||
.id_table = arvo_devices,
|
||||
.probe = arvo_probe,
|
||||
.remove = arvo_remove,
|
||||
.raw_event = arvo_raw_event
|
||||
};
|
||||
|
||||
static int __init arvo_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
arvo_class = class_create(THIS_MODULE, "arvo");
|
||||
if (IS_ERR(arvo_class))
|
||||
return PTR_ERR(arvo_class);
|
||||
arvo_class->dev_attrs = arvo_attributes;
|
||||
arvo_class->dev_bin_attrs = arvo_bin_attributes;
|
||||
|
||||
retval = hid_register_driver(&arvo_driver);
|
||||
if (retval)
|
||||
class_destroy(arvo_class);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit arvo_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&arvo_driver);
|
||||
class_destroy(arvo_class);
|
||||
}
|
||||
|
||||
module_init(arvo_init);
|
||||
module_exit(arvo_exit);
|
||||
|
||||
MODULE_AUTHOR("Stefan Achatz");
|
||||
MODULE_DESCRIPTION("USB Roccat Arvo driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,98 @@
|
|||
#ifndef __HID_ROCCAT_ARVO_H
|
||||
#define __HID_ROCCAT_ARVO_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011 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>
|
||||
|
||||
struct arvo_mode_key { /* 2 bytes */
|
||||
uint8_t command; /* ARVO_COMMAND_MODE_KEY */
|
||||
uint8_t state;
|
||||
} __packed;
|
||||
|
||||
struct arvo_button {
|
||||
uint8_t unknown[24];
|
||||
} __packed;
|
||||
|
||||
struct arvo_info {
|
||||
uint8_t unknown[8];
|
||||
} __packed;
|
||||
|
||||
struct arvo_key_mask { /* 2 bytes */
|
||||
uint8_t command; /* ARVO_COMMAND_KEY_MASK */
|
||||
uint8_t key_mask;
|
||||
} __packed;
|
||||
|
||||
/* selected profile is persistent */
|
||||
struct arvo_actual_profile { /* 2 bytes */
|
||||
uint8_t command; /* ARVO_COMMAND_ACTUAL_PROFILE */
|
||||
uint8_t actual_profile;
|
||||
} __packed;
|
||||
|
||||
enum arvo_commands {
|
||||
ARVO_COMMAND_MODE_KEY = 0x3,
|
||||
ARVO_COMMAND_BUTTON = 0x4,
|
||||
ARVO_COMMAND_INFO = 0x5,
|
||||
ARVO_COMMAND_KEY_MASK = 0x6,
|
||||
ARVO_COMMAND_ACTUAL_PROFILE = 0x7,
|
||||
};
|
||||
|
||||
enum arvo_usb_commands {
|
||||
ARVO_USB_COMMAND_MODE_KEY = 0x303,
|
||||
/*
|
||||
* read/write
|
||||
* Read uses both index bytes as profile/key indexes
|
||||
* Write has index 0, profile/key is determined by payload
|
||||
*/
|
||||
ARVO_USB_COMMAND_BUTTON = 0x304,
|
||||
ARVO_USB_COMMAND_INFO = 0x305,
|
||||
ARVO_USB_COMMAND_KEY_MASK = 0x306,
|
||||
ARVO_USB_COMMAND_ACTUAL_PROFILE = 0x307,
|
||||
};
|
||||
|
||||
struct arvo_special_report {
|
||||
uint8_t unknown1; /* always 0x01 */
|
||||
uint8_t event;
|
||||
uint8_t unknown2; /* always 0x70 */
|
||||
} __packed;
|
||||
|
||||
enum arvo_special_report_events {
|
||||
ARVO_SPECIAL_REPORT_EVENT_ACTION_PRESS = 0x10,
|
||||
ARVO_SPECIAL_REPORT_EVENT_ACTION_RELEASE = 0x0,
|
||||
};
|
||||
|
||||
enum arvo_special_report_event_masks {
|
||||
ARVO_SPECIAL_REPORT_EVENT_MASK_ACTION = 0xf0,
|
||||
ARVO_SPECIAL_REPORT_EVENT_MASK_BUTTON = 0x0f,
|
||||
};
|
||||
|
||||
struct arvo_roccat_report {
|
||||
uint8_t profile;
|
||||
uint8_t button;
|
||||
uint8_t action;
|
||||
} __packed;
|
||||
|
||||
enum arvo_roccat_report_action {
|
||||
ARVO_ROCCAT_REPORT_ACTION_RELEASE = 0,
|
||||
ARVO_ROCCAT_REPORT_ACTION_PRESS = 1,
|
||||
};
|
||||
|
||||
struct arvo_device {
|
||||
int roccat_claimed;
|
||||
int chrdev_minor;
|
||||
|
||||
struct mutex arvo_lock;
|
||||
|
||||
int actual_profile;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Roccat common functions for device specific drivers
|
||||
*
|
||||
* Copyright (c) 2011 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/slab.h>
|
||||
#include "hid-roccat-common.h"
|
||||
|
||||
int roccat_common_receive(struct usb_device *usb_dev, uint usb_command,
|
||||
void *data, uint size)
|
||||
{
|
||||
char *buf;
|
||||
int len;
|
||||
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
memcpy(data, buf, size);
|
||||
kfree(buf);
|
||||
return ((len < 0) ? len : ((len != size) ? -EIO : 0));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(roccat_common_receive);
|
||||
|
||||
int roccat_common_send(struct usb_device *usb_dev, uint usb_command,
|
||||
void const *data, uint size)
|
||||
{
|
||||
char *buf;
|
||||
int len;
|
||||
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buf, data, size);
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
kfree(buf);
|
||||
return ((len < 0) ? len : ((len != size) ? -EIO : 0));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(roccat_common_send);
|
||||
|
||||
MODULE_AUTHOR("Stefan Achatz");
|
||||
MODULE_DESCRIPTION("USB Roccat common driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef __HID_ROCCAT_COMMON_H
|
||||
#define __HID_ROCCAT_COMMON_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011 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/usb.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
int roccat_common_receive(struct usb_device *usb_dev, uint usb_command,
|
||||
void *data, uint size);
|
||||
int roccat_common_send(struct usb_device *usb_dev, uint usb_command,
|
||||
void const *data, uint size);
|
||||
|
||||
#endif
|
|
@ -28,11 +28,11 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-roccat.h>
|
||||
#include "hid-ids.h"
|
||||
#include "hid-roccat.h"
|
||||
#include "hid-roccat-common.h"
|
||||
#include "hid-roccat-kone.h"
|
||||
|
||||
static uint profile_numbers[5] = {0, 1, 2, 3, 4};
|
||||
|
@ -58,12 +58,8 @@ static void kone_set_settings_checksum(struct kone_settings *settings)
|
|||
*/
|
||||
static int kone_check_write(struct usb_device *usb_dev)
|
||||
{
|
||||
int len;
|
||||
unsigned char *data;
|
||||
|
||||
data = kmalloc(1, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
int retval;
|
||||
uint8_t data;
|
||||
|
||||
do {
|
||||
/*
|
||||
|
@ -72,56 +68,36 @@ static int kone_check_write(struct usb_device *usb_dev)
|
|||
*/
|
||||
msleep(80);
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE |
|
||||
USB_DIR_IN,
|
||||
kone_command_confirm_write, 0, data, 1,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (len != 1) {
|
||||
kfree(data);
|
||||
return -EIO;
|
||||
}
|
||||
retval = roccat_common_receive(usb_dev,
|
||||
kone_command_confirm_write, &data, 1);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/*
|
||||
* value of 3 seems to mean something like
|
||||
* "not finished yet, but it looks good"
|
||||
* So check again after a moment.
|
||||
*/
|
||||
} while (*data == 3);
|
||||
} while (data == 3);
|
||||
|
||||
if (*data == 1) { /* everything alright */
|
||||
kfree(data);
|
||||
if (data == 1) /* everything alright */
|
||||
return 0;
|
||||
} else { /* unknown answer */
|
||||
hid_err(usb_dev, "got retval %d when checking write\n", *data);
|
||||
kfree(data);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* unknown answer */
|
||||
hid_err(usb_dev, "got retval %d when checking write\n", data);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads settings from mouse and stores it in @buf
|
||||
* @buf has to be alloced with GFP_KERNEL
|
||||
* On success returns 0
|
||||
* On failure returns errno
|
||||
*/
|
||||
static int kone_get_settings(struct usb_device *usb_dev,
|
||||
struct kone_settings *buf)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
kone_command_settings, 0, buf,
|
||||
sizeof(struct kone_settings), USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (len != sizeof(struct kone_settings))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
return roccat_common_receive(usb_dev, kone_command_settings, buf,
|
||||
sizeof(struct kone_settings));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -132,22 +108,12 @@ static int kone_get_settings(struct usb_device *usb_dev,
|
|||
static int kone_set_settings(struct usb_device *usb_dev,
|
||||
struct kone_settings const *settings)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
kone_command_settings, 0, (char *)settings,
|
||||
sizeof(struct kone_settings),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (len != sizeof(struct kone_settings))
|
||||
return -EIO;
|
||||
|
||||
if (kone_check_write(usb_dev))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
int retval;
|
||||
retval = roccat_common_send(usb_dev, kone_command_settings,
|
||||
settings, sizeof(struct kone_settings));
|
||||
if (retval)
|
||||
return retval;
|
||||
return kone_check_write(usb_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -193,7 +159,7 @@ static int kone_set_profile(struct usb_device *usb_dev,
|
|||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
kone_command_profile, number, (char *)profile,
|
||||
kone_command_profile, number, (void *)profile,
|
||||
sizeof(struct kone_profile),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
|
@ -213,24 +179,15 @@ static int kone_set_profile(struct usb_device *usb_dev,
|
|||
*/
|
||||
static int kone_get_weight(struct usb_device *usb_dev, int *result)
|
||||
{
|
||||
int len;
|
||||
uint8_t *data;
|
||||
int retval;
|
||||
uint8_t data;
|
||||
|
||||
data = kmalloc(1, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
retval = roccat_common_receive(usb_dev, kone_command_weight, &data, 1);
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
kone_command_weight, 0, data, 1, USB_CTRL_SET_TIMEOUT);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (len != 1) {
|
||||
kfree(data);
|
||||
return -EIO;
|
||||
}
|
||||
*result = (int)*data;
|
||||
kfree(data);
|
||||
*result = (int)data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -241,25 +198,15 @@ static int kone_get_weight(struct usb_device *usb_dev, int *result)
|
|||
*/
|
||||
static int kone_get_firmware_version(struct usb_device *usb_dev, int *result)
|
||||
{
|
||||
int len;
|
||||
unsigned char *data;
|
||||
int retval;
|
||||
uint16_t data;
|
||||
|
||||
data = kmalloc(2, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
retval = roccat_common_receive(usb_dev, kone_command_firmware_version,
|
||||
&data, 2);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
kone_command_firmware_version, 0, data, 2,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (len != 2) {
|
||||
kfree(data);
|
||||
return -EIO;
|
||||
}
|
||||
*result = le16_to_cpu(*data);
|
||||
kfree(data);
|
||||
*result = le16_to_cpu(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -435,23 +382,9 @@ static ssize_t kone_sysfs_show_tcu(struct device *dev,
|
|||
|
||||
static int kone_tcu_command(struct usb_device *usb_dev, int number)
|
||||
{
|
||||
int len;
|
||||
char *value;
|
||||
|
||||
value = kmalloc(1, GFP_KERNEL);
|
||||
if (!value)
|
||||
return -ENOMEM;
|
||||
|
||||
*value = number;
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
kone_command_calibrate, 0, value, 1,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
kfree(value);
|
||||
return ((len != 1) ? -EIO : 0);
|
||||
unsigned char value;
|
||||
value = number;
|
||||
return roccat_common_send(usb_dev, kone_command_calibrate, &value, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -727,7 +660,8 @@ static int kone_init_specials(struct hid_device *hdev)
|
|||
goto exit_free;
|
||||
}
|
||||
|
||||
retval = roccat_connect(kone_class, hdev);
|
||||
retval = roccat_connect(kone_class, hdev,
|
||||
sizeof(struct kone_roccat_report));
|
||||
if (retval < 0) {
|
||||
hid_err(hdev, "couldn't init char dev\n");
|
||||
/* be tolerant about not getting chrdev */
|
||||
|
@ -827,8 +761,7 @@ static void kone_report_to_chrdev(struct kone_device const *kone,
|
|||
roccat_report.value = event->value;
|
||||
roccat_report.key = 0;
|
||||
roccat_report_event(kone->chrdev_minor,
|
||||
(uint8_t *)&roccat_report,
|
||||
sizeof(struct kone_roccat_report));
|
||||
(uint8_t *)&roccat_report);
|
||||
break;
|
||||
case kone_mouse_event_call_overlong_macro:
|
||||
if (event->value == kone_keystroke_action_press) {
|
||||
|
@ -836,8 +769,7 @@ static void kone_report_to_chrdev(struct kone_device const *kone,
|
|||
roccat_report.value = kone->actual_profile;
|
||||
roccat_report.key = event->macro_key;
|
||||
roccat_report_event(kone->chrdev_minor,
|
||||
(uint8_t *)&roccat_report,
|
||||
sizeof(struct kone_roccat_report));
|
||||
(uint8_t *)&roccat_report);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -912,8 +844,8 @@ static int __init kone_init(void)
|
|||
|
||||
static void __exit kone_exit(void)
|
||||
{
|
||||
class_destroy(kone_class);
|
||||
hid_unregister_driver(&kone_driver);
|
||||
class_destroy(kone_class);
|
||||
}
|
||||
|
||||
module_init(kone_init);
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-roccat.h>
|
||||
#include "hid-ids.h"
|
||||
#include "hid-roccat.h"
|
||||
#include "hid-roccat-common.h"
|
||||
#include "hid-roccat-koneplus.h"
|
||||
|
||||
static uint profile_numbers[5] = {0, 1, 2, 3, 4};
|
||||
|
@ -39,110 +39,63 @@ static void koneplus_profile_activated(struct koneplus_device *koneplus,
|
|||
static int koneplus_send_control(struct usb_device *usb_dev, uint value,
|
||||
enum koneplus_control_requests request)
|
||||
{
|
||||
int len;
|
||||
struct koneplus_control *control;
|
||||
struct koneplus_control control;
|
||||
|
||||
if ((request == KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
||||
request == KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
||||
value > 4)
|
||||
return -EINVAL;
|
||||
|
||||
control = kmalloc(sizeof(struct koneplus_control), GFP_KERNEL);
|
||||
if (!control)
|
||||
return -ENOMEM;
|
||||
control.command = KONEPLUS_COMMAND_CONTROL;
|
||||
control.value = value;
|
||||
control.request = request;
|
||||
|
||||
control->command = KONEPLUS_COMMAND_CONTROL;
|
||||
control->value = value;
|
||||
control->request = request;
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
KONEPLUS_USB_COMMAND_CONTROL, 0, control,
|
||||
sizeof(struct koneplus_control),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
kfree(control);
|
||||
|
||||
if (len != sizeof(struct koneplus_control))
|
||||
return len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int koneplus_receive(struct usb_device *usb_dev, uint usb_command,
|
||||
void *buf, uint size) {
|
||||
int len;
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
return (len != size) ? -EIO : 0;
|
||||
return roccat_common_send(usb_dev, KONEPLUS_USB_COMMAND_CONTROL,
|
||||
&control, sizeof(struct koneplus_control));
|
||||
}
|
||||
|
||||
static int koneplus_receive_control_status(struct usb_device *usb_dev)
|
||||
{
|
||||
int retval;
|
||||
struct koneplus_control *control;
|
||||
|
||||
control = kmalloc(sizeof(struct koneplus_control), GFP_KERNEL);
|
||||
if (!control)
|
||||
return -ENOMEM;
|
||||
struct koneplus_control control;
|
||||
|
||||
do {
|
||||
retval = koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_CONTROL,
|
||||
control, sizeof(struct koneplus_control));
|
||||
retval = roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_CONTROL,
|
||||
&control, sizeof(struct koneplus_control));
|
||||
|
||||
/* check if we get a completely wrong answer */
|
||||
if (retval)
|
||||
goto out;
|
||||
return retval;
|
||||
|
||||
if (control->value == KONEPLUS_CONTROL_REQUEST_STATUS_OK) {
|
||||
retval = 0;
|
||||
goto out;
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_err(&usb_dev->dev, "koneplus_receive_control_status: "
|
||||
"unknown response value 0x%x\n", control->value);
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
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);
|
||||
out:
|
||||
kfree(control);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int koneplus_send(struct usb_device *usb_dev, uint command,
|
||||
void *buf, uint size) {
|
||||
int len;
|
||||
void const *buf, uint size)
|
||||
{
|
||||
int retval;
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
|
||||
retval = roccat_common_send(usb_dev, command, buf, size);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (len != size)
|
||||
return -EIO;
|
||||
|
||||
if (koneplus_receive_control_status(usb_dev))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
return koneplus_receive_control_status(usb_dev);
|
||||
}
|
||||
|
||||
static int koneplus_select_profile(struct usb_device *usb_dev, uint number,
|
||||
|
@ -167,7 +120,7 @@ static int koneplus_select_profile(struct usb_device *usb_dev, uint number,
|
|||
static int koneplus_get_info(struct usb_device *usb_dev,
|
||||
struct koneplus_info *buf)
|
||||
{
|
||||
return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_INFO,
|
||||
return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_INFO,
|
||||
buf, sizeof(struct koneplus_info));
|
||||
}
|
||||
|
||||
|
@ -181,7 +134,7 @@ static int koneplus_get_profile_settings(struct usb_device *usb_dev,
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS,
|
||||
return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS,
|
||||
buf, sizeof(struct koneplus_profile_settings));
|
||||
}
|
||||
|
||||
|
@ -189,7 +142,7 @@ static int koneplus_set_profile_settings(struct usb_device *usb_dev,
|
|||
struct koneplus_profile_settings const *settings)
|
||||
{
|
||||
return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS,
|
||||
(void *)settings, sizeof(struct koneplus_profile_settings));
|
||||
settings, sizeof(struct koneplus_profile_settings));
|
||||
}
|
||||
|
||||
static int koneplus_get_profile_buttons(struct usb_device *usb_dev,
|
||||
|
@ -202,7 +155,7 @@ static int koneplus_get_profile_buttons(struct usb_device *usb_dev,
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS,
|
||||
return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS,
|
||||
buf, sizeof(struct koneplus_profile_buttons));
|
||||
}
|
||||
|
||||
|
@ -210,27 +163,19 @@ static int koneplus_set_profile_buttons(struct usb_device *usb_dev,
|
|||
struct koneplus_profile_buttons const *buttons)
|
||||
{
|
||||
return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS,
|
||||
(void *)buttons, sizeof(struct koneplus_profile_buttons));
|
||||
buttons, sizeof(struct koneplus_profile_buttons));
|
||||
}
|
||||
|
||||
/* retval is 0-4 on success, < 0 on error */
|
||||
static int koneplus_get_startup_profile(struct usb_device *usb_dev)
|
||||
{
|
||||
struct koneplus_startup_profile *buf;
|
||||
struct koneplus_startup_profile buf;
|
||||
int retval;
|
||||
|
||||
buf = kmalloc(sizeof(struct koneplus_startup_profile), GFP_KERNEL);
|
||||
retval = roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE,
|
||||
&buf, sizeof(struct koneplus_startup_profile));
|
||||
|
||||
retval = koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE,
|
||||
buf, sizeof(struct koneplus_startup_profile));
|
||||
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
retval = buf->startup_profile;
|
||||
out:
|
||||
kfree(buf);
|
||||
return retval;
|
||||
return retval ? retval : buf.startup_profile;
|
||||
}
|
||||
|
||||
static int koneplus_set_startup_profile(struct usb_device *usb_dev,
|
||||
|
@ -243,7 +188,7 @@ static int koneplus_set_startup_profile(struct usb_device *usb_dev,
|
|||
buf.startup_profile = startup_profile;
|
||||
|
||||
return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE,
|
||||
(char *)&buf, sizeof(struct koneplus_profile_buttons));
|
||||
&buf, sizeof(struct koneplus_profile_buttons));
|
||||
}
|
||||
|
||||
static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj,
|
||||
|
@ -256,11 +201,14 @@ static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj,
|
|||
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(&koneplus->koneplus_lock);
|
||||
retval = koneplus_receive(usb_dev, command, buf, real_size);
|
||||
retval = roccat_common_receive(usb_dev, command, buf, real_size);
|
||||
mutex_unlock(&koneplus->koneplus_lock);
|
||||
|
||||
if (retval)
|
||||
|
@ -283,7 +231,7 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj,
|
|||
return -EINVAL;
|
||||
|
||||
mutex_lock(&koneplus->koneplus_lock);
|
||||
retval = koneplus_send(usb_dev, command, (void *)buf, real_size);
|
||||
retval = koneplus_send(usb_dev, command, buf, real_size);
|
||||
mutex_unlock(&koneplus->koneplus_lock);
|
||||
|
||||
if (retval)
|
||||
|
@ -347,7 +295,7 @@ static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp,
|
|||
count = sizeof(struct koneplus_profile_settings) - off;
|
||||
|
||||
mutex_lock(&koneplus->koneplus_lock);
|
||||
memcpy(buf, ((void const *)&koneplus->profile_settings[*(uint *)(attr->private)]) + off,
|
||||
memcpy(buf, ((char const *)&koneplus->profile_settings[*(uint *)(attr->private)]) + off,
|
||||
count);
|
||||
mutex_unlock(&koneplus->koneplus_lock);
|
||||
|
||||
|
@ -406,7 +354,7 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp,
|
|||
count = sizeof(struct koneplus_profile_buttons) - off;
|
||||
|
||||
mutex_lock(&koneplus->koneplus_lock);
|
||||
memcpy(buf, ((void const *)&koneplus->profile_buttons[*(uint *)(attr->private)]) + off,
|
||||
memcpy(buf, ((char const *)&koneplus->profile_buttons[*(uint *)(attr->private)]) + off,
|
||||
count);
|
||||
mutex_unlock(&koneplus->koneplus_lock);
|
||||
|
||||
|
@ -512,7 +460,7 @@ static struct device_attribute koneplus_attributes[] = {
|
|||
|
||||
static struct bin_attribute koneplus_bin_attributes[] = {
|
||||
{
|
||||
.attr = { .name = "sensor", .mode = 0220 },
|
||||
.attr = { .name = "sensor", .mode = 0660 },
|
||||
.size = sizeof(struct koneplus_sensor),
|
||||
.read = koneplus_sysfs_read_sensor,
|
||||
.write = koneplus_sysfs_write_sensor
|
||||
|
@ -609,11 +557,13 @@ static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev,
|
|||
struct koneplus_device *koneplus)
|
||||
{
|
||||
int retval, i;
|
||||
static uint wait = 70; /* device will freeze with just 60 */
|
||||
static uint wait = 100; /* device will freeze with just 60 */
|
||||
|
||||
mutex_init(&koneplus->koneplus_lock);
|
||||
|
||||
koneplus->startup_profile = koneplus_get_startup_profile(usb_dev);
|
||||
if (koneplus->startup_profile < 0)
|
||||
return koneplus->startup_profile;
|
||||
|
||||
msleep(wait);
|
||||
retval = koneplus_get_info(usb_dev, &koneplus->info);
|
||||
|
@ -651,21 +601,21 @@ static int koneplus_init_specials(struct hid_device *hdev)
|
|||
|
||||
koneplus = kzalloc(sizeof(*koneplus), GFP_KERNEL);
|
||||
if (!koneplus) {
|
||||
dev_err(&hdev->dev, "can't alloc device descriptor\n");
|
||||
hid_err(hdev, "can't alloc device descriptor\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
hid_set_drvdata(hdev, koneplus);
|
||||
|
||||
retval = koneplus_init_koneplus_device_struct(usb_dev, koneplus);
|
||||
if (retval) {
|
||||
dev_err(&hdev->dev,
|
||||
"couldn't init struct koneplus_device\n");
|
||||
hid_err(hdev, "couldn't init struct koneplus_device\n");
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
retval = roccat_connect(koneplus_class, hdev);
|
||||
retval = roccat_connect(koneplus_class, hdev,
|
||||
sizeof(struct koneplus_roccat_report));
|
||||
if (retval < 0) {
|
||||
dev_err(&hdev->dev, "couldn't init char dev\n");
|
||||
hid_err(hdev, "couldn't init char dev\n");
|
||||
} else {
|
||||
koneplus->chrdev_minor = retval;
|
||||
koneplus->roccat_claimed = 1;
|
||||
|
@ -701,19 +651,19 @@ static int koneplus_probe(struct hid_device *hdev,
|
|||
|
||||
retval = hid_parse(hdev);
|
||||
if (retval) {
|
||||
dev_err(&hdev->dev, "parse failed\n");
|
||||
hid_err(hdev, "parse failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (retval) {
|
||||
dev_err(&hdev->dev, "hw start failed\n");
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
retval = koneplus_init_specials(hdev);
|
||||
if (retval) {
|
||||
dev_err(&hdev->dev, "couldn't install mouse\n");
|
||||
hid_err(hdev, "couldn't install mouse\n");
|
||||
goto exit_stop;
|
||||
}
|
||||
|
||||
|
@ -769,8 +719,7 @@ static void koneplus_report_to_chrdev(struct koneplus_device const *koneplus,
|
|||
roccat_report.data2 = button_report->data2;
|
||||
roccat_report.profile = koneplus->actual_profile + 1;
|
||||
roccat_report_event(koneplus->chrdev_minor,
|
||||
(uint8_t const *)&roccat_report,
|
||||
sizeof(struct koneplus_roccat_report));
|
||||
(uint8_t const *)&roccat_report);
|
||||
}
|
||||
|
||||
static int koneplus_raw_event(struct hid_device *hdev,
|
||||
|
@ -825,8 +774,8 @@ static int __init koneplus_init(void)
|
|||
|
||||
static void __exit koneplus_exit(void)
|
||||
{
|
||||
class_destroy(koneplus_class);
|
||||
hid_unregister_driver(&koneplus_driver);
|
||||
class_destroy(koneplus_class);
|
||||
}
|
||||
|
||||
module_init(koneplus_init);
|
||||
|
|
|
@ -0,0 +1,715 @@
|
|||
/*
|
||||
* Roccat Kova[+] driver for Linux
|
||||
*
|
||||
* Copyright (c) 2011 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 Kova[+] is a bigger version of the Pyra with two more side buttons.
|
||||
*/
|
||||
|
||||
#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-kovaplus.h"
|
||||
|
||||
static uint profile_numbers[5] = {0, 1, 2, 3, 4};
|
||||
|
||||
static struct class *kovaplus_class;
|
||||
|
||||
static uint kovaplus_convert_event_cpi(uint value)
|
||||
{
|
||||
return (value == 7 ? 4 : (value == 4 ? 3 : value));
|
||||
}
|
||||
|
||||
static void kovaplus_profile_activated(struct kovaplus_device *kovaplus,
|
||||
uint new_profile_index)
|
||||
{
|
||||
kovaplus->actual_profile = new_profile_index;
|
||||
kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level;
|
||||
kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x;
|
||||
kovaplus->actual_y_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_y;
|
||||
}
|
||||
|
||||
static int kovaplus_send_control(struct usb_device *usb_dev, uint value,
|
||||
enum kovaplus_control_requests request)
|
||||
{
|
||||
int retval;
|
||||
struct kovaplus_control control;
|
||||
|
||||
if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
||||
request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
||||
value > 4)
|
||||
return -EINVAL;
|
||||
|
||||
control.command = KOVAPLUS_COMMAND_CONTROL;
|
||||
control.value = value;
|
||||
control.request = request;
|
||||
|
||||
retval = roccat_common_send(usb_dev, KOVAPLUS_USB_COMMAND_CONTROL,
|
||||
&control, sizeof(struct kovaplus_control));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int kovaplus_receive_control_status(struct usb_device *usb_dev)
|
||||
{
|
||||
int retval;
|
||||
struct kovaplus_control control;
|
||||
|
||||
do {
|
||||
retval = roccat_common_receive(usb_dev, KOVAPLUS_USB_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, "kovaplus_receive_control_status: "
|
||||
"unknown response value 0x%x\n", control.value);
|
||||
return -EINVAL;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int kovaplus_send(struct usb_device *usb_dev, uint command,
|
||||
void const *buf, uint size)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = roccat_common_send(usb_dev, command, buf, size);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
msleep(100);
|
||||
|
||||
return kovaplus_receive_control_status(usb_dev);
|
||||
}
|
||||
|
||||
static int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
|
||||
enum kovaplus_control_requests request)
|
||||
{
|
||||
return kovaplus_send_control(usb_dev, number, request);
|
||||
}
|
||||
|
||||
static int kovaplus_get_info(struct usb_device *usb_dev,
|
||||
struct kovaplus_info *buf)
|
||||
{
|
||||
return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_INFO,
|
||||
buf, sizeof(struct kovaplus_info));
|
||||
}
|
||||
|
||||
static int kovaplus_get_profile_settings(struct usb_device *usb_dev,
|
||||
struct kovaplus_profile_settings *buf, uint number)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = kovaplus_select_profile(usb_dev, number,
|
||||
KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS,
|
||||
buf, sizeof(struct kovaplus_profile_settings));
|
||||
}
|
||||
|
||||
static int kovaplus_set_profile_settings(struct usb_device *usb_dev,
|
||||
struct kovaplus_profile_settings const *settings)
|
||||
{
|
||||
return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS,
|
||||
settings, sizeof(struct kovaplus_profile_settings));
|
||||
}
|
||||
|
||||
static int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
|
||||
struct kovaplus_profile_buttons *buf, int number)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = kovaplus_select_profile(usb_dev, number,
|
||||
KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS,
|
||||
buf, sizeof(struct kovaplus_profile_buttons));
|
||||
}
|
||||
|
||||
static int kovaplus_set_profile_buttons(struct usb_device *usb_dev,
|
||||
struct kovaplus_profile_buttons const *buttons)
|
||||
{
|
||||
return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS,
|
||||
buttons, sizeof(struct kovaplus_profile_buttons));
|
||||
}
|
||||
|
||||
/* retval is 0-4 on success, < 0 on error */
|
||||
static int kovaplus_get_actual_profile(struct usb_device *usb_dev)
|
||||
{
|
||||
struct kovaplus_actual_profile buf;
|
||||
int retval;
|
||||
|
||||
retval = roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE,
|
||||
&buf, sizeof(struct kovaplus_actual_profile));
|
||||
|
||||
return retval ? retval : buf.actual_profile;
|
||||
}
|
||||
|
||||
static int kovaplus_set_actual_profile(struct usb_device *usb_dev,
|
||||
int new_profile)
|
||||
{
|
||||
struct kovaplus_actual_profile buf;
|
||||
|
||||
buf.command = KOVAPLUS_COMMAND_ACTUAL_PROFILE;
|
||||
buf.size = sizeof(struct kovaplus_actual_profile);
|
||||
buf.actual_profile = new_profile;
|
||||
|
||||
return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE,
|
||||
&buf, sizeof(struct kovaplus_actual_profile));
|
||||
}
|
||||
|
||||
static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev =
|
||||
container_of(kobj, struct device, kobj)->parent->parent;
|
||||
struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
|
||||
if (off >= sizeof(struct kovaplus_profile_settings))
|
||||
return 0;
|
||||
|
||||
if (off + count > sizeof(struct kovaplus_profile_settings))
|
||||
count = sizeof(struct kovaplus_profile_settings) - off;
|
||||
|
||||
mutex_lock(&kovaplus->kovaplus_lock);
|
||||
memcpy(buf, ((char const *)&kovaplus->profile_settings[*(uint *)(attr->private)]) + off,
|
||||
count);
|
||||
mutex_unlock(&kovaplus->kovaplus_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t kovaplus_sysfs_write_profile_settings(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev =
|
||||
container_of(kobj, struct device, kobj)->parent->parent;
|
||||
struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
|
||||
int retval = 0;
|
||||
int difference;
|
||||
int profile_index;
|
||||
struct kovaplus_profile_settings *profile_settings;
|
||||
|
||||
if (off != 0 || count != sizeof(struct kovaplus_profile_settings))
|
||||
return -EINVAL;
|
||||
|
||||
profile_index = ((struct kovaplus_profile_settings const *)buf)->profile_index;
|
||||
profile_settings = &kovaplus->profile_settings[profile_index];
|
||||
|
||||
mutex_lock(&kovaplus->kovaplus_lock);
|
||||
difference = memcmp(buf, profile_settings,
|
||||
sizeof(struct kovaplus_profile_settings));
|
||||
if (difference) {
|
||||
retval = kovaplus_set_profile_settings(usb_dev,
|
||||
(struct kovaplus_profile_settings const *)buf);
|
||||
if (!retval)
|
||||
memcpy(profile_settings, buf,
|
||||
sizeof(struct kovaplus_profile_settings));
|
||||
}
|
||||
mutex_unlock(&kovaplus->kovaplus_lock);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return sizeof(struct kovaplus_profile_settings);
|
||||
}
|
||||
|
||||
static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev =
|
||||
container_of(kobj, struct device, kobj)->parent->parent;
|
||||
struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
|
||||
if (off >= sizeof(struct kovaplus_profile_buttons))
|
||||
return 0;
|
||||
|
||||
if (off + count > sizeof(struct kovaplus_profile_buttons))
|
||||
count = sizeof(struct kovaplus_profile_buttons) - off;
|
||||
|
||||
mutex_lock(&kovaplus->kovaplus_lock);
|
||||
memcpy(buf, ((char const *)&kovaplus->profile_buttons[*(uint *)(attr->private)]) + off,
|
||||
count);
|
||||
mutex_unlock(&kovaplus->kovaplus_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t kovaplus_sysfs_write_profile_buttons(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev =
|
||||
container_of(kobj, struct device, kobj)->parent->parent;
|
||||
struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
|
||||
int retval = 0;
|
||||
int difference;
|
||||
uint profile_index;
|
||||
struct kovaplus_profile_buttons *profile_buttons;
|
||||
|
||||
if (off != 0 || count != sizeof(struct kovaplus_profile_buttons))
|
||||
return -EINVAL;
|
||||
|
||||
profile_index = ((struct kovaplus_profile_buttons const *)buf)->profile_index;
|
||||
profile_buttons = &kovaplus->profile_buttons[profile_index];
|
||||
|
||||
mutex_lock(&kovaplus->kovaplus_lock);
|
||||
difference = memcmp(buf, profile_buttons,
|
||||
sizeof(struct kovaplus_profile_buttons));
|
||||
if (difference) {
|
||||
retval = kovaplus_set_profile_buttons(usb_dev,
|
||||
(struct kovaplus_profile_buttons const *)buf);
|
||||
if (!retval)
|
||||
memcpy(profile_buttons, buf,
|
||||
sizeof(struct kovaplus_profile_buttons));
|
||||
}
|
||||
mutex_unlock(&kovaplus->kovaplus_lock);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return sizeof(struct kovaplus_profile_buttons);
|
||||
}
|
||||
|
||||
static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct kovaplus_device *kovaplus =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_profile);
|
||||
}
|
||||
|
||||
static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
|
||||
struct device_attribute *attr, char const *buf, size_t size)
|
||||
{
|
||||
struct kovaplus_device *kovaplus;
|
||||
struct usb_device *usb_dev;
|
||||
unsigned long profile;
|
||||
int retval;
|
||||
|
||||
dev = dev->parent->parent;
|
||||
kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
usb_dev = interface_to_usbdev(to_usb_interface(dev));
|
||||
|
||||
retval = strict_strtoul(buf, 10, &profile);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (profile >= 5)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&kovaplus->kovaplus_lock);
|
||||
retval = kovaplus_set_actual_profile(usb_dev, profile);
|
||||
kovaplus->actual_profile = profile;
|
||||
mutex_unlock(&kovaplus->kovaplus_lock);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct kovaplus_device *kovaplus =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi);
|
||||
}
|
||||
|
||||
static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct kovaplus_device *kovaplus =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity);
|
||||
}
|
||||
|
||||
static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct kovaplus_device *kovaplus =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity);
|
||||
}
|
||||
|
||||
static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct kovaplus_device *kovaplus =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->info.firmware_version);
|
||||
}
|
||||
|
||||
static struct device_attribute kovaplus_attributes[] = {
|
||||
__ATTR(actual_cpi, 0440,
|
||||
kovaplus_sysfs_show_actual_cpi, NULL),
|
||||
__ATTR(firmware_version, 0440,
|
||||
kovaplus_sysfs_show_firmware_version, NULL),
|
||||
__ATTR(actual_profile, 0660,
|
||||
kovaplus_sysfs_show_actual_profile,
|
||||
kovaplus_sysfs_set_actual_profile),
|
||||
__ATTR(actual_sensitivity_x, 0440,
|
||||
kovaplus_sysfs_show_actual_sensitivity_x, NULL),
|
||||
__ATTR(actual_sensitivity_y, 0440,
|
||||
kovaplus_sysfs_show_actual_sensitivity_y, NULL),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static struct bin_attribute kovaplus_bin_attributes[] = {
|
||||
{
|
||||
.attr = { .name = "profile_settings", .mode = 0220 },
|
||||
.size = sizeof(struct kovaplus_profile_settings),
|
||||
.write = kovaplus_sysfs_write_profile_settings
|
||||
},
|
||||
{
|
||||
.attr = { .name = "profile1_settings", .mode = 0440 },
|
||||
.size = sizeof(struct kovaplus_profile_settings),
|
||||
.read = kovaplus_sysfs_read_profilex_settings,
|
||||
.private = &profile_numbers[0]
|
||||
},
|
||||
{
|
||||
.attr = { .name = "profile2_settings", .mode = 0440 },
|
||||
.size = sizeof(struct kovaplus_profile_settings),
|
||||
.read = kovaplus_sysfs_read_profilex_settings,
|
||||
.private = &profile_numbers[1]
|
||||
},
|
||||
{
|
||||
.attr = { .name = "profile3_settings", .mode = 0440 },
|
||||
.size = sizeof(struct kovaplus_profile_settings),
|
||||
.read = kovaplus_sysfs_read_profilex_settings,
|
||||
.private = &profile_numbers[2]
|
||||
},
|
||||
{
|
||||
.attr = { .name = "profile4_settings", .mode = 0440 },
|
||||
.size = sizeof(struct kovaplus_profile_settings),
|
||||
.read = kovaplus_sysfs_read_profilex_settings,
|
||||
.private = &profile_numbers[3]
|
||||
},
|
||||
{
|
||||
.attr = { .name = "profile5_settings", .mode = 0440 },
|
||||
.size = sizeof(struct kovaplus_profile_settings),
|
||||
.read = kovaplus_sysfs_read_profilex_settings,
|
||||
.private = &profile_numbers[4]
|
||||
},
|
||||
{
|
||||
.attr = { .name = "profile_buttons", .mode = 0220 },
|
||||
.size = sizeof(struct kovaplus_profile_buttons),
|
||||
.write = kovaplus_sysfs_write_profile_buttons
|
||||
},
|
||||
{
|
||||
.attr = { .name = "profile1_buttons", .mode = 0440 },
|
||||
.size = sizeof(struct kovaplus_profile_buttons),
|
||||
.read = kovaplus_sysfs_read_profilex_buttons,
|
||||
.private = &profile_numbers[0]
|
||||
},
|
||||
{
|
||||
.attr = { .name = "profile2_buttons", .mode = 0440 },
|
||||
.size = sizeof(struct kovaplus_profile_buttons),
|
||||
.read = kovaplus_sysfs_read_profilex_buttons,
|
||||
.private = &profile_numbers[1]
|
||||
},
|
||||
{
|
||||
.attr = { .name = "profile3_buttons", .mode = 0440 },
|
||||
.size = sizeof(struct kovaplus_profile_buttons),
|
||||
.read = kovaplus_sysfs_read_profilex_buttons,
|
||||
.private = &profile_numbers[2]
|
||||
},
|
||||
{
|
||||
.attr = { .name = "profile4_buttons", .mode = 0440 },
|
||||
.size = sizeof(struct kovaplus_profile_buttons),
|
||||
.read = kovaplus_sysfs_read_profilex_buttons,
|
||||
.private = &profile_numbers[3]
|
||||
},
|
||||
{
|
||||
.attr = { .name = "profile5_buttons", .mode = 0440 },
|
||||
.size = sizeof(struct kovaplus_profile_buttons),
|
||||
.read = kovaplus_sysfs_read_profilex_buttons,
|
||||
.private = &profile_numbers[4]
|
||||
},
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev,
|
||||
struct kovaplus_device *kovaplus)
|
||||
{
|
||||
int retval, i;
|
||||
static uint wait = 70; /* device will freeze with just 60 */
|
||||
|
||||
mutex_init(&kovaplus->kovaplus_lock);
|
||||
|
||||
retval = kovaplus_get_info(usb_dev, &kovaplus->info);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
for (i = 0; i < 5; ++i) {
|
||||
msleep(wait);
|
||||
retval = kovaplus_get_profile_settings(usb_dev,
|
||||
&kovaplus->profile_settings[i], i);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
msleep(wait);
|
||||
retval = kovaplus_get_profile_buttons(usb_dev,
|
||||
&kovaplus->profile_buttons[i], i);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
msleep(wait);
|
||||
retval = kovaplus_get_actual_profile(usb_dev);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
kovaplus_profile_activated(kovaplus, retval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kovaplus_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 kovaplus_device *kovaplus;
|
||||
int retval;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
== USB_INTERFACE_PROTOCOL_MOUSE) {
|
||||
|
||||
kovaplus = kzalloc(sizeof(*kovaplus), GFP_KERNEL);
|
||||
if (!kovaplus) {
|
||||
hid_err(hdev, "can't alloc device descriptor\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
hid_set_drvdata(hdev, kovaplus);
|
||||
|
||||
retval = kovaplus_init_kovaplus_device_struct(usb_dev, kovaplus);
|
||||
if (retval) {
|
||||
hid_err(hdev, "couldn't init struct kovaplus_device\n");
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
retval = roccat_connect(kovaplus_class, hdev,
|
||||
sizeof(struct kovaplus_roccat_report));
|
||||
if (retval < 0) {
|
||||
hid_err(hdev, "couldn't init char dev\n");
|
||||
} else {
|
||||
kovaplus->chrdev_minor = retval;
|
||||
kovaplus->roccat_claimed = 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit_free:
|
||||
kfree(kovaplus);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void kovaplus_remove_specials(struct hid_device *hdev)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
struct kovaplus_device *kovaplus;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
== USB_INTERFACE_PROTOCOL_MOUSE) {
|
||||
kovaplus = hid_get_drvdata(hdev);
|
||||
if (kovaplus->roccat_claimed)
|
||||
roccat_disconnect(kovaplus->chrdev_minor);
|
||||
kfree(kovaplus);
|
||||
}
|
||||
}
|
||||
|
||||
static int kovaplus_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 = kovaplus_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 kovaplus_remove(struct hid_device *hdev)
|
||||
{
|
||||
kovaplus_remove_specials(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus,
|
||||
u8 const *data)
|
||||
{
|
||||
struct kovaplus_mouse_report_button const *button_report;
|
||||
|
||||
if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
|
||||
return;
|
||||
|
||||
button_report = (struct kovaplus_mouse_report_button const *)data;
|
||||
|
||||
switch (button_report->type) {
|
||||
case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1:
|
||||
kovaplus_profile_activated(kovaplus, button_report->data1 - 1);
|
||||
break;
|
||||
case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI:
|
||||
kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1);
|
||||
case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY:
|
||||
kovaplus->actual_x_sensitivity = button_report->data1;
|
||||
kovaplus->actual_y_sensitivity = button_report->data2;
|
||||
}
|
||||
}
|
||||
|
||||
static void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus,
|
||||
u8 const *data)
|
||||
{
|
||||
struct kovaplus_roccat_report roccat_report;
|
||||
struct kovaplus_mouse_report_button const *button_report;
|
||||
|
||||
if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
|
||||
return;
|
||||
|
||||
button_report = (struct kovaplus_mouse_report_button const *)data;
|
||||
|
||||
if (button_report->type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2)
|
||||
return;
|
||||
|
||||
roccat_report.type = button_report->type;
|
||||
roccat_report.profile = kovaplus->actual_profile + 1;
|
||||
|
||||
if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO ||
|
||||
roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT ||
|
||||
roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH ||
|
||||
roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER)
|
||||
roccat_report.button = button_report->data1;
|
||||
else
|
||||
roccat_report.button = 0;
|
||||
|
||||
if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI)
|
||||
roccat_report.data1 = kovaplus_convert_event_cpi(button_report->data1);
|
||||
else
|
||||
roccat_report.data1 = button_report->data1;
|
||||
|
||||
roccat_report.data2 = button_report->data2;
|
||||
|
||||
roccat_report_event(kovaplus->chrdev_minor,
|
||||
(uint8_t const *)&roccat_report);
|
||||
}
|
||||
|
||||
static int kovaplus_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 kovaplus_device *kovaplus = hid_get_drvdata(hdev);
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
!= USB_INTERFACE_PROTOCOL_MOUSE)
|
||||
return 0;
|
||||
|
||||
kovaplus_keep_values_up_to_date(kovaplus, data);
|
||||
|
||||
if (kovaplus->roccat_claimed)
|
||||
kovaplus_report_to_chrdev(kovaplus, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id kovaplus_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(hid, kovaplus_devices);
|
||||
|
||||
static struct hid_driver kovaplus_driver = {
|
||||
.name = "kovaplus",
|
||||
.id_table = kovaplus_devices,
|
||||
.probe = kovaplus_probe,
|
||||
.remove = kovaplus_remove,
|
||||
.raw_event = kovaplus_raw_event
|
||||
};
|
||||
|
||||
static int __init kovaplus_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
kovaplus_class = class_create(THIS_MODULE, "kovaplus");
|
||||
if (IS_ERR(kovaplus_class))
|
||||
return PTR_ERR(kovaplus_class);
|
||||
kovaplus_class->dev_attrs = kovaplus_attributes;
|
||||
kovaplus_class->dev_bin_attrs = kovaplus_bin_attributes;
|
||||
|
||||
retval = hid_register_driver(&kovaplus_driver);
|
||||
if (retval)
|
||||
class_destroy(kovaplus_class);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit kovaplus_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&kovaplus_driver);
|
||||
class_destroy(kovaplus_class);
|
||||
}
|
||||
|
||||
module_init(kovaplus_init);
|
||||
module_exit(kovaplus_exit);
|
||||
|
||||
MODULE_AUTHOR("Stefan Achatz");
|
||||
MODULE_DESCRIPTION("USB Roccat Kova[+] driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,157 @@
|
|||
#ifndef __HID_ROCCAT_KOVAPLUS_H
|
||||
#define __HID_ROCCAT_KOVAPLUS_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 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>
|
||||
|
||||
struct kovaplus_control {
|
||||
uint8_t command; /* KOVAPLUS_COMMAND_CONTROL */
|
||||
uint8_t value;
|
||||
uint8_t request;
|
||||
} __packed;
|
||||
|
||||
enum kovaplus_control_requests {
|
||||
/* read after write; value = 1 */
|
||||
KOVAPLUS_CONTROL_REQUEST_STATUS = 0x0,
|
||||
/* write; value = profile number range 0-4 */
|
||||
KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
|
||||
/* write; value = profile number range 0-4 */
|
||||
KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20,
|
||||
};
|
||||
|
||||
enum kovaplus_control_values {
|
||||
KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD = 0, /* supposed */
|
||||
KOVAPLUS_CONTROL_REQUEST_STATUS_OK = 1,
|
||||
KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT = 3, /* supposed */
|
||||
};
|
||||
|
||||
struct kovaplus_actual_profile {
|
||||
uint8_t command; /* KOVAPLUS_COMMAND_ACTUAL_PROFILE */
|
||||
uint8_t size; /* always 3 */
|
||||
uint8_t actual_profile; /* Range 0-4! */
|
||||
} __packed;
|
||||
|
||||
struct kovaplus_profile_settings {
|
||||
uint8_t command; /* KOVAPLUS_COMMAND_PROFILE_SETTINGS */
|
||||
uint8_t size; /* 16 */
|
||||
uint8_t profile_index; /* range 0-4 */
|
||||
uint8_t unknown1;
|
||||
uint8_t sensitivity_x; /* range 1-10 */
|
||||
uint8_t sensitivity_y; /* range 1-10 */
|
||||
uint8_t cpi_levels_enabled;
|
||||
uint8_t cpi_startup_level; /* range 1-4 */
|
||||
uint8_t data[8];
|
||||
} __packed;
|
||||
|
||||
struct kovaplus_profile_buttons {
|
||||
uint8_t command; /* KOVAPLUS_COMMAND_PROFILE_BUTTONS */
|
||||
uint8_t size; /* 23 */
|
||||
uint8_t profile_index; /* range 0-4 */
|
||||
uint8_t data[20];
|
||||
} __packed;
|
||||
|
||||
struct kovaplus_info {
|
||||
uint8_t command; /* KOVAPLUS_COMMAND_INFO */
|
||||
uint8_t size; /* 6 */
|
||||
uint8_t firmware_version;
|
||||
uint8_t unknown[3];
|
||||
} __packed;
|
||||
|
||||
/* writes 1 on plugin */
|
||||
struct kovaplus_a {
|
||||
uint8_t command; /* KOVAPLUS_COMMAND_A */
|
||||
uint8_t size; /* 3 */
|
||||
uint8_t unknown;
|
||||
} __packed;
|
||||
|
||||
enum kovaplus_commands {
|
||||
KOVAPLUS_COMMAND_CONTROL = 0x4,
|
||||
KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5,
|
||||
KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6,
|
||||
KOVAPLUS_COMMAND_PROFILE_BUTTONS = 0x7,
|
||||
KOVAPLUS_COMMAND_INFO = 0x9,
|
||||
KOVAPLUS_COMMAND_A = 0xa,
|
||||
};
|
||||
|
||||
enum kovaplus_usb_commands {
|
||||
KOVAPLUS_USB_COMMAND_CONTROL = 0x304,
|
||||
KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE = 0x305,
|
||||
KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS = 0x306,
|
||||
KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS = 0x307,
|
||||
KOVAPLUS_USB_COMMAND_INFO = 0x309,
|
||||
KOVAPLUS_USB_COMMAND_A = 0x30a,
|
||||
};
|
||||
|
||||
enum kovaplus_mouse_report_numbers {
|
||||
KOVAPLUS_MOUSE_REPORT_NUMBER_MOUSE = 1,
|
||||
KOVAPLUS_MOUSE_REPORT_NUMBER_AUDIO = 2,
|
||||
KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON = 3,
|
||||
KOVAPLUS_MOUSE_REPORT_NUMBER_KBD = 4,
|
||||
};
|
||||
|
||||
struct kovaplus_mouse_report_button {
|
||||
uint8_t report_number; /* KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON */
|
||||
uint8_t unknown1;
|
||||
uint8_t type;
|
||||
uint8_t data1;
|
||||
uint8_t data2;
|
||||
} __packed;
|
||||
|
||||
enum kovaplus_mouse_report_button_types {
|
||||
/* data1 = profile_number range 1-5; no release event */
|
||||
KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1 = 0x20,
|
||||
/* data1 = profile_number range 1-5; no release event */
|
||||
KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2 = 0x30,
|
||||
/* data1 = button_number range 1-18; data2 = action */
|
||||
KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO = 0x40,
|
||||
/* data1 = button_number range 1-18; data2 = action */
|
||||
KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT = 0x50,
|
||||
/* data1 = button_number range 1-18; data2 = action */
|
||||
KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH = 0x60,
|
||||
/* data1 = button_number range 1-18; data2 = action */
|
||||
KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER = 0x80,
|
||||
/* data1 = 1 = 400, 2 = 800, 4 = 1600, 7 = 3200; no release event */
|
||||
KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI = 0xb0,
|
||||
/* data1 + data2 = sense range 1-10; no release event */
|
||||
KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY = 0xc0,
|
||||
/* data1 = type as in profile_buttons; data2 = action */
|
||||
KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MULTIMEDIA = 0xf0,
|
||||
};
|
||||
|
||||
enum kovaplus_mouse_report_button_actions {
|
||||
KOVAPLUS_MOUSE_REPORT_BUTTON_ACTION_PRESS = 0,
|
||||
KOVAPLUS_MOUSE_REPORT_BUTTON_ACTION_RELEASE = 1,
|
||||
};
|
||||
|
||||
struct kovaplus_roccat_report {
|
||||
uint8_t type;
|
||||
uint8_t profile;
|
||||
uint8_t button;
|
||||
uint8_t data1;
|
||||
uint8_t data2;
|
||||
} __packed;
|
||||
|
||||
struct kovaplus_device {
|
||||
int actual_profile;
|
||||
int actual_cpi;
|
||||
int actual_x_sensitivity;
|
||||
int actual_y_sensitivity;
|
||||
int roccat_claimed;
|
||||
int chrdev_minor;
|
||||
struct mutex kovaplus_lock;
|
||||
struct kovaplus_info info;
|
||||
struct kovaplus_profile_settings profile_settings[5];
|
||||
struct kovaplus_profile_buttons profile_buttons[5];
|
||||
};
|
||||
|
||||
#endif
|
|
@ -20,11 +20,11 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-roccat.h>
|
||||
#include "hid-ids.h"
|
||||
#include "hid-roccat.h"
|
||||
#include "hid-roccat-common.h"
|
||||
#include "hid-roccat-pyra.h"
|
||||
|
||||
static uint profile_numbers[5] = {0, 1, 2, 3, 4};
|
||||
|
@ -42,7 +42,6 @@ static void profile_activated(struct pyra_device *pyra,
|
|||
static int pyra_send_control(struct usb_device *usb_dev, int value,
|
||||
enum pyra_control_requests request)
|
||||
{
|
||||
int len;
|
||||
struct pyra_control control;
|
||||
|
||||
if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
||||
|
@ -54,47 +53,31 @@ static int pyra_send_control(struct usb_device *usb_dev, int value,
|
|||
control.value = value;
|
||||
control.request = request;
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
PYRA_USB_COMMAND_CONTROL, 0, (char *)&control,
|
||||
sizeof(struct pyra_control),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (len != sizeof(struct pyra_control))
|
||||
return len;
|
||||
|
||||
return 0;
|
||||
return roccat_common_send(usb_dev, PYRA_USB_COMMAND_CONTROL,
|
||||
&control, sizeof(struct pyra_control));
|
||||
}
|
||||
|
||||
static int pyra_receive_control_status(struct usb_device *usb_dev)
|
||||
{
|
||||
int len;
|
||||
int retval;
|
||||
struct pyra_control control;
|
||||
|
||||
do {
|
||||
msleep(10);
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE |
|
||||
USB_DIR_IN,
|
||||
PYRA_USB_COMMAND_CONTROL, 0, (char *)&control,
|
||||
sizeof(struct pyra_control),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
retval = roccat_common_receive(usb_dev, PYRA_USB_COMMAND_CONTROL,
|
||||
&control, sizeof(struct pyra_control));
|
||||
|
||||
/* requested too early, try again */
|
||||
} while (len == -EPROTO);
|
||||
} while (retval == -EPROTO);
|
||||
|
||||
if (len == sizeof(struct pyra_control) &&
|
||||
control.command == PYRA_COMMAND_CONTROL &&
|
||||
if (!retval && control.command == PYRA_COMMAND_CONTROL &&
|
||||
control.request == PYRA_CONTROL_REQUEST_STATUS &&
|
||||
control.value == 1)
|
||||
return 0;
|
||||
return 0;
|
||||
else {
|
||||
hid_err(usb_dev, "receive control status: unknown response 0x%x 0x%x\n",
|
||||
control.request, control.value);
|
||||
return -EINVAL;
|
||||
return retval ? retval : -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,125 +85,72 @@ static int pyra_get_profile_settings(struct usb_device *usb_dev,
|
|||
struct pyra_profile_settings *buf, int number)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = pyra_send_control(usb_dev, number,
|
||||
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)buf,
|
||||
sizeof(struct pyra_profile_settings),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (retval != sizeof(struct pyra_profile_settings))
|
||||
return retval;
|
||||
|
||||
return 0;
|
||||
return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_PROFILE_SETTINGS,
|
||||
buf, sizeof(struct pyra_profile_settings));
|
||||
}
|
||||
|
||||
static int pyra_get_profile_buttons(struct usb_device *usb_dev,
|
||||
struct pyra_profile_buttons *buf, int number)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = pyra_send_control(usb_dev, number,
|
||||
PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buf,
|
||||
sizeof(struct pyra_profile_buttons),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (retval != sizeof(struct pyra_profile_buttons))
|
||||
return retval;
|
||||
|
||||
return 0;
|
||||
return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_PROFILE_BUTTONS,
|
||||
buf, sizeof(struct pyra_profile_buttons));
|
||||
}
|
||||
|
||||
static int pyra_get_settings(struct usb_device *usb_dev,
|
||||
struct pyra_settings *buf)
|
||||
{
|
||||
int len;
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
PYRA_USB_COMMAND_SETTINGS, 0, buf,
|
||||
sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT);
|
||||
if (len != sizeof(struct pyra_settings))
|
||||
return -EIO;
|
||||
return 0;
|
||||
return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_SETTINGS,
|
||||
buf, sizeof(struct pyra_settings));
|
||||
}
|
||||
|
||||
static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf)
|
||||
{
|
||||
int len;
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
PYRA_USB_COMMAND_INFO, 0, buf,
|
||||
sizeof(struct pyra_info), USB_CTRL_SET_TIMEOUT);
|
||||
if (len != sizeof(struct pyra_info))
|
||||
return -EIO;
|
||||
return 0;
|
||||
return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_INFO,
|
||||
buf, sizeof(struct pyra_info));
|
||||
}
|
||||
|
||||
static int pyra_send(struct usb_device *usb_dev, uint command,
|
||||
void const *buf, uint size)
|
||||
{
|
||||
int retval;
|
||||
retval = roccat_common_send(usb_dev, command, buf, size);
|
||||
if (retval)
|
||||
return retval;
|
||||
return pyra_receive_control_status(usb_dev);
|
||||
}
|
||||
|
||||
static int pyra_set_profile_settings(struct usb_device *usb_dev,
|
||||
struct pyra_profile_settings const *settings)
|
||||
{
|
||||
int len;
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)settings,
|
||||
sizeof(struct pyra_profile_settings),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (len != sizeof(struct pyra_profile_settings))
|
||||
return -EIO;
|
||||
if (pyra_receive_control_status(usb_dev))
|
||||
return -EIO;
|
||||
return 0;
|
||||
return pyra_send(usb_dev, PYRA_USB_COMMAND_PROFILE_SETTINGS, settings,
|
||||
sizeof(struct pyra_profile_settings));
|
||||
}
|
||||
|
||||
static int pyra_set_profile_buttons(struct usb_device *usb_dev,
|
||||
struct pyra_profile_buttons const *buttons)
|
||||
{
|
||||
int len;
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buttons,
|
||||
sizeof(struct pyra_profile_buttons),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (len != sizeof(struct pyra_profile_buttons))
|
||||
return -EIO;
|
||||
if (pyra_receive_control_status(usb_dev))
|
||||
return -EIO;
|
||||
return 0;
|
||||
return pyra_send(usb_dev, PYRA_USB_COMMAND_PROFILE_BUTTONS, buttons,
|
||||
sizeof(struct pyra_profile_buttons));
|
||||
}
|
||||
|
||||
static int pyra_set_settings(struct usb_device *usb_dev,
|
||||
struct pyra_settings const *settings)
|
||||
{
|
||||
int len;
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
PYRA_USB_COMMAND_SETTINGS, 0, (char *)settings,
|
||||
sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT);
|
||||
if (len != sizeof(struct pyra_settings))
|
||||
return -EIO;
|
||||
if (pyra_receive_control_status(usb_dev))
|
||||
return -EIO;
|
||||
return 0;
|
||||
int retval;
|
||||
retval = roccat_common_send(usb_dev, PYRA_USB_COMMAND_SETTINGS, settings,
|
||||
sizeof(struct pyra_settings));
|
||||
if (retval)
|
||||
return retval;
|
||||
return pyra_receive_control_status(usb_dev);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
|
||||
|
@ -521,21 +451,16 @@ static struct bin_attribute pyra_bin_attributes[] = {
|
|||
static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
|
||||
struct pyra_device *pyra)
|
||||
{
|
||||
struct pyra_info *info;
|
||||
struct pyra_info info;
|
||||
int retval, i;
|
||||
|
||||
mutex_init(&pyra->pyra_lock);
|
||||
|
||||
info = kmalloc(sizeof(struct pyra_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
retval = pyra_get_info(usb_dev, info);
|
||||
if (retval) {
|
||||
kfree(info);
|
||||
retval = pyra_get_info(usb_dev, &info);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
pyra->firmware_version = info->firmware_version;
|
||||
kfree(info);
|
||||
|
||||
pyra->firmware_version = info.firmware_version;
|
||||
|
||||
retval = pyra_get_settings(usb_dev, &pyra->settings);
|
||||
if (retval)
|
||||
|
@ -581,7 +506,8 @@ static int pyra_init_specials(struct hid_device *hdev)
|
|||
goto exit_free;
|
||||
}
|
||||
|
||||
retval = roccat_connect(pyra_class, hdev);
|
||||
retval = roccat_connect(pyra_class, hdev,
|
||||
sizeof(struct pyra_roccat_report));
|
||||
if (retval < 0) {
|
||||
hid_err(hdev, "couldn't init char dev\n");
|
||||
} else {
|
||||
|
@ -685,8 +611,7 @@ static void pyra_report_to_chrdev(struct pyra_device const *pyra,
|
|||
roccat_report.value = button_event->data1;
|
||||
roccat_report.key = 0;
|
||||
roccat_report_event(pyra->chrdev_minor,
|
||||
(uint8_t const *)&roccat_report,
|
||||
sizeof(struct pyra_roccat_report));
|
||||
(uint8_t const *)&roccat_report);
|
||||
break;
|
||||
case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO:
|
||||
case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT:
|
||||
|
@ -700,8 +625,7 @@ static void pyra_report_to_chrdev(struct pyra_device const *pyra,
|
|||
*/
|
||||
roccat_report.value = pyra->actual_profile + 1;
|
||||
roccat_report_event(pyra->chrdev_minor,
|
||||
(uint8_t const *)&roccat_report,
|
||||
sizeof(struct pyra_roccat_report));
|
||||
(uint8_t const *)&roccat_report);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -761,8 +685,8 @@ static int __init pyra_init(void)
|
|||
|
||||
static void __exit pyra_exit(void)
|
||||
{
|
||||
class_destroy(pyra_class);
|
||||
hid_unregister_driver(&pyra_driver);
|
||||
class_destroy(pyra_class);
|
||||
}
|
||||
|
||||
module_init(pyra_init);
|
||||
|
|
|
@ -26,8 +26,7 @@
|
|||
#include <linux/cdev.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "hid-roccat.h"
|
||||
#include <linux/hid-roccat.h>
|
||||
|
||||
#define ROCCAT_FIRST_MINOR 0
|
||||
#define ROCCAT_MAX_DEVICES 8
|
||||
|
@ -37,11 +36,11 @@
|
|||
|
||||
struct roccat_report {
|
||||
uint8_t *value;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct roccat_device {
|
||||
unsigned int minor;
|
||||
int report_size;
|
||||
int open;
|
||||
int exist;
|
||||
wait_queue_head_t wait;
|
||||
|
@ -123,7 +122,7 @@ static ssize_t roccat_read(struct file *file, char __user *buffer,
|
|||
* If report is larger than requested amount of data, rest of report
|
||||
* is lost!
|
||||
*/
|
||||
len = report->len > count ? count : report->len;
|
||||
len = device->report_size > count ? count : device->report_size;
|
||||
|
||||
if (copy_to_user(buffer, report->value, len)) {
|
||||
retval = -EFAULT;
|
||||
|
@ -248,26 +247,25 @@ static int roccat_release(struct inode *inode, struct file *file)
|
|||
*
|
||||
* This is called from interrupt handler.
|
||||
*/
|
||||
int roccat_report_event(int minor, u8 const *data, int len)
|
||||
int roccat_report_event(int minor, u8 const *data)
|
||||
{
|
||||
struct roccat_device *device;
|
||||
struct roccat_reader *reader;
|
||||
struct roccat_report *report;
|
||||
uint8_t *new_value;
|
||||
|
||||
new_value = kmemdup(data, len, GFP_ATOMIC);
|
||||
device = devices[minor];
|
||||
|
||||
new_value = kmemdup(data, device->report_size, GFP_ATOMIC);
|
||||
if (!new_value)
|
||||
return -ENOMEM;
|
||||
|
||||
device = devices[minor];
|
||||
|
||||
report = &device->cbuf[device->cbuf_end];
|
||||
|
||||
/* passing NULL is safe */
|
||||
kfree(report->value);
|
||||
|
||||
report->value = new_value;
|
||||
report->len = len;
|
||||
device->cbuf_end = (device->cbuf_end + 1) % ROCCAT_CBUF_SIZE;
|
||||
|
||||
list_for_each_entry(reader, &device->readers, node) {
|
||||
|
@ -295,7 +293,7 @@ EXPORT_SYMBOL_GPL(roccat_report_event);
|
|||
* Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on
|
||||
* success, a negative error code on failure.
|
||||
*/
|
||||
int roccat_connect(struct class *klass, struct hid_device *hid)
|
||||
int roccat_connect(struct class *klass, struct hid_device *hid, int report_size)
|
||||
{
|
||||
unsigned int minor;
|
||||
struct roccat_device *device;
|
||||
|
@ -343,6 +341,7 @@ int roccat_connect(struct class *klass, struct hid_device *hid)
|
|||
device->hid = hid;
|
||||
device->exist = 1;
|
||||
device->cbuf_end = 0;
|
||||
device->report_size = report_size;
|
||||
|
||||
return minor;
|
||||
}
|
||||
|
@ -357,13 +356,16 @@ void roccat_disconnect(int minor)
|
|||
|
||||
mutex_lock(&devices_lock);
|
||||
device = devices[minor];
|
||||
devices[minor] = NULL;
|
||||
mutex_unlock(&devices_lock);
|
||||
|
||||
device->exist = 0; /* TODO exist maybe not needed */
|
||||
|
||||
device_destroy(device->dev->class, MKDEV(roccat_major, minor));
|
||||
|
||||
mutex_lock(&devices_lock);
|
||||
devices[minor] = NULL;
|
||||
mutex_unlock(&devices_lock);
|
||||
|
||||
if (device->open) {
|
||||
hid_hw_close(device->hid);
|
||||
wake_up_interruptible(&device->wait);
|
||||
|
@ -373,6 +375,34 @@ void roccat_disconnect(int minor)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(roccat_disconnect);
|
||||
|
||||
static long roccat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
struct roccat_device *device;
|
||||
unsigned int minor = iminor(inode);
|
||||
long retval = 0;
|
||||
|
||||
mutex_lock(&devices_lock);
|
||||
|
||||
device = devices[minor];
|
||||
if (!device) {
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case ROCCATIOCGREPSIZE:
|
||||
if (put_user(device->report_size, (int __user *)arg))
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
default:
|
||||
retval = -ENOTTY;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&devices_lock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct file_operations roccat_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = roccat_read,
|
||||
|
@ -380,6 +410,7 @@ static const struct file_operations roccat_ops = {
|
|||
.open = roccat_open,
|
||||
.release = roccat_release,
|
||||
.llseek = noop_llseek,
|
||||
.unlocked_ioctl = roccat_ioctl,
|
||||
};
|
||||
|
||||
static int __init roccat_init(void)
|
||||
|
|
|
@ -46,6 +46,16 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
return rdesc;
|
||||
}
|
||||
|
||||
/*
|
||||
* The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP
|
||||
* like it should according to usbhid/hid-core.c::usbhid_output_raw_report()
|
||||
* so we need to override that forcing HID Output Reports on the Control EP.
|
||||
*
|
||||
* There is also another issue about HID Output Reports via USB, the Sixaxis
|
||||
* does not want the report_id as part of the data packet, so we have to
|
||||
* discard buf[0] when sending the actual control message, even for numbered
|
||||
* reports, humpf!
|
||||
*/
|
||||
static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
|
||||
size_t count, unsigned char report_type)
|
||||
{
|
||||
|
@ -55,6 +65,12 @@ static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
|
|||
int report_id = buf[0];
|
||||
int ret;
|
||||
|
||||
if (report_type == HID_OUTPUT_REPORT) {
|
||||
/* Don't send the Report ID */
|
||||
buf++;
|
||||
count--;
|
||||
}
|
||||
|
||||
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
HID_REQ_SET_REPORT,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
|
@ -62,6 +78,10 @@ static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
|
|||
interface->desc.bInterfaceNumber, buf, count,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
/* Count also the Report ID, in case of an Output report. */
|
||||
if (ret > 0 && report_type == HID_OUTPUT_REPORT)
|
||||
ret++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,
|
|||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
ret += len;
|
||||
ret = len;
|
||||
|
||||
kfree(list->buffer[list->tail].value);
|
||||
list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
|
||||
|
@ -102,15 +102,14 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,
|
|||
}
|
||||
|
||||
/* the first byte is expected to be a report number */
|
||||
static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
||||
/* This function is to be called with the minors_lock mutex held */
|
||||
static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type)
|
||||
{
|
||||
unsigned int minor = iminor(file->f_path.dentry->d_inode);
|
||||
struct hid_device *dev;
|
||||
__u8 *buf;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&minors_lock);
|
||||
|
||||
if (!hidraw_table[minor]) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
|
@ -148,14 +147,92 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT);
|
||||
ret = dev->hid_output_raw_report(dev, buf, count, report_type);
|
||||
out_free:
|
||||
kfree(buf);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* the first byte is expected to be a report number */
|
||||
static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
ssize_t ret;
|
||||
mutex_lock(&minors_lock);
|
||||
ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
|
||||
mutex_unlock(&minors_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* This function performs a Get_Report transfer over the control endpoint
|
||||
per section 7.2.1 of the HID specification, version 1.1. The first byte
|
||||
of buffer is the report number to request, or 0x0 if the defice does not
|
||||
use numbered reports. The report_type parameter can be HID_FEATURE_REPORT
|
||||
or HID_INPUT_REPORT. This function is to be called with the minors_lock
|
||||
mutex held. */
|
||||
static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type)
|
||||
{
|
||||
unsigned int minor = iminor(file->f_path.dentry->d_inode);
|
||||
struct hid_device *dev;
|
||||
__u8 *buf;
|
||||
int ret = 0, len;
|
||||
unsigned char report_number;
|
||||
|
||||
dev = hidraw_table[minor]->hid;
|
||||
|
||||
if (!dev->hid_get_raw_report) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (count > HID_MAX_BUFFER_SIZE) {
|
||||
printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
|
||||
task_pid_nr(current));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (count < 2) {
|
||||
printk(KERN_WARNING "hidraw: pid %d passed too short report\n",
|
||||
task_pid_nr(current));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Read the first byte from the user. This is the report number,
|
||||
which is passed to dev->hid_get_raw_report(). */
|
||||
if (copy_from_user(&report_number, buffer, 1)) {
|
||||
ret = -EFAULT;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type);
|
||||
|
||||
if (ret < 0)
|
||||
goto out_free;
|
||||
|
||||
len = (ret < count) ? ret : count;
|
||||
|
||||
if (copy_to_user(buffer, buf, len)) {
|
||||
ret = -EFAULT;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
ret = len;
|
||||
|
||||
out_free:
|
||||
kfree(buf);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int hidraw_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct hidraw_list *list = file->private_data;
|
||||
|
@ -295,7 +372,24 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
|
|||
default:
|
||||
{
|
||||
struct hid_device *hid = dev->hid;
|
||||
if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) {
|
||||
if (_IOC_TYPE(cmd) != 'H') {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) {
|
||||
int len = _IOC_SIZE(cmd);
|
||||
ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT);
|
||||
break;
|
||||
}
|
||||
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) {
|
||||
int len = _IOC_SIZE(cmd);
|
||||
ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Begin Read-only ioctls. */
|
||||
if (_IOC_DIR(cmd) != _IOC_READ) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
@ -327,7 +421,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
|
|||
-EFAULT : len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
|
@ -428,12 +522,12 @@ void hidraw_disconnect(struct hid_device *hid)
|
|||
|
||||
hidraw->exist = 0;
|
||||
|
||||
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
|
||||
|
||||
mutex_lock(&minors_lock);
|
||||
hidraw_table[hidraw->minor] = NULL;
|
||||
mutex_unlock(&minors_lock);
|
||||
|
||||
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
|
||||
|
||||
if (hidraw->open) {
|
||||
hid_hw_close(hid);
|
||||
wake_up_interruptible(&hidraw->wait);
|
||||
|
|
|
@ -799,6 +799,40 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int usbhid_get_raw_report(struct hid_device *hid,
|
||||
unsigned char report_number, __u8 *buf, size_t count,
|
||||
unsigned char report_type)
|
||||
{
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
struct usb_device *dev = hid_to_usb_dev(hid);
|
||||
struct usb_interface *intf = usbhid->intf;
|
||||
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||
int skipped_report_id = 0;
|
||||
int ret;
|
||||
|
||||
/* Byte 0 is the report number. Report data starts at byte 1.*/
|
||||
buf[0] = report_number;
|
||||
if (report_number == 0x0) {
|
||||
/* Offset the return buffer by 1, so that the report ID
|
||||
will remain in byte 0. */
|
||||
buf++;
|
||||
count--;
|
||||
skipped_report_id = 1;
|
||||
}
|
||||
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
|
||||
HID_REQ_GET_REPORT,
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
((report_type + 1) << 8) | report_number,
|
||||
interface->desc.bInterfaceNumber, buf, count,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
/* count also the report id */
|
||||
if (ret > 0 && skipped_report_id)
|
||||
ret++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count,
|
||||
unsigned char report_type)
|
||||
{
|
||||
|
@ -1139,6 +1173,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
|
|||
|
||||
usb_set_intfdata(intf, hid);
|
||||
hid->ll_driver = &usb_hid_driver;
|
||||
hid->hid_get_raw_report = usbhid_get_raw_report;
|
||||
hid->hid_output_raw_report = usbhid_output_raw_report;
|
||||
hid->ff_init = hid_pidff_init;
|
||||
#ifdef CONFIG_USB_HIDDEV
|
||||
|
|
|
@ -15,18 +15,15 @@
|
|||
#include <linux/hid.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#if defined(CONFIG_HID_ROCCAT) || defined(CONFIG_HID_ROCCAT_MODULE)
|
||||
int roccat_connect(struct class *klass, struct hid_device *hid);
|
||||
#define ROCCATIOCGREPSIZE _IOR('H', 0xf1, int)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
int roccat_connect(struct class *klass, struct hid_device *hid,
|
||||
int report_size);
|
||||
void roccat_disconnect(int minor);
|
||||
int roccat_report_event(int minor, u8 const *data, int len);
|
||||
#else
|
||||
static inline int roccat_connect(struct class *klass,
|
||||
struct hid_device *hid) { return -1; }
|
||||
static inline void roccat_disconnect(int minor) {}
|
||||
static inline int roccat_report_event(int minor, u8 const *data, int len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int roccat_report_event(int minor, u8 const *data);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -504,6 +504,9 @@ struct hid_device { /* device report descriptor */
|
|||
struct hid_usage *, __s32);
|
||||
void (*hiddev_report_event) (struct hid_device *, struct hid_report *);
|
||||
|
||||
/* handler for raw input (Get_Report) data, used by hidraw */
|
||||
int (*hid_get_raw_report) (struct hid_device *, unsigned char, __u8 *, size_t, unsigned char);
|
||||
|
||||
/* handler for raw output data, used by hidraw */
|
||||
int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char);
|
||||
|
||||
|
@ -638,7 +641,7 @@ struct hid_driver {
|
|||
struct hid_input *hidinput, struct hid_field *field,
|
||||
struct hid_usage *usage, unsigned long **bit, int *max);
|
||||
void (*feature_mapping)(struct hid_device *hdev,
|
||||
struct hid_input *hidinput, struct hid_field *field,
|
||||
struct hid_field *field,
|
||||
struct hid_usage *usage);
|
||||
#ifdef CONFIG_PM
|
||||
int (*suspend)(struct hid_device *hdev, pm_message_t message);
|
||||
|
|
|
@ -35,6 +35,9 @@ struct hidraw_devinfo {
|
|||
#define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo)
|
||||
#define HIDIOCGRAWNAME(len) _IOC(_IOC_READ, 'H', 0x04, len)
|
||||
#define HIDIOCGRAWPHYS(len) _IOC(_IOC_READ, 'H', 0x05, len)
|
||||
/* The first byte of SFEATURE and GFEATURE is the report number */
|
||||
#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
|
||||
#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
|
||||
|
||||
#define HIDRAW_FIRST_MINOR 0
|
||||
#define HIDRAW_MAX_DEVICES 64
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/file.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
|
@ -313,24 +314,144 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep
|
|||
return hidp_queue_report(session, buf, rsize);
|
||||
}
|
||||
|
||||
static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
|
||||
static int hidp_get_raw_report(struct hid_device *hid,
|
||||
unsigned char report_number,
|
||||
unsigned char *data, size_t count,
|
||||
unsigned char report_type)
|
||||
{
|
||||
struct hidp_session *session = hid->driver_data;
|
||||
struct sk_buff *skb;
|
||||
size_t len;
|
||||
int numbered_reports = hid->report_enum[report_type].numbered;
|
||||
|
||||
switch (report_type) {
|
||||
case HID_FEATURE_REPORT:
|
||||
report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
|
||||
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE;
|
||||
break;
|
||||
case HID_INPUT_REPORT:
|
||||
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT;
|
||||
break;
|
||||
case HID_OUTPUT_REPORT:
|
||||
report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
|
||||
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mutex_lock_interruptible(&session->report_mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
/* Set up our wait, and send the report request to the device. */
|
||||
session->waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK;
|
||||
session->waiting_report_number = numbered_reports ? report_number : -1;
|
||||
set_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
||||
data[0] = report_number;
|
||||
if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1))
|
||||
goto err_eio;
|
||||
|
||||
/* Wait for the return of the report. The returned report
|
||||
gets put in session->report_return. */
|
||||
while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) {
|
||||
int res;
|
||||
|
||||
res = wait_event_interruptible_timeout(session->report_queue,
|
||||
!test_bit(HIDP_WAITING_FOR_RETURN, &session->flags),
|
||||
5*HZ);
|
||||
if (res == 0) {
|
||||
/* timeout */
|
||||
goto err_eio;
|
||||
}
|
||||
if (res < 0) {
|
||||
/* signal */
|
||||
goto err_restartsys;
|
||||
}
|
||||
}
|
||||
|
||||
skb = session->report_return;
|
||||
if (skb) {
|
||||
len = skb->len < count ? skb->len : count;
|
||||
memcpy(data, skb->data, len);
|
||||
|
||||
kfree_skb(skb);
|
||||
session->report_return = NULL;
|
||||
} else {
|
||||
/* Device returned a HANDSHAKE, indicating protocol error. */
|
||||
len = -EIO;
|
||||
}
|
||||
|
||||
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
||||
mutex_unlock(&session->report_mutex);
|
||||
|
||||
return len;
|
||||
|
||||
err_restartsys:
|
||||
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
||||
mutex_unlock(&session->report_mutex);
|
||||
return -ERESTARTSYS;
|
||||
err_eio:
|
||||
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
||||
mutex_unlock(&session->report_mutex);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
|
||||
unsigned char report_type)
|
||||
{
|
||||
struct hidp_session *session = hid->driver_data;
|
||||
int ret;
|
||||
|
||||
switch (report_type) {
|
||||
case HID_FEATURE_REPORT:
|
||||
report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
|
||||
break;
|
||||
case HID_OUTPUT_REPORT:
|
||||
report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mutex_lock_interruptible(&session->report_mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
/* Set up our wait, and send the report request to the device. */
|
||||
set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
|
||||
if (hidp_send_ctrl_message(hid->driver_data, report_type,
|
||||
data, count))
|
||||
return -ENOMEM;
|
||||
return count;
|
||||
data, count)) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Wait for the ACK from the device. */
|
||||
while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
|
||||
int res;
|
||||
|
||||
res = wait_event_interruptible_timeout(session->report_queue,
|
||||
!test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags),
|
||||
10*HZ);
|
||||
if (res == 0) {
|
||||
/* timeout */
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
if (res < 0) {
|
||||
/* signal */
|
||||
ret = -ERESTARTSYS;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!session->output_report_success) {
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = count;
|
||||
|
||||
err:
|
||||
clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
|
||||
mutex_unlock(&session->report_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hidp_idle_timeout(unsigned long arg)
|
||||
|
@ -357,16 +478,22 @@ static void hidp_process_handshake(struct hidp_session *session,
|
|||
unsigned char param)
|
||||
{
|
||||
BT_DBG("session %p param 0x%02x", session, param);
|
||||
session->output_report_success = 0; /* default condition */
|
||||
|
||||
switch (param) {
|
||||
case HIDP_HSHK_SUCCESSFUL:
|
||||
/* FIXME: Call into SET_ GET_ handlers here */
|
||||
session->output_report_success = 1;
|
||||
break;
|
||||
|
||||
case HIDP_HSHK_NOT_READY:
|
||||
case HIDP_HSHK_ERR_INVALID_REPORT_ID:
|
||||
case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
|
||||
case HIDP_HSHK_ERR_INVALID_PARAMETER:
|
||||
if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) {
|
||||
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
||||
wake_up_interruptible(&session->report_queue);
|
||||
}
|
||||
/* FIXME: Call into SET_ GET_ handlers here */
|
||||
break;
|
||||
|
||||
|
@ -385,6 +512,12 @@ static void hidp_process_handshake(struct hidp_session *session,
|
|||
HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Wake up the waiting thread. */
|
||||
if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
|
||||
clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
|
||||
wake_up_interruptible(&session->report_queue);
|
||||
}
|
||||
}
|
||||
|
||||
static void hidp_process_hid_control(struct hidp_session *session,
|
||||
|
@ -403,9 +536,11 @@ static void hidp_process_hid_control(struct hidp_session *session,
|
|||
}
|
||||
}
|
||||
|
||||
static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
|
||||
/* Returns true if the passed-in skb should be freed by the caller. */
|
||||
static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
|
||||
unsigned char param)
|
||||
{
|
||||
int done_with_skb = 1;
|
||||
BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param);
|
||||
|
||||
switch (param) {
|
||||
|
@ -417,7 +552,6 @@ static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
|
|||
|
||||
if (session->hid)
|
||||
hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0);
|
||||
|
||||
break;
|
||||
|
||||
case HIDP_DATA_RTYPE_OTHER:
|
||||
|
@ -429,12 +563,27 @@ static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
|
|||
__hidp_send_ctrl_message(session,
|
||||
HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
|
||||
}
|
||||
|
||||
if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) &&
|
||||
param == session->waiting_report_type) {
|
||||
if (session->waiting_report_number < 0 ||
|
||||
session->waiting_report_number == skb->data[0]) {
|
||||
/* hidp_get_raw_report() is waiting on this report. */
|
||||
session->report_return = skb;
|
||||
done_with_skb = 0;
|
||||
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
||||
wake_up_interruptible(&session->report_queue);
|
||||
}
|
||||
}
|
||||
|
||||
return done_with_skb;
|
||||
}
|
||||
|
||||
static void hidp_recv_ctrl_frame(struct hidp_session *session,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
unsigned char hdr, type, param;
|
||||
int free_skb = 1;
|
||||
|
||||
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
|
||||
|
||||
|
@ -454,7 +603,7 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session,
|
|||
break;
|
||||
|
||||
case HIDP_TRANS_DATA:
|
||||
hidp_process_data(session, skb, param);
|
||||
free_skb = hidp_process_data(session, skb, param);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -463,7 +612,8 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session,
|
|||
break;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
if (free_skb)
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void hidp_recv_intr_frame(struct hidp_session *session,
|
||||
|
@ -563,6 +713,8 @@ static int hidp_session(void *arg)
|
|||
init_waitqueue_entry(&intr_wait, current);
|
||||
add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
|
||||
add_wait_queue(sk_sleep(intr_sk), &intr_wait);
|
||||
session->waiting_for_startup = 0;
|
||||
wake_up_interruptible(&session->startup_queue);
|
||||
while (!atomic_read(&session->terminate)) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
|
@ -754,6 +906,8 @@ static struct hid_ll_driver hidp_hid_driver = {
|
|||
.hidinput_input_event = hidp_hidinput_event,
|
||||
};
|
||||
|
||||
/* This function sets up the hid device. It does not add it
|
||||
to the HID system. That is done in hidp_add_connection(). */
|
||||
static int hidp_setup_hid(struct hidp_session *session,
|
||||
struct hidp_connadd_req *req)
|
||||
{
|
||||
|
@ -793,18 +947,11 @@ static int hidp_setup_hid(struct hidp_session *session,
|
|||
hid->dev.parent = hidp_get_device(session);
|
||||
hid->ll_driver = &hidp_hid_driver;
|
||||
|
||||
hid->hid_get_raw_report = hidp_get_raw_report;
|
||||
hid->hid_output_raw_report = hidp_output_raw_report;
|
||||
|
||||
err = hid_add_device(hid);
|
||||
if (err < 0)
|
||||
goto failed;
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
hid_destroy_device(hid);
|
||||
session->hid = NULL;
|
||||
|
||||
fault:
|
||||
kfree(session->rd_data);
|
||||
session->rd_data = NULL;
|
||||
|
@ -853,6 +1000,10 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
skb_queue_head_init(&session->ctrl_transmit);
|
||||
skb_queue_head_init(&session->intr_transmit);
|
||||
|
||||
mutex_init(&session->report_mutex);
|
||||
init_waitqueue_head(&session->report_queue);
|
||||
init_waitqueue_head(&session->startup_queue);
|
||||
session->waiting_for_startup = 1;
|
||||
session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
|
||||
session->idle_to = req->idle_to;
|
||||
|
||||
|
@ -875,6 +1026,14 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
err = kernel_thread(hidp_session, session, CLONE_KERNEL);
|
||||
if (err < 0)
|
||||
goto unlink;
|
||||
while (session->waiting_for_startup) {
|
||||
wait_event_interruptible(session->startup_queue,
|
||||
!session->waiting_for_startup);
|
||||
}
|
||||
|
||||
err = hid_add_device(session->hid);
|
||||
if (err < 0)
|
||||
goto err_add_device;
|
||||
|
||||
if (session->input) {
|
||||
hidp_send_ctrl_message(session,
|
||||
|
@ -888,6 +1047,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
up_write(&hidp_session_sem);
|
||||
return 0;
|
||||
|
||||
err_add_device:
|
||||
hid_destroy_device(session->hid);
|
||||
session->hid = NULL;
|
||||
atomic_inc(&session->terminate);
|
||||
hidp_schedule(session);
|
||||
|
||||
unlink:
|
||||
hidp_del_timer(session);
|
||||
|
||||
|
|
|
@ -80,6 +80,8 @@
|
|||
#define HIDP_VIRTUAL_CABLE_UNPLUG 0
|
||||
#define HIDP_BOOT_PROTOCOL_MODE 1
|
||||
#define HIDP_BLUETOOTH_VENDOR_ID 9
|
||||
#define HIDP_WAITING_FOR_RETURN 10
|
||||
#define HIDP_WAITING_FOR_SEND_ACK 11
|
||||
|
||||
struct hidp_connadd_req {
|
||||
int ctrl_sock; // Connected control socket
|
||||
|
@ -154,9 +156,22 @@ struct hidp_session {
|
|||
struct sk_buff_head ctrl_transmit;
|
||||
struct sk_buff_head intr_transmit;
|
||||
|
||||
/* Used in hidp_get_raw_report() */
|
||||
int waiting_report_type; /* HIDP_DATA_RTYPE_* */
|
||||
int waiting_report_number; /* -1 for not numbered */
|
||||
struct mutex report_mutex;
|
||||
struct sk_buff *report_return;
|
||||
wait_queue_head_t report_queue;
|
||||
|
||||
/* Used in hidp_output_raw_report() */
|
||||
int output_report_success; /* boolean */
|
||||
|
||||
/* Report descriptor */
|
||||
__u8 *rd_data;
|
||||
uint rd_size;
|
||||
|
||||
wait_queue_head_t startup_queue;
|
||||
int waiting_for_startup;
|
||||
};
|
||||
|
||||
static inline void hidp_schedule(struct hidp_session *session)
|
||||
|
|
Loading…
Reference in New Issue