2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-11-17 23:25:46 +08:00

Merge branches 'hidraw', 'magicmouse', 'multitouch', 'roccat', 'suspend-fixes' and 'upstream' into for-linus

This commit is contained in:
Jiri Kosina 2012-07-24 13:39:00 +02:00
32 changed files with 1693 additions and 546 deletions

View File

@ -0,0 +1,38 @@
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/press_to_select
Date: July 2011
Contact: linux-input@vger.kernel.org
Description: This controls if mouse clicks should be generated if the trackpoint is quickly pressed. How fast this press has to be
is being controlled by press_speed.
Values are 0 or 1.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/dragging
Date: July 2011
Contact: linux-input@vger.kernel.org
Description: If this setting is enabled, it is possible to do dragging by pressing the trackpoint. This requires press_to_select to be enabled.
Values are 0 or 1.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/release_to_select
Date: July 2011
Contact: linux-input@vger.kernel.org
Description: For details regarding this setting please refer to http://www.pc.ibm.com/ww/healthycomputing/trkpntb.html
Values are 0 or 1.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/select_right
Date: July 2011
Contact: linux-input@vger.kernel.org
Description: This setting controls if the mouse click events generated by pressing the trackpoint (if press_to_select is enabled) generate
a left or right mouse button click.
Values are 0 or 1.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/sensitivity
Date: July 2011
Contact: linux-input@vger.kernel.org
Description: This file contains the trackpoint sensitivity.
Values are decimal integers from 1 (lowest sensitivity) to 255 (highest sensitivity).
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/press_speed
Date: July 2011
Contact: linux-input@vger.kernel.org
Description: This setting controls how fast the trackpoint needs to be pressed to generate a mouse click if press_to_select is enabled.
Values are decimal integers from 1 (slowest) to 255 (fastest).

View File

@ -0,0 +1,77 @@
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/buttons
Date: Mai 2012
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: The mouse can store 5 profiles which can be switched by the
press of a button. A profile is split into general settings and
button settings. buttons holds informations about button layout.
When written, this file lets one write the respective profile
buttons to the mouse. The data has to be 47 bytes long.
The mouse will reject invalid data.
Which profile to write is determined by the profile number
contained in the data.
Before reading this file, control has to be written to select
which profile to read.
Users: http://roccat.sourceforge.net
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/control
Date: Mai 2012
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: When written, this file lets one select which data from which
profile will be read next. The data has to be 3 bytes long.
This file is writeonly.
Users: http://roccat.sourceforge.net
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/general
Date: Mai 2012
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: The mouse can store 5 profiles which can be switched by the
press of a button. A profile is split into general settings and
button settings. profile holds informations like resolution, sensitivity
and light effects.
When written, this file lets one write the respective profile
settings back to the mouse. The data has to be 43 bytes long.
The mouse will reject invalid data.
Which profile to write is determined by the profile number
contained in the data.
This file is writeonly.
Users: http://roccat.sourceforge.net
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/info
Date: Mai 2012
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: When read, this file returns general data like firmware version.
The data is 8 bytes long.
This file is readonly.
Users: http://roccat.sourceforge.net
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/macro
Date: Mai 2012
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: When written, this file lets one store macros with max 500
keystrokes for a specific button for a specific profile.
Button and profile numbers are included in written data.
The data has to be 2083 bytes long.
Before reading this file, control has to be written to select
which profile and key to read.
Users: http://roccat.sourceforge.net
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/profile
Date: Mai 2012
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: The mouse can store 5 profiles which can be switched by the
press of a button. profile holds number of actual profile.
This value is persistent, so its value determines the profile
that's active when the mouse is powered on next time.
When written, the mouse activates the set profile immediately.
The data has to be 3 bytes long.
The mouse will reject invalid data.
Users: http://roccat.sourceforge.net
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/savu/roccatsavu<minor>/sensor
Date: July 2012
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: The mouse has a Avago ADNS-3090 sensor.
This file allows reading and writing of the mouse sensors registers.
The data has to be 4 bytes long.
Users: http://roccat.sourceforge.net

View 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

View 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

View 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);

View 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) },

View 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 },
{ }

View File

@ -0,0 +1,183 @@
/*
* HID driver for Holtek keyboard
* Copyright (c) 2012 Tom Harwood
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/usb.h>
#include "hid-ids.h"
#include "usbhid/usbhid.h"
/* Holtek based keyboards (USB ID 04d9:a055) have the following issues:
* - The report descriptor specifies an excessively large number of consumer
* usages (2^15), which is more than HID_MAX_USAGES. This prevents proper
* parsing of the report descriptor.
* - The report descriptor reports on caps/scroll/num lock key presses, but
* doesn't have an LED output usage block.
*
* The replacement descriptor below fixes the number of consumer usages,
* and provides an LED output usage block. LED output events are redirected
* to the boot interface.
*/
static __u8 holtek_kbd_rdesc_fixed[] = {
/* Original report descriptor, with reduced number of consumer usages */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x80, /* Usage (Sys Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x01, /* Report ID (1), */
0x19, 0x81, /* Usage Minimum (Sys Power Down), */
0x29, 0x83, /* Usage Maximum (Sys Wake Up), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x03, /* Report Count (3), */
0x75, 0x01, /* Report Size (1), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x01, /* Report Count (1), */
0x75, 0x05, /* Report Size (5), */
0x81, 0x01, /* Input (Constant), */
0xC0, /* End Collection, */
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x02, /* Report ID (2), */
0x19, 0x00, /* Usage Minimum (00h), */
0x2A, 0xFF, 0x2F, /* Usage Maximum (0x2FFF), previously 0x7FFF */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x2F, /* Logical Maximum (0x2FFF),previously 0x7FFF*/
0x95, 0x01, /* Report Count (1), */
0x75, 0x10, /* Report Size (16), */
0x81, 0x00, /* Input, */
0xC0, /* End Collection, */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x06, /* Usage (Keyboard), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x03, /* Report ID (3), */
0x95, 0x38, /* Report Count (56), */
0x75, 0x01, /* Report Size (1), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x05, 0x07, /* Usage Page (Keyboard), */
0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */
0x29, 0xE7, /* Usage Maximum (KB Right GUI), */
0x19, 0x00, /* Usage Minimum (None), */
0x29, 0x2F, /* Usage Maximum (KB Lboxbracket And Lbrace),*/
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x06, /* Usage (Keyboard), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x04, /* Report ID (4), */
0x95, 0x38, /* Report Count (56), */
0x75, 0x01, /* Report Size (1), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x05, 0x07, /* Usage Page (Keyboard), */
0x19, 0x30, /* Usage Minimum (KB Rboxbracket And Rbrace),*/
0x29, 0x67, /* Usage Maximum (KP Equals), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection */
/* LED usage for the boot protocol interface */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x06, /* Usage (Keyboard), */
0xA1, 0x01, /* Collection (Application), */
0x05, 0x08, /* Usage Page (LED), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x03, /* Usage Maximum (03h), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x03, /* Report Count (3), */
0x91, 0x02, /* Output (Variable), */
0x95, 0x05, /* Report Count (5), */
0x91, 0x01, /* Output (Constant), */
0xC0, /* End Collection */
};
static __u8 *holtek_kbd_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
rdesc = holtek_kbd_rdesc_fixed;
*rsize = sizeof(holtek_kbd_rdesc_fixed);
}
return rdesc;
}
static int holtek_kbd_input_event(struct input_dev *dev, unsigned int type,
unsigned int code,
int value)
{
struct hid_device *hid = input_get_drvdata(dev);
struct usb_device *usb_dev = hid_to_usb_dev(hid);
/* Locate the boot interface, to receive the LED change events */
struct usb_interface *boot_interface = usb_ifnum_to_if(usb_dev, 0);
struct hid_device *boot_hid = usb_get_intfdata(boot_interface);
struct hid_input *boot_hid_input = list_first_entry(&boot_hid->inputs,
struct hid_input, list);
return boot_hid_input->input->event(boot_hid_input->input, type, code,
value);
}
static int holtek_kbd_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
int ret = hid_parse(hdev);
if (!ret)
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (!ret && intf->cur_altsetting->desc.bInterfaceNumber == 1) {
struct hid_input *hidinput;
list_for_each_entry(hidinput, &hdev->inputs, list) {
hidinput->input->event = holtek_kbd_input_event;
}
}
return ret;
}
static const struct hid_device_id holtek_kbd_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },
{ }
};
MODULE_DEVICE_TABLE(hid, holtek_kbd_devices);
static struct hid_driver holtek_kbd_driver = {
.name = "holtek_kbd",
.id_table = holtek_kbd_devices,
.report_fixup = holtek_kbd_report_fixup,
.probe = holtek_kbd_probe
};
static int __init holtek_kbd_init(void)
{
return hid_register_driver(&holtek_kbd_driver);
}
static void __exit holtek_kbd_exit(void)
{
hid_unregister_driver(&holtek_kbd_driver);
}
module_exit(holtek_kbd_exit);
module_init(holtek_kbd_init);
MODULE_LICENSE("GPL");

View File

@ -208,6 +208,7 @@
#define USB_DEVICE_ID_CHICONY_MULTI_TOUCH 0xb19d
#define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618
#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123
#define USB_DEVICE_ID_CHICONY_AK1D 0x1125
#define USB_VENDOR_ID_CHUNGHWAT 0x2247
#define USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH 0x0001
@ -237,6 +238,7 @@
#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61
#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64
#define USB_DEVICE_ID_CYPRESS_BARCODE_3 0xbca1
#define USB_DEVICE_ID_CYPRESS_BARCODE_4 0xed81
#define USB_DEVICE_ID_CYPRESS_TRUETOUCH 0xc001
#define USB_VENDOR_ID_DEALEXTREAME 0x10c5
@ -410,6 +412,9 @@
#define USB_VENDOR_ID_HOLTEK 0x1241
#define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP 0x5015
#define USB_VENDOR_ID_HOLTEK_ALT 0x04d9
#define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD 0xa055
#define USB_VENDOR_ID_IMATION 0x0718
#define USB_DEVICE_ID_DISC_STAKKA 0xd000
@ -479,6 +484,9 @@
#define USB_DEVICE_ID_LD_HYBRID 0x2090
#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0
#define USB_VENDOR_ID_LENOVO 0x17ef
#define USB_DEVICE_ID_LENOVO_TPKBD 0x6009
#define USB_VENDOR_ID_LG 0x1fd2
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
@ -573,6 +581,9 @@
#define USB_VENDOR_ID_NINTENDO 0x057e
#define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306
#define USB_VENDOR_ID_NOVATEK 0x0603
#define USB_DEVICE_ID_NOVATEK_PCT 0x0600
#define USB_VENDOR_ID_NTRIG 0x1b96
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1 0x0003
@ -650,6 +661,7 @@
#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6
#define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a
#define USB_VENDOR_ID_SAITEK 0x06a3
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17

View 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;

View File

@ -0,0 +1,564 @@
/*
* HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint
*
* Copyright (c) 2012 Bernhard Seibold
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#include <linux/module.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/usb.h>
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/leds.h>
#include "usbhid/usbhid.h"
#include "hid-ids.h"
/* This is only used for the trackpoint part of the driver, hence _tp */
struct tpkbd_data_pointer {
int led_state;
struct led_classdev led_mute;
struct led_classdev led_micmute;
int press_to_select;
int dragging;
int release_to_select;
int select_right;
int sensitivity;
int press_speed;
};
#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
static int tpkbd_input_mapping(struct hid_device *hdev,
struct hid_input *hi, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max)
{
struct usbhid_device *uhdev;
uhdev = (struct usbhid_device *) hdev->driver_data;
if (uhdev->ifnum == 1 && usage->hid == (HID_UP_BUTTON | 0x0010)) {
map_key_clear(KEY_MICMUTE);
return 1;
}
return 0;
}
#undef map_key_clear
static int tpkbd_features_set(struct hid_device *hdev)
{
struct hid_report *report;
struct tpkbd_data_pointer *data_pointer;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02;
report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08;
report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20;
report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40;
report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver
report->field[2]->value[0] = data_pointer->sensitivity;
report->field[3]->value[0] = data_pointer->press_speed;
usbhid_submit_report(hdev, report, USB_DIR_OUT);
return 0;
}
static ssize_t pointer_press_to_select_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
}
static ssize_t pointer_press_to_select_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
return -EINVAL;
data_pointer->press_to_select = value;
tpkbd_features_set(hdev);
return count;
}
static ssize_t pointer_dragging_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
}
static ssize_t pointer_dragging_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
return -EINVAL;
data_pointer->dragging = value;
tpkbd_features_set(hdev);
return count;
}
static ssize_t pointer_release_to_select_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
}
static ssize_t pointer_release_to_select_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
return -EINVAL;
data_pointer->release_to_select = value;
tpkbd_features_set(hdev);
return count;
}
static ssize_t pointer_select_right_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
}
static ssize_t pointer_select_right_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
return -EINVAL;
data_pointer->select_right = value;
tpkbd_features_set(hdev);
return count;
}
static ssize_t pointer_sensitivity_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n",
data_pointer->sensitivity);
}
static ssize_t pointer_sensitivity_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
return -EINVAL;
data_pointer->sensitivity = value;
tpkbd_features_set(hdev);
return count;
}
static ssize_t pointer_press_speed_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n",
data_pointer->press_speed);
}
static ssize_t pointer_press_speed_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
int value;
hdev = container_of(dev, struct hid_device, dev);
if (hdev == NULL)
return -ENODEV;
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
return -EINVAL;
data_pointer->press_speed = value;
tpkbd_features_set(hdev);
return count;
}
static struct device_attribute dev_attr_pointer_press_to_select =
__ATTR(press_to_select, S_IWUSR | S_IRUGO,
pointer_press_to_select_show,
pointer_press_to_select_store);
static struct device_attribute dev_attr_pointer_dragging =
__ATTR(dragging, S_IWUSR | S_IRUGO,
pointer_dragging_show,
pointer_dragging_store);
static struct device_attribute dev_attr_pointer_release_to_select =
__ATTR(release_to_select, S_IWUSR | S_IRUGO,
pointer_release_to_select_show,
pointer_release_to_select_store);
static struct device_attribute dev_attr_pointer_select_right =
__ATTR(select_right, S_IWUSR | S_IRUGO,
pointer_select_right_show,
pointer_select_right_store);
static struct device_attribute dev_attr_pointer_sensitivity =
__ATTR(sensitivity, S_IWUSR | S_IRUGO,
pointer_sensitivity_show,
pointer_sensitivity_store);
static struct device_attribute dev_attr_pointer_press_speed =
__ATTR(press_speed, S_IWUSR | S_IRUGO,
pointer_press_speed_show,
pointer_press_speed_store);
static struct attribute *tpkbd_attributes_pointer[] = {
&dev_attr_pointer_press_to_select.attr,
&dev_attr_pointer_dragging.attr,
&dev_attr_pointer_release_to_select.attr,
&dev_attr_pointer_select_right.attr,
&dev_attr_pointer_sensitivity.attr,
&dev_attr_pointer_press_speed.attr,
NULL
};
static const struct attribute_group tpkbd_attr_group_pointer = {
.attrs = tpkbd_attributes_pointer,
};
static enum led_brightness tpkbd_led_brightness_get(
struct led_classdev *led_cdev)
{
struct device *dev;
struct hid_device *hdev;
struct tpkbd_data_pointer *data_pointer;
int led_nr = 0;
dev = led_cdev->dev->parent;
hdev = container_of(dev, struct hid_device, dev);
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (led_cdev == &data_pointer->led_micmute)
led_nr = 1;
return data_pointer->led_state & (1 << led_nr)
? LED_FULL
: LED_OFF;
}
static void tpkbd_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct device *dev;
struct hid_device *hdev;
struct hid_report *report;
struct tpkbd_data_pointer *data_pointer;
int led_nr = 0;
dev = led_cdev->dev->parent;
hdev = container_of(dev, struct hid_device, dev);
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
if (led_cdev == &data_pointer->led_micmute)
led_nr = 1;
if (value == LED_OFF)
data_pointer->led_state &= ~(1 << led_nr);
else
data_pointer->led_state |= 1 << led_nr;
report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
usbhid_submit_report(hdev, report, USB_DIR_OUT);
}
static int tpkbd_probe_tp(struct hid_device *hdev)
{
struct device *dev = &hdev->dev;
struct tpkbd_data_pointer *data_pointer;
size_t name_sz = strlen(dev_name(dev)) + 16;
char *name_mute, *name_micmute;
int ret;
if (sysfs_create_group(&hdev->dev.kobj,
&tpkbd_attr_group_pointer)) {
hid_warn(hdev, "Could not create sysfs group\n");
}
data_pointer = kzalloc(sizeof(struct tpkbd_data_pointer), GFP_KERNEL);
if (data_pointer == NULL) {
hid_err(hdev, "Could not allocate memory for driver data\n");
return -ENOMEM;
}
// set same default values as windows driver
data_pointer->sensitivity = 0xa0;
data_pointer->press_speed = 0x38;
name_mute = kzalloc(name_sz, GFP_KERNEL);
if (name_mute == NULL) {
hid_err(hdev, "Could not allocate memory for led data\n");
ret = -ENOMEM;
goto err;
}
snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
name_micmute = kzalloc(name_sz, GFP_KERNEL);
if (name_micmute == NULL) {
hid_err(hdev, "Could not allocate memory for led data\n");
ret = -ENOMEM;
goto err2;
}
snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
hid_set_drvdata(hdev, data_pointer);
data_pointer->led_mute.name = name_mute;
data_pointer->led_mute.brightness_get = tpkbd_led_brightness_get;
data_pointer->led_mute.brightness_set = tpkbd_led_brightness_set;
data_pointer->led_mute.dev = dev;
led_classdev_register(dev, &data_pointer->led_mute);
data_pointer->led_micmute.name = name_micmute;
data_pointer->led_micmute.brightness_get = tpkbd_led_brightness_get;
data_pointer->led_micmute.brightness_set = tpkbd_led_brightness_set;
data_pointer->led_micmute.dev = dev;
led_classdev_register(dev, &data_pointer->led_micmute);
tpkbd_features_set(hdev);
return 0;
err2:
kfree(name_mute);
err:
kfree(data_pointer);
return ret;
}
static int tpkbd_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int ret;
struct usbhid_device *uhdev;
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "hid_parse failed\n");
goto err_free;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "hid_hw_start failed\n");
goto err_free;
}
uhdev = (struct usbhid_device *) hdev->driver_data;
if (uhdev->ifnum == 1)
return tpkbd_probe_tp(hdev);
return 0;
err_free:
return ret;
}
static void tpkbd_remove_tp(struct hid_device *hdev)
{
struct tpkbd_data_pointer *data_pointer;
sysfs_remove_group(&hdev->dev.kobj,
&tpkbd_attr_group_pointer);
data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
led_classdev_unregister(&data_pointer->led_micmute);
led_classdev_unregister(&data_pointer->led_mute);
hid_set_drvdata(hdev, NULL);
kfree(data_pointer);
}
static void tpkbd_remove(struct hid_device *hdev)
{
struct usbhid_device *uhdev;
uhdev = (struct usbhid_device *) hdev->driver_data;
if (uhdev->ifnum == 1)
tpkbd_remove_tp(hdev);
hid_hw_stop(hdev);
}
static const struct hid_device_id tpkbd_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
{ }
};
MODULE_DEVICE_TABLE(hid, tpkbd_devices);
static struct hid_driver tpkbd_driver = {
.name = "lenovo_tpkbd",
.id_table = tpkbd_devices,
.input_mapping = tpkbd_input_mapping,
.probe = tpkbd_probe,
.remove = tpkbd_remove,
};
static int __init tpkbd_init(void)
{
return hid_register_driver(&tpkbd_driver);
}
static void __exit tpkbd_exit(void)
{
hid_unregister_driver(&tpkbd_driver);
}
module_init(tpkbd_init);
module_exit(tpkbd_exit);
MODULE_LICENSE("GPL");

View 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);
__set_bit(EV_ABS, input->evbit);
/* Note: Touch Y position from the device is inverted relative
* to how pointer motion is reported (and relative to how USB
* HID recommends the coordinates work). This driver keeps
* the origin at the same position, and just uses the additive
* inverse of the reported Y.
*/
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
input_set_abs_params(input, ABS_MT_POSITION_X,
MOUSE_MIN_X, MOUSE_MAX_X, 4, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
MOUSE_MIN_Y, MOUSE_MAX_Y, 4, 0);
error = input_mt_init_slots(input, 16);
if (error)
return error;
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
4, 0);
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2,
4, 0);
input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
input_abs_set_res(input, ABS_MT_POSITION_X,
MOUSE_RES_X);
input_abs_set_res(input, ABS_MT_POSITION_Y,
MOUSE_RES_Y);
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X,
TRACKPAD_MAX_X, 4, 0);
input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y,
TRACKPAD_MAX_Y, 4, 0);
input_set_abs_params(input, ABS_MT_POSITION_X,
TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
TRACKPAD_MIN_Y, TRACKPAD_MAX_Y, 4, 0);
/* Note: Touch Y position from the device is inverted relative
* to how pointer motion is reported (and relative to how USB
* HID recommends the coordinates work). This driver keeps
* the origin at the same position, and just uses the additive
* inverse of the reported Y.
*/
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
input_set_abs_params(input, ABS_MT_POSITION_X,
MOUSE_MIN_X, MOUSE_MAX_X, 4, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
MOUSE_MIN_Y, MOUSE_MAX_Y, 4, 0);
input_abs_set_res(input, ABS_X, TRACKPAD_RES_X);
input_abs_set_res(input, ABS_Y, TRACKPAD_RES_Y);
input_abs_set_res(input, ABS_MT_POSITION_X,
TRACKPAD_RES_X);
input_abs_set_res(input, ABS_MT_POSITION_Y,
TRACKPAD_RES_Y);
}
input_abs_set_res(input, ABS_MT_POSITION_X,
MOUSE_RES_X);
input_abs_set_res(input, ABS_MT_POSITION_Y,
MOUSE_RES_Y);
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X,
TRACKPAD_MAX_X, 4, 0);
input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y,
TRACKPAD_MAX_Y, 4, 0);
input_set_abs_params(input, ABS_MT_POSITION_X,
TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
TRACKPAD_MIN_Y, TRACKPAD_MAX_Y, 4, 0);
input_set_events_per_packet(input, 60);
input_abs_set_res(input, ABS_X, TRACKPAD_RES_X);
input_abs_set_res(input, ABS_Y, TRACKPAD_RES_Y);
input_abs_set_res(input, ABS_MT_POSITION_X,
TRACKPAD_RES_X);
input_abs_set_res(input, ABS_MT_POSITION_Y,
TRACKPAD_RES_Y);
}
input_set_events_per_packet(input, 60);
if (report_undeciphered) {
__set_bit(EV_MSC, input->evbit);
__set_bit(MSC_RAW, input->mscbit);
}
return 0;
}
static int magicmouse_input_mapping(struct hid_device *hdev,
@ -511,8 +481,6 @@ static int magicmouse_probe(struct hid_device *hdev,
msc->quirks = id->driver_data;
hid_set_drvdata(hdev, msc);
msc->single_touch_id = NO_TOUCHES;
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "magicmouse hid parse failed\n");
@ -528,8 +496,13 @@ static int magicmouse_probe(struct hid_device *hdev,
/* We do this after hid-input is done parsing reports so that
* hid-input uses the most natural button and axis IDs.
*/
if (msc->input)
magicmouse_setup_input(msc->input, hdev);
if (msc->input) {
ret = magicmouse_setup_input(msc->input, hdev);
if (ret) {
hid_err(hdev, "magicmouse setup input failed (%d)\n", ret);
goto err_stop_hw;
}
}
if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
report = hid_register_report(hdev, HID_INPUT_REPORT,

View 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,

View 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;

View 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);

View File

@ -16,12 +16,12 @@
#include <linux/module.h>
#include "hid-roccat-common.h"
static inline uint16_t roccat_common_feature_report(uint8_t report_id)
static inline uint16_t roccat_common2_feature_report(uint8_t report_id)
{
return 0x300 | report_id;
}
int roccat_common_receive(struct usb_device *usb_dev, uint report_id,
int roccat_common2_receive(struct usb_device *usb_dev, uint report_id,
void *data, uint size)
{
char *buf;
@ -34,16 +34,16 @@ int roccat_common_receive(struct usb_device *usb_dev, uint report_id,
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
HID_REQ_GET_REPORT,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
roccat_common_feature_report(report_id),
roccat_common2_feature_report(report_id),
0, buf, size, USB_CTRL_SET_TIMEOUT);
memcpy(data, buf, size);
kfree(buf);
return ((len < 0) ? len : ((len != size) ? -EIO : 0));
}
EXPORT_SYMBOL_GPL(roccat_common_receive);
EXPORT_SYMBOL_GPL(roccat_common2_receive);
int roccat_common_send(struct usb_device *usb_dev, uint report_id,
int roccat_common2_send(struct usb_device *usb_dev, uint report_id,
void const *data, uint size)
{
char *buf;
@ -56,13 +56,71 @@ int roccat_common_send(struct usb_device *usb_dev, uint report_id,
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
HID_REQ_SET_REPORT,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
roccat_common_feature_report(report_id),
roccat_common2_feature_report(report_id),
0, buf, size, USB_CTRL_SET_TIMEOUT);
kfree(buf);
return ((len < 0) ? len : ((len != size) ? -EIO : 0));
}
EXPORT_SYMBOL_GPL(roccat_common_send);
EXPORT_SYMBOL_GPL(roccat_common2_send);
enum roccat_common2_control_states {
ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD = 0,
ROCCAT_COMMON_CONTROL_STATUS_OK = 1,
ROCCAT_COMMON_CONTROL_STATUS_INVALID = 2,
ROCCAT_COMMON_CONTROL_STATUS_WAIT = 3,
};
static int roccat_common2_receive_control_status(struct usb_device *usb_dev)
{
int retval;
struct roccat_common2_control control;
do {
msleep(50);
retval = roccat_common2_receive(usb_dev,
ROCCAT_COMMON_COMMAND_CONTROL,
&control, sizeof(struct roccat_common2_control));
if (retval)
return retval;
switch (control.value) {
case ROCCAT_COMMON_CONTROL_STATUS_OK:
return 0;
case ROCCAT_COMMON_CONTROL_STATUS_WAIT:
msleep(500);
continue;
case ROCCAT_COMMON_CONTROL_STATUS_INVALID:
case ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD:
/* seems to be critical - replug necessary */
return -EINVAL;
default:
dev_err(&usb_dev->dev,
"roccat_common2_receive_control_status: "
"unknown response value 0x%x\n",
control.value);
return -EINVAL;
}
} while (1);
}
int roccat_common2_send_with_status(struct usb_device *usb_dev,
uint command, void const *buf, uint size)
{
int retval;
retval = roccat_common2_send(usb_dev, command, buf, size);
if (retval)
return retval;
msleep(100);
return roccat_common2_receive_control_status(usb_dev);
}
EXPORT_SYMBOL_GPL(roccat_common2_send_with_status);
MODULE_AUTHOR("Stefan Achatz");
MODULE_DESCRIPTION("USB Roccat common driver");

View File

@ -15,9 +15,21 @@
#include <linux/usb.h>
#include <linux/types.h>
int roccat_common_receive(struct usb_device *usb_dev, uint report_id,
enum roccat_common2_commands {
ROCCAT_COMMON_COMMAND_CONTROL = 0x4,
};
struct roccat_common2_control {
uint8_t command;
uint8_t value;
uint8_t request; /* always 0 on requesting write check */
} __packed;
int roccat_common2_receive(struct usb_device *usb_dev, uint report_id,
void *data, uint size);
int roccat_common_send(struct usb_device *usb_dev, uint report_id,
int roccat_common2_send(struct usb_device *usb_dev, uint report_id,
void const *data, uint size);
int roccat_common2_send_with_status(struct usb_device *usb_dev,
uint command, void const *buf, uint size);
#endif

View 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;

View 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 */

View 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;

View 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)

View 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,

View 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));
}

View File

@ -14,27 +14,13 @@
#include <linux/types.h>
struct kovaplus_control {
uint8_t command; /* KOVAPLUS_COMMAND_CONTROL */
uint8_t value;
uint8_t request;
} __packed;
enum kovaplus_control_requests {
/* read after write; value = 1 */
KOVAPLUS_CONTROL_REQUEST_STATUS = 0x0,
/* write; value = profile number range 0-4 */
KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
/* write; value = profile number range 0-4 */
KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20,
};
enum kovaplus_control_values {
KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD = 0, /* supposed */
KOVAPLUS_CONTROL_REQUEST_STATUS_OK = 1,
KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT = 3, /* supposed */
};
struct kovaplus_actual_profile {
uint8_t command; /* KOVAPLUS_COMMAND_ACTUAL_PROFILE */
uint8_t size; /* always 3 */
@ -75,7 +61,6 @@ struct kovaplus_a {
} __packed;
enum kovaplus_commands {
KOVAPLUS_COMMAND_CONTROL = 0x4,
KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5,
KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6,
KOVAPLUS_COMMAND_PROFILE_BUTTONS = 0x7,

View 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));
}

View 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,

View File

@ -0,0 +1,316 @@
/*
* Roccat Savu driver for Linux
*
* Copyright (c) 2012 Stefan Achatz <erazor_de@users.sourceforge.net>
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
/* Roccat Savu is a gamer mouse with macro keys that can be configured in
* 5 profiles.
*/
#include <linux/device.h>
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/hid-roccat.h>
#include "hid-ids.h"
#include "hid-roccat-common.h"
#include "hid-roccat-savu.h"
static struct class *savu_class;
static ssize_t savu_sysfs_read(struct file *fp, struct kobject *kobj,
char *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
if (off >= real_size)
return 0;
if (off != 0 || count != real_size)
return -EINVAL;
mutex_lock(&savu->savu_lock);
retval = roccat_common2_receive(usb_dev, command, buf, real_size);
mutex_unlock(&savu->savu_lock);
return retval ? retval : real_size;
}
static ssize_t savu_sysfs_write(struct file *fp, struct kobject *kobj,
void const *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
if (off != 0 || count != real_size)
return -EINVAL;
mutex_lock(&savu->savu_lock);
retval = roccat_common2_send_with_status(usb_dev, command,
(void *)buf, real_size);
mutex_unlock(&savu->savu_lock);
return retval ? retval : real_size;
}
#define SAVU_SYSFS_W(thingy, THINGY) \
static ssize_t savu_sysfs_write_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
{ \
return savu_sysfs_write(fp, kobj, buf, off, count, \
SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \
}
#define SAVU_SYSFS_R(thingy, THINGY) \
static ssize_t savu_sysfs_read_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
{ \
return savu_sysfs_read(fp, kobj, buf, off, count, \
SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \
}
#define SAVU_SYSFS_RW(thingy, THINGY) \
SAVU_SYSFS_W(thingy, THINGY) \
SAVU_SYSFS_R(thingy, THINGY)
#define SAVU_BIN_ATTRIBUTE_RW(thingy, THINGY) \
{ \
.attr = { .name = #thingy, .mode = 0660 }, \
.size = SAVU_SIZE_ ## THINGY, \
.read = savu_sysfs_read_ ## thingy, \
.write = savu_sysfs_write_ ## thingy \
}
#define SAVU_BIN_ATTRIBUTE_R(thingy, THINGY) \
{ \
.attr = { .name = #thingy, .mode = 0440 }, \
.size = SAVU_SIZE_ ## THINGY, \
.read = savu_sysfs_read_ ## thingy, \
}
#define SAVU_BIN_ATTRIBUTE_W(thingy, THINGY) \
{ \
.attr = { .name = #thingy, .mode = 0220 }, \
.size = SAVU_SIZE_ ## THINGY, \
.write = savu_sysfs_write_ ## thingy \
}
SAVU_SYSFS_W(control, CONTROL)
SAVU_SYSFS_RW(profile, PROFILE)
SAVU_SYSFS_RW(general, GENERAL)
SAVU_SYSFS_RW(buttons, BUTTONS)
SAVU_SYSFS_RW(macro, MACRO)
SAVU_SYSFS_R(info, INFO)
SAVU_SYSFS_RW(sensor, SENSOR)
static struct bin_attribute savu_bin_attributes[] = {
SAVU_BIN_ATTRIBUTE_W(control, CONTROL),
SAVU_BIN_ATTRIBUTE_RW(profile, PROFILE),
SAVU_BIN_ATTRIBUTE_RW(general, GENERAL),
SAVU_BIN_ATTRIBUTE_RW(buttons, BUTTONS),
SAVU_BIN_ATTRIBUTE_RW(macro, MACRO),
SAVU_BIN_ATTRIBUTE_R(info, INFO),
SAVU_BIN_ATTRIBUTE_RW(sensor, SENSOR),
__ATTR_NULL
};
static int savu_init_savu_device_struct(struct usb_device *usb_dev,
struct savu_device *savu)
{
mutex_init(&savu->savu_lock);
return 0;
}
static int savu_init_specials(struct hid_device *hdev)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct usb_device *usb_dev = interface_to_usbdev(intf);
struct savu_device *savu;
int retval;
if (intf->cur_altsetting->desc.bInterfaceProtocol
!= USB_INTERFACE_PROTOCOL_MOUSE) {
hid_set_drvdata(hdev, NULL);
return 0;
}
savu = kzalloc(sizeof(*savu), GFP_KERNEL);
if (!savu) {
hid_err(hdev, "can't alloc device descriptor\n");
return -ENOMEM;
}
hid_set_drvdata(hdev, savu);
retval = savu_init_savu_device_struct(usb_dev, savu);
if (retval) {
hid_err(hdev, "couldn't init struct savu_device\n");
goto exit_free;
}
retval = roccat_connect(savu_class, hdev,
sizeof(struct savu_roccat_report));
if (retval < 0) {
hid_err(hdev, "couldn't init char dev\n");
} else {
savu->chrdev_minor = retval;
savu->roccat_claimed = 1;
}
return 0;
exit_free:
kfree(savu);
return retval;
}
static void savu_remove_specials(struct hid_device *hdev)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct savu_device *savu;
if (intf->cur_altsetting->desc.bInterfaceProtocol
!= USB_INTERFACE_PROTOCOL_MOUSE)
return;
savu = hid_get_drvdata(hdev);
if (savu->roccat_claimed)
roccat_disconnect(savu->chrdev_minor);
kfree(savu);
}
static int savu_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int retval;
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
goto exit;
}
retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (retval) {
hid_err(hdev, "hw start failed\n");
goto exit;
}
retval = savu_init_specials(hdev);
if (retval) {
hid_err(hdev, "couldn't install mouse\n");
goto exit_stop;
}
return 0;
exit_stop:
hid_hw_stop(hdev);
exit:
return retval;
}
static void savu_remove(struct hid_device *hdev)
{
savu_remove_specials(hdev);
hid_hw_stop(hdev);
}
static void savu_report_to_chrdev(struct savu_device const *savu,
u8 const *data)
{
struct savu_roccat_report roccat_report;
struct savu_mouse_report_special const *special_report;
if (data[0] != SAVU_MOUSE_REPORT_NUMBER_SPECIAL)
return;
special_report = (struct savu_mouse_report_special const *)data;
roccat_report.type = special_report->type;
roccat_report.data[0] = special_report->data[0];
roccat_report.data[1] = special_report->data[1];
roccat_report_event(savu->chrdev_minor,
(uint8_t const *)&roccat_report);
}
static int savu_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *data, int size)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct savu_device *savu = hid_get_drvdata(hdev);
if (intf->cur_altsetting->desc.bInterfaceProtocol
!= USB_INTERFACE_PROTOCOL_MOUSE)
return 0;
if (savu == NULL)
return 0;
if (savu->roccat_claimed)
savu_report_to_chrdev(savu, data);
return 0;
}
static const struct hid_device_id savu_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
{ }
};
MODULE_DEVICE_TABLE(hid, savu_devices);
static struct hid_driver savu_driver = {
.name = "savu",
.id_table = savu_devices,
.probe = savu_probe,
.remove = savu_remove,
.raw_event = savu_raw_event
};
static int __init savu_init(void)
{
int retval;
savu_class = class_create(THIS_MODULE, "savu");
if (IS_ERR(savu_class))
return PTR_ERR(savu_class);
savu_class->dev_bin_attrs = savu_bin_attributes;
retval = hid_register_driver(&savu_driver);
if (retval)
class_destroy(savu_class);
return retval;
}
static void __exit savu_exit(void)
{
hid_unregister_driver(&savu_driver);
class_destroy(savu_class);
}
module_init(savu_init);
module_exit(savu_exit);
MODULE_AUTHOR("Stefan Achatz");
MODULE_DESCRIPTION("USB Roccat Savu driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,87 @@
#ifndef __HID_ROCCAT_SAVU_H
#define __HID_ROCCAT_SAVU_H
/*
* Copyright (c) 2012 Stefan Achatz <erazor_de@users.sourceforge.net>
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#include <linux/types.h>
enum {
SAVU_SIZE_CONTROL = 0x03,
SAVU_SIZE_PROFILE = 0x03,
SAVU_SIZE_GENERAL = 0x10,
SAVU_SIZE_BUTTONS = 0x2f,
SAVU_SIZE_MACRO = 0x0823,
SAVU_SIZE_INFO = 0x08,
SAVU_SIZE_SENSOR = 0x04,
};
enum savu_control_requests {
SAVU_CONTROL_REQUEST_GENERAL = 0x80,
SAVU_CONTROL_REQUEST_BUTTONS = 0x90,
};
enum savu_commands {
SAVU_COMMAND_CONTROL = 0x4,
SAVU_COMMAND_PROFILE = 0x5,
SAVU_COMMAND_GENERAL = 0x6,
SAVU_COMMAND_BUTTONS = 0x7,
SAVU_COMMAND_MACRO = 0x8,
SAVU_COMMAND_INFO = 0x9,
SAVU_COMMAND_SENSOR = 0xc,
};
struct savu_mouse_report_special {
uint8_t report_number; /* always 3 */
uint8_t zero;
uint8_t type;
uint8_t data[2];
} __packed;
enum {
SAVU_MOUSE_REPORT_NUMBER_SPECIAL = 3,
};
enum savu_mouse_report_button_types {
/* data1 = new profile range 1-5 */
SAVU_MOUSE_REPORT_BUTTON_TYPE_PROFILE = 0x20,
/* data1 = button number range 1-24; data2 = action */
SAVU_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH = 0x60,
/* data1 = button number range 1-24; data2 = action */
SAVU_MOUSE_REPORT_BUTTON_TYPE_TIMER = 0x80,
/* data1 = setting number range 1-5 */
SAVU_MOUSE_REPORT_BUTTON_TYPE_CPI = 0xb0,
/* data1 and data2 = range 0x1-0xb */
SAVU_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY = 0xc0,
/* data1 = 22 = next track...
* data2 = action
*/
SAVU_MOUSE_REPORT_BUTTON_TYPE_MULTIMEDIA = 0xf0,
};
struct savu_roccat_report {
uint8_t type;
uint8_t data[2];
} __packed;
struct savu_device {
int roccat_claimed;
int chrdev_minor;
struct mutex savu_lock;
};
#endif

View 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;

View 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);

View 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

View 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