Merge branches 'dragonrise', 'hidraw-feature', 'multitouch', 'ntrig', 'roccat', ...
authorJiri Kosina <jkosina@suse.cz>
Thu, 17 Mar 2011 13:31:46 +0000 (14:31 +0100)
committerJiri Kosina <jkosina@suse.cz>
Thu, 17 Mar 2011 13:31:46 +0000 (14:31 +0100)
36 files changed:
Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo [new file with mode: 0644]
Documentation/ABI/testing/sysfs-driver-hid-roccat-kone
Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus
Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus [new file with mode: 0644]
Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra
Documentation/ioctl/ioctl-number.txt
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-dr.c [moved from drivers/hid/hid-drff.c with 54% similarity]
drivers/hid/hid-egalax.c [deleted file]
drivers/hid/hid-gyration.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-lg.c
drivers/hid/hid-magicmouse.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-ntrig.c
drivers/hid/hid-ortek.c
drivers/hid/hid-roccat-arvo.c [new file with mode: 0644]
drivers/hid/hid-roccat-arvo.h [new file with mode: 0644]
drivers/hid/hid-roccat-common.c [new file with mode: 0644]
drivers/hid/hid-roccat-common.h [new file with mode: 0644]
drivers/hid/hid-roccat-kone.c
drivers/hid/hid-roccat-koneplus.c
drivers/hid/hid-roccat-kovaplus.c [new file with mode: 0644]
drivers/hid/hid-roccat-kovaplus.h [new file with mode: 0644]
drivers/hid/hid-roccat-pyra.c
drivers/hid/hid-roccat.c
drivers/hid/hidraw.c
drivers/hid/usbhid/hid-core.c
include/linux/hid-roccat.h [moved from drivers/hid/hid-roccat.h with 52% similarity]
include/linux/hid.h
include/linux/hidraw.h
net/bluetooth/hidp/core.c
net/bluetooth/hidp/hidp.h

diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo b/Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo
new file mode 100644 (file)
index 0000000..55e281b
--- /dev/null
@@ -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
index 698b808..b4c4f15 100644 (file)
@@ -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
index 0f9f30e..00efced 100644 (file)
@@ -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
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus
new file mode 100644 (file)
index 0000000..fdfa16f
--- /dev/null
@@ -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
index 1c37b82..5fab71a 100644 (file)
@@ -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
index ac293e9..e68543f 100644 (file)
@@ -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!
index d942d42..d812392 100644 (file)
@@ -146,7 +146,12 @@ config HID_DRAGONRISE
        tristate "DragonRise Inc. game controller"
        depends on USB_HID
        ---help---
-       Say Y here if you have DragonRise Inc.game controllers.
+       Say Y here if you have DragonRise Inc. game controllers.
+       These might be branded as:
+       - Tesun USB-703
+       - Media-tech MT1504 "Rogue"
+       - DVTech JS19 "Gear"
+       - Defender Game Master
 
 config DRAGONRISE_FF
        bool "DragonRise Inc. force feedback"
@@ -166,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
@@ -324,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.
 
@@ -339,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"
@@ -437,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.
 
@@ -448,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.
 
index 125ba10..06c68ae 100644 (file)
@@ -34,9 +34,8 @@ obj-$(CONFIG_HID_CANDO)               += hid-cando.o
 obj-$(CONFIG_HID_CHERRY)       += hid-cherry.o
 obj-$(CONFIG_HID_CHICONY)      += hid-chicony.o
 obj-$(CONFIG_HID_CYPRESS)      += hid-cypress.o
-obj-$(CONFIG_HID_DRAGONRISE)   += hid-drff.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
@@ -58,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
index c650efb..c3d6626 100644 (file)
@@ -1359,6 +1359,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
        { 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) },
@@ -1376,6 +1377,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
        { HID_USB_DEVICE(USB_VENDOR_ID_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) },
@@ -1401,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) },
@@ -1433,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) },
similarity index 54%
rename from drivers/hid/hid-drff.c
rename to drivers/hid/hid-dr.c
index afcf3d6..61eece4 100644 (file)
@@ -145,6 +145,110 @@ static inline int drff_init(struct hid_device *hid)
 }
 #endif
 
+/*
+ * The original descriptor of joystick with PID 0x0011, represented by DVTech PC
+ * JS19. It seems both copied from another device and a result of confusion
+ * either about the specification or about the program used to create the
+ * descriptor. In any case, it's a wonder it works on Windows.
+ *
+ *  Usage Page (Desktop),             ; Generic desktop controls (01h)
+ *  Usage (Joystik),                  ; Joystik (04h, application collection)
+ *  Collection (Application),
+ *    Collection (Logical),
+ *      Report Size (8),
+ *      Report Count (5),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Physical Minimum (0),
+ *      Physical Maximum (255),
+ *      Usage (X),                    ; X (30h, dynamic value)
+ *      Usage (X),                    ; X (30h, dynamic value)
+ *      Usage (X),                    ; X (30h, dynamic value)
+ *      Usage (X),                    ; X (30h, dynamic value)
+ *      Usage (Y),                    ; Y (31h, dynamic value)
+ *      Input (Variable),
+ *      Report Size (4),
+ *      Report Count (1),
+ *      Logical Maximum (7),
+ *      Physical Maximum (315),
+ *      Unit (Degrees),
+ *      Usage (00h),
+ *      Input (Variable, Null State),
+ *      Unit,
+ *      Report Size (1),
+ *      Report Count (10),
+ *      Logical Maximum (1),
+ *      Physical Maximum (1),
+ *      Usage Page (Button),          ; Button (09h)
+ *      Usage Minimum (01h),
+ *      Usage Maximum (0Ah),
+ *      Input (Variable),
+ *      Usage Page (FF00h),           ; FF00h, vendor-defined
+ *      Report Size (1),
+ *      Report Count (10),
+ *      Logical Maximum (1),
+ *      Physical Maximum (1),
+ *      Usage (01h),
+ *      Input (Variable),
+ *    End Collection,
+ *    Collection (Logical),
+ *      Report Size (8),
+ *      Report Count (4),
+ *      Physical Maximum (255),
+ *      Logical Maximum (255),
+ *      Usage (02h),
+ *      Output (Variable),
+ *    End Collection,
+ *  End Collection
+ */
+
+/* Size of the original descriptor of the PID 0x0011 joystick */
+#define PID0011_RDESC_ORIG_SIZE        101
+
+/* Fixed report descriptor for PID 0x011 joystick */
+static __u8 pid0011_rdesc_fixed[] = {
+       0x05, 0x01,         /*  Usage Page (Desktop),           */
+       0x09, 0x04,         /*  Usage (Joystik),                */
+       0xA1, 0x01,         /*  Collection (Application),       */
+       0xA1, 0x02,         /*      Collection (Logical),       */
+       0x14,               /*          Logical Minimum (0),    */
+       0x75, 0x08,         /*          Report Size (8),        */
+       0x95, 0x03,         /*          Report Count (3),       */
+       0x81, 0x01,         /*          Input (Constant),       */
+       0x26, 0xFF, 0x00,   /*          Logical Maximum (255),  */
+       0x95, 0x02,         /*          Report Count (2),       */
+       0x09, 0x30,         /*          Usage (X),              */
+       0x09, 0x31,         /*          Usage (Y),              */
+       0x81, 0x02,         /*          Input (Variable),       */
+       0x75, 0x01,         /*          Report Size (1),        */
+       0x95, 0x04,         /*          Report Count (4),       */
+       0x81, 0x01,         /*          Input (Constant),       */
+       0x25, 0x01,         /*          Logical Maximum (1),    */
+       0x95, 0x0A,         /*          Report Count (10),      */
+       0x05, 0x09,         /*          Usage Page (Button),    */
+       0x19, 0x01,         /*          Usage Minimum (01h),    */
+       0x29, 0x0A,         /*          Usage Maximum (0Ah),    */
+       0x81, 0x02,         /*          Input (Variable),       */
+       0x95, 0x0A,         /*          Report Count (10),      */
+       0x81, 0x01,         /*          Input (Constant),       */
+       0xC0,               /*      End Collection,             */
+       0xC0                /*  End Collection                  */
+};
+
+static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+                               unsigned int *rsize)
+{
+       switch (hdev->product) {
+       case 0x0011:
+               if (*rsize == PID0011_RDESC_ORIG_SIZE) {
+                       rdesc = pid0011_rdesc_fixed;
+                       *rsize = sizeof(pid0011_rdesc_fixed);
+               }
+               break;
+       }
+       return rdesc;
+}
+
 static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        int ret;
@@ -163,7 +267,16 @@ static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id)
                goto err;
        }
 
-       drff_init(hdev);
+       switch (hdev->product) {
+       case 0x0006:
+               ret = drff_init(hdev);
+               if (ret) {
+                       dev_err(&hdev->dev, "force feedback init failed\n");
+                       hid_hw_stop(hdev);
+                       goto err;
+               }
+               break;
+       }
 
        return 0;
 err:
@@ -172,6 +285,7 @@ err:
 
 static const struct hid_device_id dr_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006),  },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011),  },
        { }
 };
 MODULE_DEVICE_TABLE(hid, dr_devices);
@@ -179,6 +293,7 @@ MODULE_DEVICE_TABLE(hid, dr_devices);
 static struct hid_driver dr_driver = {
        .name = "dragonrise",
        .id_table = dr_devices,
+       .report_fixup = dr_report_fixup,
        .probe = dr_probe,
 };
 
diff --git a/drivers/hid/hid-egalax.c b/drivers/hid/hid-egalax.c
deleted file mode 100644 (file)
index 03bee19..0000000
+++ /dev/null
@@ -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);
-
index 3975e03..e88b951 100644 (file)
@@ -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;
index 8fc82a7..d485894 100644 (file)
 #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
 
 #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
 #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
 #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
 
index ebcc02a..cd74203 100644 (file)
@@ -940,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:
index aef4104..3da9040 100644 (file)
@@ -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 ),
index 698e645..318cc40 100644 (file)
@@ -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
index 2bbc954..ee01e65 100644 (file)
@@ -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,6 +128,14 @@ 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,
+       },
        { }
 };
 
@@ -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);
index beb4034..9fae2eb 100644 (file)
@@ -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:
-                       /*
-                        * 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;
+               nd->mt_footer[nd->mt_foot_count++] = value;
 
-                       nd->mt_footer[nd->mt_foot_count++] = value;
+               /* if the footer isn't complete break */
+               if (nd->mt_foot_count != 4)
+                       break;
 
-                       /* 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;
+                       nd->confidence = 0;
+                       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;
-                       }
+               } else
+                       break;
 
+               if (nd->act_state > 0) {
                        /*
-                        * The first footer value indicates the presence of a
-                        * finger.
+                        * Contact meets the activation size threshold
                         */
-                       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
+                       if (nd->w >= nd->activation_width &&
+                           nd->h >= nd->activation_height) {
+                               if (nd->id)
                                        /*
-                                        * Defer adjusting the activation state
-                                        * until the end of the frame.
+                                        * 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;
-                       }
-
-                       /* Discarding this contact */
-                       if (!nd->confidence)
-                               break;
-
-                       /* emit a normal (X, Y) for the first point only */
-                       if (nd->id == 0) {
+                               }
+                       } else
                                /*
-                                * TipSwitch is superfluous in multitouch
-                                * mode.  The footer events tell us
-                                * if there is a finger on the screen or
-                                * not.
+                                * Defer adjusting the activation state
+                                * until the end of the frame.
                                 */
-                               nd->first_contact_touch = nd->confidence;
-                               input_event(input, EV_ABS, ABS_X, nd->x);
-                               input_event(input, EV_ABS, ABS_Y, nd->y);
-                       }
+                               break;
+               }
 
-                       /* 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);
+               /* Discarding this contact */
+               if (!nd->confidence)
+                       break;
 
+               /* emit a normal (X, Y) for the first point only */
+               if (nd->id == 0) {
                        /*
-                        * Translate from height and width to size
-                        * and orientation.
+                        * TipSwitch is superfluous in multitouch
+                        * mode.  The footer events tell us
+                        * if there is a finger on the screen or
+                        * not.
                         */
-                       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;
+                       nd->first_contact_touch = nd->confidence;
+                       input_event(input, EV_ABS, ABS_X, nd->x);
+                       input_event(input, EV_ABS, ABS_Y, nd->y);
+               }
 
-               case HID_DG_CONTACTCOUNT: /* End of a multitouch group */
-                       if (!nd->reading_mt) /* Just to be sure */
-                               break;
+               /* 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;
 
-                       nd->reading_mt = 0;
+       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;
 
                        /*
-                        * 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
+                        * Entered this block inactive and no
+                        * coordinates sent this frame, so hold off
+                        * on button state.
                         */
-
-                       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;
-
+                       break;
+               } else { /* Currently active */
+                       if (value && nd->act_state >=
+                                    nd->deactivate_slack)
                                /*
-                                * Entered this block inactive and no
-                                * coordinates sent this frame, so hold off
-                                * on button state.
+                                * Live point: clear accumulated
+                                * deactivation count.
                                 */
-                               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) {
+                               nd->act_state = 0;
+                       else if (nd->act_state <= nd->deactivate_slack)
                                /*
-                                * 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.
+                                * We've consumed the deactivation
+                                * slack, time to deactivate and reset.
                                 */
-                               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);
+                               nd->act_state =
+                                       nd->activate_slack;
+                       else { /* Move towards deactivation */
+                               nd->act_state--;
+                               break;
                        }
-                       break;
+               }
 
-               default:
-                       /* fall-back to the generic hidinput handling */
-                       return 0;
+               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);
 
index e90edfc..f9b7dd4 100644 (file)
@@ -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) },
        { }
 };
diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c
new file mode 100644 (file)
index 0000000..2307471
--- /dev/null
@@ -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");
diff --git a/drivers/hid/hid-roccat-arvo.h b/drivers/hid/hid-roccat-arvo.h
new file mode 100644 (file)
index 0000000..d284a78
--- /dev/null
@@ -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
diff --git a/drivers/hid/hid-roccat-common.c b/drivers/hid/hid-roccat-common.c
new file mode 100644 (file)
index 0000000..13b1eb0
--- /dev/null
@@ -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");
diff --git a/drivers/hid/hid-roccat-common.h b/drivers/hid/hid-roccat-common.h
new file mode 100644 (file)
index 0000000..fe45fae
--- /dev/null
@@ -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
index cbd8cc4..a57838d 100644 (file)
 #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;
-
-       data = kmalloc(2, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
+       int retval;
+       uint16_t data;
 
-       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);
+       retval = roccat_common_receive(usb_dev, kone_command_firmware_version,
+                       &data, 2);
+       if (retval)
+               return retval;
 
-       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);
index 1608c8d..33eec74 100644 (file)
 #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;
-
-       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);
-
-       if (len != size)
-               return -EIO;
+               void const *buf, uint size)
+{
+       int retval;
 
-       if (koneplus_receive_control_status(usb_dev))
-               return -EIO;
+       retval = roccat_common_send(usb_dev, command, buf, size);
+       if (retval)
+               return retval;
 
-       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);
diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c
new file mode 100644 (file)
index 0000000..984be2f
--- /dev/null
@@ -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");
diff --git a/drivers/hid/hid-roccat-kovaplus.h b/drivers/hid/hid-roccat-kovaplus.h
new file mode 100644 (file)
index 0000000..ce40607
--- /dev/null
@@ -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
index 02c58e0..160f481 100644 (file)
 #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);
index a14c579..5666e75 100644 (file)
@@ -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
 
 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)
index 66fbcba..54409cb 100644 (file)
@@ -102,15 +102,14 @@ out:
 }
 
 /* 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);
index b336dd8..38c261a 100644 (file)
@@ -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
similarity index 52%
rename from drivers/hid/hid-roccat.h
rename to include/linux/hid-roccat.h
index 5784281..24e1ca0 100644 (file)
 #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
index fc5faf6..bb29bb1 100644 (file)
@@ -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);
 
index dd8d692..4b88e69 100644 (file)
@@ -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
index e286e70..3c036b0 100644 (file)
@@ -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,9 +314,92 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep
        return hidp_queue_report(session, buf, rsize);
 }
 
+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_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_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;
@@ -327,10 +411,47 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s
                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);
 
index 8d934a1..13de5fa 100644 (file)
@@ -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)