Merge branches 'hidraw', 'magicmouse', 'multitouch', 'roccat', 'suspend-fixes' and...
authorJiri Kosina <jkosina@suse.cz>
Tue, 24 Jul 2012 11:39:00 +0000 (13:39 +0200)
committerJiri Kosina <jkosina@suse.cz>
Tue, 24 Jul 2012 11:39:00 +0000 (13:39 +0200)
32 files changed:
Documentation/ABI/testing/sysfs-driver-hid-lenovo-tpkbd [new file with mode: 0644]
Documentation/ABI/testing/sysfs-driver-hid-roccat-savu [new file with mode: 0644]
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-chicony.c
drivers/hid/hid-core.c
drivers/hid/hid-cypress.c
drivers/hid/hid-holtek-kbd.c [new file with mode: 0644]
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-lenovo-tpkbd.c [new file with mode: 0644]
drivers/hid/hid-magicmouse.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-picolcd.c
drivers/hid/hid-roccat-arvo.c
drivers/hid/hid-roccat-common.c
drivers/hid/hid-roccat-common.h
drivers/hid/hid-roccat-isku.c
drivers/hid/hid-roccat-isku.h
drivers/hid/hid-roccat-kone.c
drivers/hid/hid-roccat-koneplus.c
drivers/hid/hid-roccat-koneplus.h
drivers/hid/hid-roccat-kovaplus.c
drivers/hid/hid-roccat-kovaplus.h
drivers/hid/hid-roccat-pyra.c
drivers/hid/hid-roccat-pyra.h
drivers/hid/hid-roccat-savu.c [new file with mode: 0644]
drivers/hid/hid-roccat-savu.h [new file with mode: 0644]
drivers/hid/hid-wiimote-ext.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/usbhid.h
include/linux/hid.h

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