mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-13 23:24:05 +08:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (68 commits) hid-input/battery: add FEATURE quirk hid-input/battery: remove battery_val hid-input/battery: power-supply type really *is* a battery hid-input/battery: make the battery setup common for INPUTs and FEATUREs hid-input/battery: deal with both FEATURE and INPUT report batteries hid-input/battery: add quirks for battery hid-input/battery: remove apparently redundant kmalloc hid-input: add support for HID devices reporting Battery Strength HID: hid-multitouch: add support 9 new Xiroku devices HID: multitouch: add support for 3M 32" HID: multitouch: add support of Atmel multitouch panels HID: usbhid: defer LED setting to a workqueue HID: usbhid: hid-core: submit queued urbs before suspend HID: usbhid: remove LED_ON HID: emsff: use symbolic name instead of hardcoded PID constant HID: Enable HID_QUIRK_MULTI_INPUT for Trio Linker Plus II HID: Kconfig: fix syntax HID: introduce proper dependency of HID_BATTERY on POWER_SUPPLY HID: multitouch: support PixArt optical touch screen HID: make parser more verbose about parsing errors by default ... Fix up rename/delete conflict in drivers/hid/hid-hyperv.c (removed in staging, moved in this branch) and similarly for the rules for same file in drivers/staging/hv/{Kconfig,Makefile}.
This commit is contained in:
commit
f62f61917d
9
Documentation/ABI/testing/sysfs-driver-hid-multitouch
Normal file
9
Documentation/ABI/testing/sysfs-driver-hid-multitouch
Normal file
@ -0,0 +1,9 @@
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/quirks
|
||||
Date: November 2011
|
||||
Contact: Benjamin Tissoires <benjamin.tissoires@gmail.com>
|
||||
Description: The integer value of this attribute corresponds to the
|
||||
quirks actually in place to handle the device's protocol.
|
||||
When read, this attribute returns the current settings (see
|
||||
MT_QUIRKS_* in hid-multitouch.c).
|
||||
When written this attribute change on the fly the quirks, then
|
||||
the protocol to handle the device.
|
135
Documentation/ABI/testing/sysfs-driver-hid-roccat-isku
Normal file
135
Documentation/ABI/testing/sysfs-driver-hid-roccat-isku
Normal file
@ -0,0 +1,135 @@
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/actual_profile
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The integer value of this attribute ranges from 0-4.
|
||||
When read, this attribute returns the number of the actual
|
||||
profile. This value is persistent, so its equivalent to the
|
||||
profile that's active when the device is powered on next time.
|
||||
When written, this file sets the number of the startup profile
|
||||
and the device activates this profile immediately.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/info
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When read, this file returns general data like firmware version.
|
||||
The data is 6 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>/isku/roccatisku<minor>/key_mask
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one deactivate certain keys like
|
||||
windows and application keys, to prevent accidental presses.
|
||||
Profile number for which this settings occur is included in
|
||||
written data. The data has to be 6 bytes long.
|
||||
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>/isku/roccatisku<minor>/keys_capslock
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one set the function of the
|
||||
capslock key for a specific profile. Profile number is included
|
||||
in written data. The data has to be 6 bytes long.
|
||||
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>/isku/roccatisku<minor>/keys_easyzone
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one set the function of the
|
||||
easyzone keys for a specific profile. Profile number is included
|
||||
in written data. The data has to be 65 bytes long.
|
||||
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>/isku/roccatisku<minor>/keys_function
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one set the function of the
|
||||
function keys for a specific profile. Profile number is included
|
||||
in written data. The data has to be 41 bytes long.
|
||||
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>/isku/roccatisku<minor>/keys_macro
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one set the function of the macro
|
||||
keys for a specific profile. Profile number is included in
|
||||
written data. The data has to be 35 bytes long.
|
||||
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>/isku/roccatisku<minor>/keys_media
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one set the function of the media
|
||||
keys for a specific profile. Profile number is included in
|
||||
written data. The data has to be 29 bytes long.
|
||||
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>/isku/roccatisku<minor>/keys_thumbster
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one set the function of the
|
||||
thumbster keys for a specific profile. Profile number is included
|
||||
in written data. The data has to be 23 bytes long.
|
||||
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>/isku/roccatisku<minor>/last_set
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one set the time in secs since
|
||||
epoch in which the last configuration took place.
|
||||
The data has to be 20 bytes long.
|
||||
Users: http://roccat.sourceforge.net
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/light
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one set the backlight intensity for
|
||||
a specific profile. Profile number is included in written data.
|
||||
The data has to be 10 bytes long.
|
||||
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>/isku/roccatisku<minor>/macro
|
||||
Date: June 2011
|
||||
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>/isku/roccatisku<minor>/control
|
||||
Date: June 2011
|
||||
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>/isku/roccatisku<minor>/talk
|
||||
Date: June 2011
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When written, this file lets one trigger easyshift functionality
|
||||
from the host.
|
||||
The data has to be 16 bytes long.
|
||||
This file is writeonly.
|
||||
Users: http://roccat.sourceforge.net
|
@ -8,3 +8,15 @@ Contact: David Herrmann <dh.herrmann@googlemail.com>
|
||||
Description: Make it possible to set/get current led state. Reading from it
|
||||
returns 0 if led is off and 1 if it is on. Writing 0 to it
|
||||
disables the led, writing 1 enables it.
|
||||
|
||||
What: /sys/bus/hid/drivers/wiimote/<dev>/extension
|
||||
Date: August 2011
|
||||
KernelVersion: 3.2
|
||||
Contact: David Herrmann <dh.herrmann@googlemail.com>
|
||||
Description: This file contains the currently connected and initialized
|
||||
extensions. It can be one of: none, motionp, nunchuck, classic,
|
||||
motionp+nunchuck, motionp+classic
|
||||
motionp is the official Nintendo Motion+ extension, nunchuck is
|
||||
the official Nintendo Nunchuck extension and classic is the
|
||||
Nintendo Classic Controller extension. The motionp extension can
|
||||
be combined with the other two.
|
||||
|
@ -31,6 +31,11 @@ config HID
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config HID_BATTERY_STRENGTH
|
||||
bool
|
||||
depends on HID && POWER_SUPPLY && HID = POWER_SUPPLY
|
||||
default y
|
||||
|
||||
config HIDRAW
|
||||
bool "/dev/hidraw raw HID device support"
|
||||
depends on HID
|
||||
@ -335,6 +340,7 @@ config HID_MULTITOUCH
|
||||
Say Y here if you have one of the following devices:
|
||||
- 3M PCT touch screens
|
||||
- ActionStar dual touch panels
|
||||
- Atmel panels
|
||||
- Cando dual touch panels
|
||||
- Chunghwa panels
|
||||
- CVTouch panels
|
||||
@ -349,12 +355,15 @@ config HID_MULTITOUCH
|
||||
- Lumio CrystalTouch panels
|
||||
- MosArt dual-touch panels
|
||||
- PenMount dual touch panels
|
||||
- PixArt optical touch screen
|
||||
- Pixcir dual touch panels
|
||||
- Quanta panels
|
||||
- eGalax dual-touch panels, including the Joojoo and Wetab tablets
|
||||
- Stantum multitouch panels
|
||||
- Touch International Panels
|
||||
- Unitec Panels
|
||||
- XAT optical touch panels
|
||||
- Xiroku optical touch panels
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
@ -466,12 +475,6 @@ config HID_PRIMAX
|
||||
Support for Primax devices that are not fully compliant with the
|
||||
HID standard.
|
||||
|
||||
config HID_QUANTA
|
||||
tristate "Quanta Optical Touch panels"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Quanta Optical Touch dual-touch panels.
|
||||
|
||||
config HID_ROCCAT
|
||||
tristate "Roccat special event support"
|
||||
depends on USB_HID
|
||||
@ -492,6 +495,13 @@ config HID_ROCCAT_ARVO
|
||||
---help---
|
||||
Support for Roccat Arvo keyboard.
|
||||
|
||||
config HID_ROCCAT_ISKU
|
||||
tristate "Roccat Isku keyboard support"
|
||||
depends on USB_HID
|
||||
depends on HID_ROCCAT
|
||||
---help---
|
||||
Support for Roccat Isku keyboard.
|
||||
|
||||
config HID_ROCCAT_KONE
|
||||
tristate "Roccat Kone Mouse support"
|
||||
depends on USB_HID
|
||||
@ -560,6 +570,12 @@ config GREENASIA_FF
|
||||
(like MANTA Warrior MM816 and SpeedLink Strike2 SL-6635) or adapter
|
||||
and want to enable force feedback support for it.
|
||||
|
||||
config HID_HYPERV_MOUSE
|
||||
tristate "Microsoft Hyper-V mouse driver"
|
||||
depends on HYPERV
|
||||
---help---
|
||||
Select this option to enable the Hyper-V mouse driver.
|
||||
|
||||
config HID_SMARTJOYPLUS
|
||||
tristate "SmartJoy PLUS PS2/USB adapter support"
|
||||
depends on USB_HID
|
||||
@ -620,9 +636,19 @@ config HID_WIIMOTE
|
||||
depends on BT_HIDP
|
||||
depends on LEDS_CLASS
|
||||
select POWER_SUPPLY
|
||||
select INPUT_FF_MEMLESS
|
||||
---help---
|
||||
Support for the Nintendo Wii Remote bluetooth device.
|
||||
|
||||
config HID_WIIMOTE_EXT
|
||||
bool "Nintendo Wii Remote Extension support"
|
||||
depends on HID_WIIMOTE
|
||||
default HID_WIIMOTE
|
||||
---help---
|
||||
Support for extension controllers of the Nintendo Wii Remote. Say yes
|
||||
here if you want to use the Nintendo Motion+, Nunchuck or Classic
|
||||
extension controllers with your Wii Remote.
|
||||
|
||||
config HID_ZEROPLUS
|
||||
tristate "Zeroplus based game controller support"
|
||||
depends on USB_HID
|
||||
|
@ -25,6 +25,14 @@ ifdef CONFIG_LOGIWHEELS_FF
|
||||
hid-logitech-y += hid-lg4ff.o
|
||||
endif
|
||||
|
||||
hid-wiimote-y := hid-wiimote-core.o
|
||||
ifdef CONFIG_HID_WIIMOTE_EXT
|
||||
hid-wiimote-y += hid-wiimote-ext.o
|
||||
endif
|
||||
ifdef CONFIG_DEBUG_FS
|
||||
hid-wiimote-y += hid-wiimote-debug.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
|
||||
obj-$(CONFIG_HID_ACRUX) += hid-axff.o
|
||||
obj-$(CONFIG_HID_APPLE) += hid-apple.o
|
||||
@ -38,6 +46,7 @@ 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-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
|
||||
@ -51,7 +60,6 @@ obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
|
||||
obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o
|
||||
obj-$(CONFIG_HID_ORTEK) += hid-ortek.o
|
||||
obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
|
||||
obj-$(CONFIG_HID_QUANTA) += hid-quanta.o
|
||||
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
|
||||
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
|
||||
obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o
|
||||
@ -59,6 +67,7 @@ obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
|
||||
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o
|
||||
obj-$(CONFIG_HID_ROCCAT_COMMON) += hid-roccat-common.o
|
||||
obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o
|
||||
obj-$(CONFIG_HID_ROCCAT_ISKU) += hid-roccat-isku.o
|
||||
obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o
|
||||
obj-$(CONFIG_HID_ROCCAT_KONEPLUS) += hid-roccat-koneplus.o
|
||||
obj-$(CONFIG_HID_ROCCAT_KOVAPLUS) += hid-roccat-kovaplus.o
|
||||
|
@ -90,7 +90,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
|
||||
struct hid_field *field;
|
||||
|
||||
if (report->maxfield == HID_MAX_FIELDS) {
|
||||
dbg_hid("too many fields in report\n");
|
||||
hid_err(report->device, "too many fields in report\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -121,7 +121,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
|
||||
usage = parser->local.usage[0];
|
||||
|
||||
if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
|
||||
dbg_hid("collection stack overflow\n");
|
||||
hid_err(parser->device, "collection stack overflow\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
|
||||
collection = kmalloc(sizeof(struct hid_collection) *
|
||||
parser->device->collection_size * 2, GFP_KERNEL);
|
||||
if (collection == NULL) {
|
||||
dbg_hid("failed to reallocate collection array\n");
|
||||
hid_err(parser->device, "failed to reallocate collection array\n");
|
||||
return -1;
|
||||
}
|
||||
memcpy(collection, parser->device->collection,
|
||||
@ -165,7 +165,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
|
||||
static int close_collection(struct hid_parser *parser)
|
||||
{
|
||||
if (!parser->collection_stack_ptr) {
|
||||
dbg_hid("collection stack underflow\n");
|
||||
hid_err(parser->device, "collection stack underflow\n");
|
||||
return -1;
|
||||
}
|
||||
parser->collection_stack_ptr--;
|
||||
@ -197,7 +197,7 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
|
||||
static int hid_add_usage(struct hid_parser *parser, unsigned usage)
|
||||
{
|
||||
if (parser->local.usage_index >= HID_MAX_USAGES) {
|
||||
dbg_hid("usage index exceeded\n");
|
||||
hid_err(parser->device, "usage index exceeded\n");
|
||||
return -1;
|
||||
}
|
||||
parser->local.usage[parser->local.usage_index] = usage;
|
||||
@ -222,12 +222,13 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
|
||||
|
||||
report = hid_register_report(parser->device, report_type, parser->global.report_id);
|
||||
if (!report) {
|
||||
dbg_hid("hid_register_report failed\n");
|
||||
hid_err(parser->device, "hid_register_report failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (parser->global.logical_maximum < parser->global.logical_minimum) {
|
||||
dbg_hid("logical range invalid %d %d\n", parser->global.logical_minimum, parser->global.logical_maximum);
|
||||
hid_err(parser->device, "logical range invalid %d %d\n",
|
||||
parser->global.logical_minimum, parser->global.logical_maximum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -307,7 +308,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
|
||||
case HID_GLOBAL_ITEM_TAG_PUSH:
|
||||
|
||||
if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
|
||||
dbg_hid("global environment stack overflow\n");
|
||||
hid_err(parser->device, "global environment stack overflow\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -318,7 +319,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
|
||||
case HID_GLOBAL_ITEM_TAG_POP:
|
||||
|
||||
if (!parser->global_stack_ptr) {
|
||||
dbg_hid("global environment stack underflow\n");
|
||||
hid_err(parser->device, "global environment stack underflow\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -362,8 +363,8 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
|
||||
|
||||
case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
|
||||
parser->global.report_size = item_udata(item);
|
||||
if (parser->global.report_size > 32) {
|
||||
dbg_hid("invalid report_size %d\n",
|
||||
if (parser->global.report_size > 96) {
|
||||
hid_err(parser->device, "invalid report_size %d\n",
|
||||
parser->global.report_size);
|
||||
return -1;
|
||||
}
|
||||
@ -372,7 +373,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
|
||||
case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
|
||||
parser->global.report_count = item_udata(item);
|
||||
if (parser->global.report_count > HID_MAX_USAGES) {
|
||||
dbg_hid("invalid report_count %d\n",
|
||||
hid_err(parser->device, "invalid report_count %d\n",
|
||||
parser->global.report_count);
|
||||
return -1;
|
||||
}
|
||||
@ -381,13 +382,13 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
|
||||
case HID_GLOBAL_ITEM_TAG_REPORT_ID:
|
||||
parser->global.report_id = item_udata(item);
|
||||
if (parser->global.report_id == 0) {
|
||||
dbg_hid("report_id 0 is invalid\n");
|
||||
hid_err(parser->device, "report_id 0 is invalid\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
default:
|
||||
dbg_hid("unknown global tag 0x%x\n", item->tag);
|
||||
hid_err(parser->device, "unknown global tag 0x%x\n", item->tag);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -414,14 +415,14 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
|
||||
* items and the first delimiter set.
|
||||
*/
|
||||
if (parser->local.delimiter_depth != 0) {
|
||||
dbg_hid("nested delimiters\n");
|
||||
hid_err(parser->device, "nested delimiters\n");
|
||||
return -1;
|
||||
}
|
||||
parser->local.delimiter_depth++;
|
||||
parser->local.delimiter_branch++;
|
||||
} else {
|
||||
if (parser->local.delimiter_depth < 1) {
|
||||
dbg_hid("bogus close delimiter\n");
|
||||
hid_err(parser->device, "bogus close delimiter\n");
|
||||
return -1;
|
||||
}
|
||||
parser->local.delimiter_depth--;
|
||||
@ -506,7 +507,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
|
||||
ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
|
||||
break;
|
||||
default:
|
||||
dbg_hid("unknown main item tag 0x%x\n", item->tag);
|
||||
hid_err(parser->device, "unknown main item tag 0x%x\n", item->tag);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
@ -678,12 +679,12 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
|
||||
while ((start = fetch_item(start, end, &item)) != NULL) {
|
||||
|
||||
if (item.format != HID_ITEM_FORMAT_SHORT) {
|
||||
dbg_hid("unexpected long global item\n");
|
||||
hid_err(device, "unexpected long global item\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (dispatch_type[item.type](parser, &item)) {
|
||||
dbg_hid("item %u %u %u %u parsing failed\n",
|
||||
hid_err(device, "item %u %u %u %u parsing failed\n",
|
||||
item.format, (unsigned)item.size,
|
||||
(unsigned)item.type, (unsigned)item.tag);
|
||||
goto err;
|
||||
@ -691,11 +692,11 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
|
||||
|
||||
if (start == end) {
|
||||
if (parser->collection_stack_ptr) {
|
||||
dbg_hid("unbalanced collection at end of report description\n");
|
||||
hid_err(device, "unbalanced collection at end of report description\n");
|
||||
goto err;
|
||||
}
|
||||
if (parser->local.delimiter_depth) {
|
||||
dbg_hid("unbalanced delimiter at end of report description\n");
|
||||
hid_err(device, "unbalanced delimiter at end of report description\n");
|
||||
goto err;
|
||||
}
|
||||
vfree(parser);
|
||||
@ -703,7 +704,7 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
|
||||
}
|
||||
}
|
||||
|
||||
dbg_hid("item fetching failed at offset %d\n", (int)(end - start));
|
||||
hid_err(device, "item fetching failed at offset %d\n", (int)(end - start));
|
||||
err:
|
||||
vfree(parser);
|
||||
return ret;
|
||||
@ -873,7 +874,7 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field,
|
||||
ret = hdrv->event(hid, field, usage, value);
|
||||
if (ret != 0) {
|
||||
if (ret < 0)
|
||||
dbg_hid("%s's event failed with %d\n",
|
||||
hid_err(hid, "%s's event failed with %d\n",
|
||||
hdrv->name, ret);
|
||||
return;
|
||||
}
|
||||
@ -995,12 +996,13 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
|
||||
hid_dump_input(field->report->device, field->usage + offset, value);
|
||||
|
||||
if (offset >= field->report_count) {
|
||||
dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count);
|
||||
hid_err(field->report->device, "offset (%d) exceeds report_count (%d)\n",
|
||||
offset, field->report_count);
|
||||
return -1;
|
||||
}
|
||||
if (field->logical_minimum < 0) {
|
||||
if (value != snto32(s32ton(value, size), size)) {
|
||||
dbg_hid("value %d is out of range\n", value);
|
||||
hid_err(field->report->device, "value %d is out of range\n", value);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -1157,7 +1159,7 @@ static bool hid_match_one_id(struct hid_device *hdev,
|
||||
(id->product == HID_ANY_ID || id->product == hdev->product);
|
||||
}
|
||||
|
||||
static const struct hid_device_id *hid_match_id(struct hid_device *hdev,
|
||||
const struct hid_device_id *hid_match_id(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
for (; id->bus; id++)
|
||||
@ -1404,11 +1406,13 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2515) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) },
|
||||
@ -1423,6 +1427,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6650) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, USB_DEVICE_ID_ILITEK_MULTITOUCH) },
|
||||
@ -1498,11 +1503,15 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_PCI) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
|
||||
@ -1544,11 +1553,21 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX1) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX1) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR1) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
|
||||
@ -1768,6 +1787,7 @@ static const struct hid_device_id hid_ignore_list[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) },
|
||||
|
@ -114,6 +114,14 @@ static const struct hid_usage_entry hid_usage_table[] = {
|
||||
{0, 0xbd, "FlareRelease"},
|
||||
{0, 0xbe, "LandingGear"},
|
||||
{0, 0xbf, "ToeBrake"},
|
||||
{ 6, 0, "GenericDeviceControls" },
|
||||
{0, 0x20, "BatteryStrength" },
|
||||
{0, 0x21, "WirelessChannel" },
|
||||
{0, 0x22, "WirelessID" },
|
||||
{0, 0x23, "DiscoverWirelessControl" },
|
||||
{0, 0x24, "SecurityCodeCharacterEntered" },
|
||||
{0, 0x25, "SecurityCodeCharactedErased" },
|
||||
{0, 0x26, "SecurityCodeCleared" },
|
||||
{ 7, 0, "Keyboard" },
|
||||
{ 8, 0, "LED" },
|
||||
{0, 0x01, "NumLock"},
|
||||
|
@ -140,7 +140,7 @@ err:
|
||||
}
|
||||
|
||||
static const struct hid_device_id ems_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_EMS, 0x118) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, ems_devices);
|
||||
|
586
drivers/hid/hid-hyperv.c
Normal file
586
drivers/hid/hid-hyperv.c
Normal file
@ -0,0 +1,586 @@
|
||||
/*
|
||||
* Copyright (c) 2009, Citrix Systems, Inc.
|
||||
* Copyright (c) 2010, Microsoft Corporation.
|
||||
* Copyright (c) 2011, Novell Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hiddev.h>
|
||||
#include <linux/hyperv.h>
|
||||
|
||||
|
||||
struct hv_input_dev_info {
|
||||
unsigned int size;
|
||||
unsigned short vendor;
|
||||
unsigned short product;
|
||||
unsigned short version;
|
||||
unsigned short reserved[11];
|
||||
};
|
||||
|
||||
/* The maximum size of a synthetic input message. */
|
||||
#define SYNTHHID_MAX_INPUT_REPORT_SIZE 16
|
||||
|
||||
/*
|
||||
* Current version
|
||||
*
|
||||
* History:
|
||||
* Beta, RC < 2008/1/22 1,0
|
||||
* RC > 2008/1/22 2,0
|
||||
*/
|
||||
#define SYNTHHID_INPUT_VERSION_MAJOR 2
|
||||
#define SYNTHHID_INPUT_VERSION_MINOR 0
|
||||
#define SYNTHHID_INPUT_VERSION (SYNTHHID_INPUT_VERSION_MINOR | \
|
||||
(SYNTHHID_INPUT_VERSION_MAJOR << 16))
|
||||
|
||||
|
||||
#pragma pack(push, 1)
|
||||
/*
|
||||
* Message types in the synthetic input protocol
|
||||
*/
|
||||
enum synthhid_msg_type {
|
||||
SYNTH_HID_PROTOCOL_REQUEST,
|
||||
SYNTH_HID_PROTOCOL_RESPONSE,
|
||||
SYNTH_HID_INITIAL_DEVICE_INFO,
|
||||
SYNTH_HID_INITIAL_DEVICE_INFO_ACK,
|
||||
SYNTH_HID_INPUT_REPORT,
|
||||
SYNTH_HID_MAX
|
||||
};
|
||||
|
||||
/*
|
||||
* Basic message structures.
|
||||
*/
|
||||
struct synthhid_msg_hdr {
|
||||
enum synthhid_msg_type type;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct synthhid_msg {
|
||||
struct synthhid_msg_hdr header;
|
||||
char data[1]; /* Enclosed message */
|
||||
};
|
||||
|
||||
union synthhid_version {
|
||||
struct {
|
||||
u16 minor_version;
|
||||
u16 major_version;
|
||||
};
|
||||
u32 version;
|
||||
};
|
||||
|
||||
/*
|
||||
* Protocol messages
|
||||
*/
|
||||
struct synthhid_protocol_request {
|
||||
struct synthhid_msg_hdr header;
|
||||
union synthhid_version version_requested;
|
||||
};
|
||||
|
||||
struct synthhid_protocol_response {
|
||||
struct synthhid_msg_hdr header;
|
||||
union synthhid_version version_requested;
|
||||
unsigned char approved;
|
||||
};
|
||||
|
||||
struct synthhid_device_info {
|
||||
struct synthhid_msg_hdr header;
|
||||
struct hv_input_dev_info hid_dev_info;
|
||||
struct hid_descriptor hid_descriptor;
|
||||
};
|
||||
|
||||
struct synthhid_device_info_ack {
|
||||
struct synthhid_msg_hdr header;
|
||||
unsigned char reserved;
|
||||
};
|
||||
|
||||
struct synthhid_input_report {
|
||||
struct synthhid_msg_hdr header;
|
||||
char buffer[1];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define INPUTVSC_SEND_RING_BUFFER_SIZE (10*PAGE_SIZE)
|
||||
#define INPUTVSC_RECV_RING_BUFFER_SIZE (10*PAGE_SIZE)
|
||||
|
||||
|
||||
enum pipe_prot_msg_type {
|
||||
PIPE_MESSAGE_INVALID,
|
||||
PIPE_MESSAGE_DATA,
|
||||
PIPE_MESSAGE_MAXIMUM
|
||||
};
|
||||
|
||||
|
||||
struct pipe_prt_msg {
|
||||
enum pipe_prot_msg_type type;
|
||||
u32 size;
|
||||
char data[1];
|
||||
};
|
||||
|
||||
struct mousevsc_prt_msg {
|
||||
enum pipe_prot_msg_type type;
|
||||
u32 size;
|
||||
union {
|
||||
struct synthhid_protocol_request request;
|
||||
struct synthhid_protocol_response response;
|
||||
struct synthhid_device_info_ack ack;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Represents an mousevsc device
|
||||
*/
|
||||
struct mousevsc_dev {
|
||||
struct hv_device *device;
|
||||
bool init_complete;
|
||||
bool connected;
|
||||
struct mousevsc_prt_msg protocol_req;
|
||||
struct mousevsc_prt_msg protocol_resp;
|
||||
/* Synchronize the request/response if needed */
|
||||
struct completion wait_event;
|
||||
int dev_info_status;
|
||||
|
||||
struct hid_descriptor *hid_desc;
|
||||
unsigned char *report_desc;
|
||||
u32 report_desc_size;
|
||||
struct hv_input_dev_info hid_dev_info;
|
||||
struct hid_device *hid_device;
|
||||
};
|
||||
|
||||
|
||||
static struct mousevsc_dev *mousevsc_alloc_device(struct hv_device *device)
|
||||
{
|
||||
struct mousevsc_dev *input_dev;
|
||||
|
||||
input_dev = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL);
|
||||
|
||||
if (!input_dev)
|
||||
return NULL;
|
||||
|
||||
input_dev->device = device;
|
||||
hv_set_drvdata(device, input_dev);
|
||||
init_completion(&input_dev->wait_event);
|
||||
input_dev->init_complete = false;
|
||||
|
||||
return input_dev;
|
||||
}
|
||||
|
||||
static void mousevsc_free_device(struct mousevsc_dev *device)
|
||||
{
|
||||
kfree(device->hid_desc);
|
||||
kfree(device->report_desc);
|
||||
hv_set_drvdata(device->device, NULL);
|
||||
kfree(device);
|
||||
}
|
||||
|
||||
static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device,
|
||||
struct synthhid_device_info *device_info)
|
||||
{
|
||||
int ret = 0;
|
||||
struct hid_descriptor *desc;
|
||||
struct mousevsc_prt_msg ack;
|
||||
|
||||
input_device->dev_info_status = -ENOMEM;
|
||||
|
||||
input_device->hid_dev_info = device_info->hid_dev_info;
|
||||
desc = &device_info->hid_descriptor;
|
||||
if (desc->bLength == 0)
|
||||
goto cleanup;
|
||||
|
||||
input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC);
|
||||
|
||||
if (!input_device->hid_desc)
|
||||
goto cleanup;
|
||||
|
||||
memcpy(input_device->hid_desc, desc, desc->bLength);
|
||||
|
||||
input_device->report_desc_size = desc->desc[0].wDescriptorLength;
|
||||
if (input_device->report_desc_size == 0) {
|
||||
input_device->dev_info_status = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
input_device->report_desc = kzalloc(input_device->report_desc_size,
|
||||
GFP_ATOMIC);
|
||||
|
||||
if (!input_device->report_desc) {
|
||||
input_device->dev_info_status = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memcpy(input_device->report_desc,
|
||||
((unsigned char *)desc) + desc->bLength,
|
||||
desc->desc[0].wDescriptorLength);
|
||||
|
||||
/* Send the ack */
|
||||
memset(&ack, 0, sizeof(struct mousevsc_prt_msg));
|
||||
|
||||
ack.type = PIPE_MESSAGE_DATA;
|
||||
ack.size = sizeof(struct synthhid_device_info_ack);
|
||||
|
||||
ack.ack.header.type = SYNTH_HID_INITIAL_DEVICE_INFO_ACK;
|
||||
ack.ack.header.size = 1;
|
||||
ack.ack.reserved = 0;
|
||||
|
||||
ret = vmbus_sendpacket(input_device->device->channel,
|
||||
&ack,
|
||||
sizeof(struct pipe_prt_msg) - sizeof(unsigned char) +
|
||||
sizeof(struct synthhid_device_info_ack),
|
||||
(unsigned long)&ack,
|
||||
VM_PKT_DATA_INBAND,
|
||||
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
|
||||
|
||||
if (!ret)
|
||||
input_device->dev_info_status = 0;
|
||||
|
||||
cleanup:
|
||||
complete(&input_device->wait_event);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void mousevsc_on_receive(struct hv_device *device,
|
||||
struct vmpacket_descriptor *packet)
|
||||
{
|
||||
struct pipe_prt_msg *pipe_msg;
|
||||
struct synthhid_msg *hid_msg;
|
||||
struct mousevsc_dev *input_dev = hv_get_drvdata(device);
|
||||
struct synthhid_input_report *input_report;
|
||||
|
||||
pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet +
|
||||
(packet->offset8 << 3));
|
||||
|
||||
if (pipe_msg->type != PIPE_MESSAGE_DATA)
|
||||
return;
|
||||
|
||||
hid_msg = (struct synthhid_msg *)pipe_msg->data;
|
||||
|
||||
switch (hid_msg->header.type) {
|
||||
case SYNTH_HID_PROTOCOL_RESPONSE:
|
||||
/*
|
||||
* While it will be impossible for us to protect against
|
||||
* malicious/buggy hypervisor/host, add a check here to
|
||||
* ensure we don't corrupt memory.
|
||||
*/
|
||||
if ((pipe_msg->size + sizeof(struct pipe_prt_msg)
|
||||
- sizeof(unsigned char))
|
||||
> sizeof(struct mousevsc_prt_msg)) {
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(&input_dev->protocol_resp, pipe_msg,
|
||||
pipe_msg->size + sizeof(struct pipe_prt_msg) -
|
||||
sizeof(unsigned char));
|
||||
complete(&input_dev->wait_event);
|
||||
break;
|
||||
|
||||
case SYNTH_HID_INITIAL_DEVICE_INFO:
|
||||
WARN_ON(pipe_msg->size < sizeof(struct hv_input_dev_info));
|
||||
|
||||
/*
|
||||
* Parse out the device info into device attr,
|
||||
* hid desc and report desc
|
||||
*/
|
||||
mousevsc_on_receive_device_info(input_dev,
|
||||
(struct synthhid_device_info *)pipe_msg->data);
|
||||
break;
|
||||
case SYNTH_HID_INPUT_REPORT:
|
||||
input_report =
|
||||
(struct synthhid_input_report *)pipe_msg->data;
|
||||
if (!input_dev->init_complete)
|
||||
break;
|
||||
hid_input_report(input_dev->hid_device,
|
||||
HID_INPUT_REPORT, input_report->buffer,
|
||||
input_report->header.size, 1);
|
||||
break;
|
||||
default:
|
||||
pr_err("unsupported hid msg type - type %d len %d",
|
||||
hid_msg->header.type, hid_msg->header.size);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void mousevsc_on_channel_callback(void *context)
|
||||
{
|
||||
const int packet_size = 0x100;
|
||||
int ret;
|
||||
struct hv_device *device = context;
|
||||
u32 bytes_recvd;
|
||||
u64 req_id;
|
||||
struct vmpacket_descriptor *desc;
|
||||
unsigned char *buffer;
|
||||
int bufferlen = packet_size;
|
||||
|
||||
buffer = kmalloc(bufferlen, GFP_ATOMIC);
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
do {
|
||||
ret = vmbus_recvpacket_raw(device->channel, buffer,
|
||||
bufferlen, &bytes_recvd, &req_id);
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
if (bytes_recvd <= 0) {
|
||||
kfree(buffer);
|
||||
return;
|
||||
}
|
||||
desc = (struct vmpacket_descriptor *)buffer;
|
||||
|
||||
switch (desc->type) {
|
||||
case VM_PKT_COMP:
|
||||
break;
|
||||
|
||||
case VM_PKT_DATA_INBAND:
|
||||
mousevsc_on_receive(device, desc);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("unhandled packet type %d, tid %llx len %d\n",
|
||||
desc->type, req_id, bytes_recvd);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case -ENOBUFS:
|
||||
kfree(buffer);
|
||||
/* Handle large packet */
|
||||
bufferlen = bytes_recvd;
|
||||
buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
|
||||
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
}
|
||||
|
||||
static int mousevsc_connect_to_vsp(struct hv_device *device)
|
||||
{
|
||||
int ret = 0;
|
||||
int t;
|
||||
struct mousevsc_dev *input_dev = hv_get_drvdata(device);
|
||||
struct mousevsc_prt_msg *request;
|
||||
struct mousevsc_prt_msg *response;
|
||||
|
||||
request = &input_dev->protocol_req;
|
||||
memset(request, 0, sizeof(struct mousevsc_prt_msg));
|
||||
|
||||
request->type = PIPE_MESSAGE_DATA;
|
||||
request->size = sizeof(struct synthhid_protocol_request);
|
||||
request->request.header.type = SYNTH_HID_PROTOCOL_REQUEST;
|
||||
request->request.header.size = sizeof(unsigned int);
|
||||
request->request.version_requested.version = SYNTHHID_INPUT_VERSION;
|
||||
|
||||
ret = vmbus_sendpacket(device->channel, request,
|
||||
sizeof(struct pipe_prt_msg) -
|
||||
sizeof(unsigned char) +
|
||||
sizeof(struct synthhid_protocol_request),
|
||||
(unsigned long)request,
|
||||
VM_PKT_DATA_INBAND,
|
||||
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ);
|
||||
if (!t) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
response = &input_dev->protocol_resp;
|
||||
|
||||
if (!response->response.approved) {
|
||||
pr_err("synthhid protocol request failed (version %d)\n",
|
||||
SYNTHHID_INPUT_VERSION);
|
||||
ret = -ENODEV;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ);
|
||||
if (!t) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* We should have gotten the device attr, hid desc and report
|
||||
* desc at this point
|
||||
*/
|
||||
ret = input_dev->dev_info_status;
|
||||
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mousevsc_hid_open(struct hid_device *hid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mousevsc_hid_start(struct hid_device *hid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mousevsc_hid_close(struct hid_device *hid)
|
||||
{
|
||||
}
|
||||
|
||||
static void mousevsc_hid_stop(struct hid_device *hid)
|
||||
{
|
||||
}
|
||||
|
||||
static struct hid_ll_driver mousevsc_ll_driver = {
|
||||
.open = mousevsc_hid_open,
|
||||
.close = mousevsc_hid_close,
|
||||
.start = mousevsc_hid_start,
|
||||
.stop = mousevsc_hid_stop,
|
||||
};
|
||||
|
||||
static struct hid_driver mousevsc_hid_driver;
|
||||
|
||||
static int mousevsc_probe(struct hv_device *device,
|
||||
const struct hv_vmbus_device_id *dev_id)
|
||||
{
|
||||
int ret;
|
||||
struct mousevsc_dev *input_dev;
|
||||
struct hid_device *hid_dev;
|
||||
|
||||
input_dev = mousevsc_alloc_device(device);
|
||||
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = vmbus_open(device->channel,
|
||||
INPUTVSC_SEND_RING_BUFFER_SIZE,
|
||||
INPUTVSC_RECV_RING_BUFFER_SIZE,
|
||||
NULL,
|
||||
0,
|
||||
mousevsc_on_channel_callback,
|
||||
device
|
||||
);
|
||||
|
||||
if (ret)
|
||||
goto probe_err0;
|
||||
|
||||
ret = mousevsc_connect_to_vsp(device);
|
||||
|
||||
if (ret)
|
||||
goto probe_err1;
|
||||
|
||||
/* workaround SA-167 */
|
||||
if (input_dev->report_desc[14] == 0x25)
|
||||
input_dev->report_desc[14] = 0x29;
|
||||
|
||||
hid_dev = hid_allocate_device();
|
||||
if (IS_ERR(hid_dev)) {
|
||||
ret = PTR_ERR(hid_dev);
|
||||
goto probe_err1;
|
||||
}
|
||||
|
||||
hid_dev->ll_driver = &mousevsc_ll_driver;
|
||||
hid_dev->driver = &mousevsc_hid_driver;
|
||||
hid_dev->bus = BUS_VIRTUAL;
|
||||
hid_dev->vendor = input_dev->hid_dev_info.vendor;
|
||||
hid_dev->product = input_dev->hid_dev_info.product;
|
||||
hid_dev->version = input_dev->hid_dev_info.version;
|
||||
input_dev->hid_device = hid_dev;
|
||||
|
||||
sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse");
|
||||
|
||||
ret = hid_add_device(hid_dev);
|
||||
if (ret)
|
||||
goto probe_err1;
|
||||
|
||||
ret = hid_parse_report(hid_dev, input_dev->report_desc,
|
||||
input_dev->report_desc_size);
|
||||
|
||||
if (ret) {
|
||||
hid_err(hid_dev, "parse failed\n");
|
||||
goto probe_err2;
|
||||
}
|
||||
|
||||
ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV);
|
||||
|
||||
if (ret) {
|
||||
hid_err(hid_dev, "hw start failed\n");
|
||||
goto probe_err2;
|
||||
}
|
||||
|
||||
input_dev->connected = true;
|
||||
input_dev->init_complete = true;
|
||||
|
||||
return ret;
|
||||
|
||||
probe_err2:
|
||||
hid_destroy_device(hid_dev);
|
||||
|
||||
probe_err1:
|
||||
vmbus_close(device->channel);
|
||||
|
||||
probe_err0:
|
||||
mousevsc_free_device(input_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int mousevsc_remove(struct hv_device *dev)
|
||||
{
|
||||
struct mousevsc_dev *input_dev = hv_get_drvdata(dev);
|
||||
|
||||
vmbus_close(dev->channel);
|
||||
hid_destroy_device(input_dev->hid_device);
|
||||
mousevsc_free_device(input_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hv_vmbus_device_id id_table[] = {
|
||||
/* Mouse guid */
|
||||
{ VMBUS_DEVICE(0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
|
||||
0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A) },
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(vmbus, id_table);
|
||||
|
||||
static struct hv_driver mousevsc_drv = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = id_table,
|
||||
.probe = mousevsc_probe,
|
||||
.remove = mousevsc_remove,
|
||||
};
|
||||
|
||||
static int __init mousevsc_init(void)
|
||||
{
|
||||
return vmbus_driver_register(&mousevsc_drv);
|
||||
}
|
||||
|
||||
static void __exit mousevsc_exit(void)
|
||||
{
|
||||
vmbus_driver_unregister(&mousevsc_drv);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(HV_DRV_VERSION);
|
||||
module_init(mousevsc_init);
|
||||
module_exit(mousevsc_exit);
|
@ -21,6 +21,7 @@
|
||||
#define USB_VENDOR_ID_3M 0x0596
|
||||
#define USB_DEVICE_ID_3M1968 0x0500
|
||||
#define USB_DEVICE_ID_3M2256 0x0502
|
||||
#define USB_DEVICE_ID_3M3266 0x0506
|
||||
|
||||
#define USB_VENDOR_ID_A4TECH 0x09da
|
||||
#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006
|
||||
@ -124,6 +125,7 @@
|
||||
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239
|
||||
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a
|
||||
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
|
||||
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255
|
||||
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256
|
||||
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
|
||||
@ -145,6 +147,9 @@
|
||||
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
|
||||
#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
|
||||
|
||||
#define USB_VENDOR_ID_ATMEL 0x03eb
|
||||
#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
|
||||
|
||||
#define USB_VENDOR_ID_AVERMEDIA 0x07ca
|
||||
#define USB_DEVICE_ID_AVER_FM_MR800 0xb800
|
||||
|
||||
@ -230,11 +235,14 @@
|
||||
|
||||
#define USB_VENDOR_ID_DWAV 0x0eef
|
||||
#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 0x480d
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1 0x720c
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2 0x72a1
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3 0x480e
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4 0x726b
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D 0x480d
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E 0x480e
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C 0x720c
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
|
||||
|
||||
#define USB_VENDOR_ID_ELECOM 0x056e
|
||||
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
|
||||
@ -356,6 +364,9 @@
|
||||
#define USB_VENDOR_ID_HANVON 0x20b3
|
||||
#define USB_DEVICE_ID_HANVON_MULTITOUCH 0x0a18
|
||||
|
||||
#define USB_VENDOR_ID_HANVON_ALT 0x22ed
|
||||
#define USB_DEVICE_ID_HANVON_ALT_MULTITOUCH 0x1010
|
||||
|
||||
#define USB_VENDOR_ID_HAPP 0x078b
|
||||
#define USB_DEVICE_ID_UGCI_DRIVING 0x0010
|
||||
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
|
||||
@ -571,6 +582,11 @@
|
||||
#define USB_VENDOR_ID_PI_ENGINEERING 0x05f3
|
||||
#define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff
|
||||
|
||||
#define USB_VENDOR_ID_PIXART 0x093a
|
||||
#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN 0x8001
|
||||
#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1 0x8002
|
||||
#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2 0x8003
|
||||
|
||||
#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43
|
||||
#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003
|
||||
|
||||
@ -581,11 +597,14 @@
|
||||
#define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062
|
||||
|
||||
#define USB_VENDOR_ID_QUANTA 0x0408
|
||||
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000
|
||||
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000
|
||||
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001
|
||||
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008
|
||||
#define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001
|
||||
|
||||
#define USB_VENDOR_ID_ROCCAT 0x1e7d
|
||||
#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4
|
||||
#define USB_DEVICE_ID_ROCCAT_ISKU 0x319c
|
||||
#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced
|
||||
#define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51
|
||||
#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50
|
||||
@ -679,6 +698,7 @@
|
||||
|
||||
#define USB_VENDOR_ID_WACOM 0x056a
|
||||
#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81
|
||||
#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH 0x00BD
|
||||
|
||||
#define USB_VENDOR_ID_WALTOP 0x172f
|
||||
#define USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH 0x0032
|
||||
@ -707,6 +727,17 @@
|
||||
#define USB_VENDOR_ID_XAT 0x2505
|
||||
#define USB_DEVICE_ID_XAT_CSR 0x0220
|
||||
|
||||
#define USB_VENDOR_ID_XIROKU 0x1477
|
||||
#define USB_DEVICE_ID_XIROKU_SPX 0x1006
|
||||
#define USB_DEVICE_ID_XIROKU_MPX 0x1007
|
||||
#define USB_DEVICE_ID_XIROKU_CSR 0x100e
|
||||
#define USB_DEVICE_ID_XIROKU_SPX1 0x1021
|
||||
#define USB_DEVICE_ID_XIROKU_CSR1 0x1022
|
||||
#define USB_DEVICE_ID_XIROKU_MPX1 0x1023
|
||||
#define USB_DEVICE_ID_XIROKU_SPX2 0x1024
|
||||
#define USB_DEVICE_ID_XIROKU_CSR2 0x1025
|
||||
#define USB_DEVICE_ID_XIROKU_MPX2 0x1026
|
||||
|
||||
#define USB_VENDOR_ID_YEALINK 0x6993
|
||||
#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001
|
||||
|
||||
|
@ -32,6 +32,8 @@
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hid-debug.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define unk KEY_UNKNOWN
|
||||
|
||||
static const unsigned char hid_keyboard[256] = {
|
||||
@ -271,6 +273,161 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
|
||||
return logical_extents / physical_extents;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HID_BATTERY_STRENGTH
|
||||
static enum power_supply_property hidinput_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_STATUS
|
||||
};
|
||||
|
||||
#define HID_BATTERY_QUIRK_PERCENT (1 << 0) /* always reports percent */
|
||||
#define HID_BATTERY_QUIRK_FEATURE (1 << 1) /* ask for feature report */
|
||||
|
||||
static const struct hid_device_id hid_battery_quirks[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
|
||||
USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI),
|
||||
HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
|
||||
{}
|
||||
};
|
||||
|
||||
static unsigned find_battery_quirk(struct hid_device *hdev)
|
||||
{
|
||||
unsigned quirks = 0;
|
||||
const struct hid_device_id *match;
|
||||
|
||||
match = hid_match_id(hdev, hid_battery_quirks);
|
||||
if (match != NULL)
|
||||
quirks = match->driver_data;
|
||||
|
||||
return quirks;
|
||||
}
|
||||
|
||||
static int hidinput_get_battery_property(struct power_supply *psy,
|
||||
enum power_supply_property prop,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct hid_device *dev = container_of(psy, struct hid_device, battery);
|
||||
int ret = 0;
|
||||
__u8 buf[2] = {};
|
||||
|
||||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = 1;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
ret = dev->hid_get_raw_report(dev, dev->battery_report_id,
|
||||
buf, sizeof(buf),
|
||||
dev->battery_report_type);
|
||||
|
||||
if (ret != 2) {
|
||||
if (ret >= 0)
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dev->battery_min < dev->battery_max &&
|
||||
buf[1] >= dev->battery_min &&
|
||||
buf[1] <= dev->battery_max)
|
||||
val->intval = (100 * (buf[1] - dev->battery_min)) /
|
||||
(dev->battery_max - dev->battery_min);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = dev->name;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field)
|
||||
{
|
||||
struct power_supply *battery = &dev->battery;
|
||||
int ret;
|
||||
unsigned quirks;
|
||||
s32 min, max;
|
||||
|
||||
if (field->usage->hid != HID_DC_BATTERYSTRENGTH)
|
||||
return false; /* no match */
|
||||
|
||||
if (battery->name != NULL)
|
||||
goto out; /* already initialized? */
|
||||
|
||||
battery->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq);
|
||||
if (battery->name == NULL)
|
||||
goto out;
|
||||
|
||||
battery->type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
battery->properties = hidinput_battery_props;
|
||||
battery->num_properties = ARRAY_SIZE(hidinput_battery_props);
|
||||
battery->use_for_apm = 0;
|
||||
battery->get_property = hidinput_get_battery_property;
|
||||
|
||||
quirks = find_battery_quirk(dev);
|
||||
|
||||
hid_dbg(dev, "device %x:%x:%x %d quirks %d\n",
|
||||
dev->bus, dev->vendor, dev->product, dev->version, quirks);
|
||||
|
||||
min = field->logical_minimum;
|
||||
max = field->logical_maximum;
|
||||
|
||||
if (quirks & HID_BATTERY_QUIRK_PERCENT) {
|
||||
min = 0;
|
||||
max = 100;
|
||||
}
|
||||
|
||||
if (quirks & HID_BATTERY_QUIRK_FEATURE)
|
||||
report_type = HID_FEATURE_REPORT;
|
||||
|
||||
dev->battery_min = min;
|
||||
dev->battery_max = max;
|
||||
dev->battery_report_type = report_type;
|
||||
dev->battery_report_id = field->report->id;
|
||||
|
||||
ret = power_supply_register(&dev->dev, battery);
|
||||
if (ret != 0) {
|
||||
hid_warn(dev, "can't register power supply: %d\n", ret);
|
||||
kfree(battery->name);
|
||||
battery->name = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hidinput_cleanup_battery(struct hid_device *dev)
|
||||
{
|
||||
if (!dev->battery.name)
|
||||
return;
|
||||
|
||||
power_supply_unregister(&dev->battery);
|
||||
kfree(dev->battery.name);
|
||||
dev->battery.name = NULL;
|
||||
}
|
||||
#else /* !CONFIG_HID_BATTERY_STRENGTH */
|
||||
static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
|
||||
struct hid_field *field)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static void hidinput_cleanup_battery(struct hid_device *dev)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_HID_BATTERY_STRENGTH */
|
||||
|
||||
static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
|
||||
struct hid_usage *usage)
|
||||
{
|
||||
@ -629,6 +786,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_UP_GENDEVCTRLS:
|
||||
if (hidinput_setup_battery(device, HID_INPUT_REPORT, field))
|
||||
goto ignore;
|
||||
else
|
||||
goto unknown;
|
||||
break;
|
||||
|
||||
case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */
|
||||
set_bit(EV_REP, input->evbit);
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
@ -822,6 +986,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore out-of-range values as per HID specification, section 5.10 */
|
||||
if (value < field->logical_minimum || value > field->logical_maximum) {
|
||||
dbg_hid("Ignoring out-of-range value %x\n", value);
|
||||
return;
|
||||
}
|
||||
|
||||
/* report the usage code as scancode if the key status has changed */
|
||||
if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
|
||||
input_event(input, EV_MSC, MSC_SCAN, usage->hid);
|
||||
@ -861,6 +1031,48 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hidinput_find_field);
|
||||
|
||||
struct hid_field *hidinput_get_led_field(struct hid_device *hid)
|
||||
{
|
||||
struct hid_report *report;
|
||||
struct hid_field *field;
|
||||
int i, j;
|
||||
|
||||
list_for_each_entry(report,
|
||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list,
|
||||
list) {
|
||||
for (i = 0; i < report->maxfield; i++) {
|
||||
field = report->field[i];
|
||||
for (j = 0; j < field->maxusage; j++)
|
||||
if (field->usage[j].type == EV_LED)
|
||||
return field;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hidinput_get_led_field);
|
||||
|
||||
unsigned int hidinput_count_leds(struct hid_device *hid)
|
||||
{
|
||||
struct hid_report *report;
|
||||
struct hid_field *field;
|
||||
int i, j;
|
||||
unsigned int count = 0;
|
||||
|
||||
list_for_each_entry(report,
|
||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list,
|
||||
list) {
|
||||
for (i = 0; i < report->maxfield; i++) {
|
||||
field = report->field[i];
|
||||
for (j = 0; j < field->maxusage; j++)
|
||||
if (field->usage[j].type == EV_LED &&
|
||||
field->value[j])
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hidinput_count_leds);
|
||||
|
||||
static int hidinput_open(struct input_dev *dev)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
@ -882,15 +1094,17 @@ static void report_features(struct hid_device *hid)
|
||||
struct hid_report *rep;
|
||||
int i, j;
|
||||
|
||||
if (!drv->feature_mapping)
|
||||
return;
|
||||
|
||||
rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
|
||||
list_for_each_entry(rep, &rep_enum->report_list, list)
|
||||
for (i = 0; i < rep->maxfield; i++)
|
||||
for (j = 0; j < rep->field[i]->maxusage; j++)
|
||||
drv->feature_mapping(hid, rep->field[i],
|
||||
rep->field[i]->usage + j);
|
||||
for (j = 0; j < rep->field[i]->maxusage; j++) {
|
||||
/* Verify if Battery Strength feature is available */
|
||||
hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]);
|
||||
|
||||
if (drv->feature_mapping)
|
||||
drv->feature_mapping(hid, rep->field[i],
|
||||
rep->field[i]->usage + j);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1010,6 +1224,8 @@ void hidinput_disconnect(struct hid_device *hid)
|
||||
{
|
||||
struct hid_input *hidinput, *next;
|
||||
|
||||
hidinput_cleanup_battery(hid);
|
||||
|
||||
list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
|
||||
list_del(&hidinput->list);
|
||||
input_unregister_device(hidinput->input);
|
||||
|
@ -430,7 +430,7 @@ int lg4ff_init(struct hid_device *hid)
|
||||
}
|
||||
|
||||
/* Add the device to device_list */
|
||||
entry = (struct lg4ff_device_entry *)kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
|
||||
entry = kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
hid_err(hid, "Cannot add device, insufficient memory.\n");
|
||||
return -ENOMEM;
|
||||
|
@ -50,7 +50,6 @@ MODULE_LICENSE("GPL");
|
||||
#define MT_QUIRK_ALWAYS_VALID (1 << 4)
|
||||
#define MT_QUIRK_VALID_IS_INRANGE (1 << 5)
|
||||
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6)
|
||||
#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 7)
|
||||
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8)
|
||||
|
||||
struct mt_slot {
|
||||
@ -60,20 +59,6 @@ struct mt_slot {
|
||||
bool seen_in_this_frame;/* has this slot been updated */
|
||||
};
|
||||
|
||||
struct mt_device {
|
||||
struct mt_slot curdata; /* placeholder of incoming data */
|
||||
struct mt_class *mtclass; /* our mt device class */
|
||||
unsigned last_field_index; /* last field index of the report */
|
||||
unsigned last_slot_field; /* the last field of a slot */
|
||||
int last_mt_collection; /* last known mt-related collection */
|
||||
__s8 inputmode; /* InputMode HID feature, -1 if non-existent */
|
||||
__u8 num_received; /* how many contacts we received */
|
||||
__u8 num_expected; /* expected last contact index */
|
||||
__u8 maxcontacts;
|
||||
bool curvalid; /* is the current contact valid? */
|
||||
struct mt_slot *slots;
|
||||
};
|
||||
|
||||
struct mt_class {
|
||||
__s32 name; /* MT_CLS */
|
||||
__s32 quirks;
|
||||
@ -84,20 +69,37 @@ struct mt_class {
|
||||
__u8 maxcontacts;
|
||||
};
|
||||
|
||||
struct mt_device {
|
||||
struct mt_slot curdata; /* placeholder of incoming data */
|
||||
struct mt_class mtclass; /* our mt device class */
|
||||
unsigned last_field_index; /* last field index of the report */
|
||||
unsigned last_slot_field; /* the last field of a slot */
|
||||
int last_mt_collection; /* last known mt-related collection */
|
||||
__s8 inputmode; /* InputMode HID feature, -1 if non-existent */
|
||||
__u8 num_received; /* how many contacts we received */
|
||||
__u8 num_expected; /* expected last contact index */
|
||||
__u8 maxcontacts;
|
||||
bool curvalid; /* is the current contact valid? */
|
||||
struct mt_slot *slots;
|
||||
};
|
||||
|
||||
/* classes of device behavior */
|
||||
#define MT_CLS_DEFAULT 0x0001
|
||||
|
||||
#define MT_CLS_SERIAL 0x0002
|
||||
#define MT_CLS_CONFIDENCE 0x0003
|
||||
#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0004
|
||||
#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0005
|
||||
#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0006
|
||||
#define MT_CLS_DUAL_NSMU_CONTACTID 0x0007
|
||||
#define MT_CLS_CONFIDENCE_CONTACT_ID 0x0004
|
||||
#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0005
|
||||
#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0006
|
||||
#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0007
|
||||
#define MT_CLS_DUAL_NSMU_CONTACTID 0x0008
|
||||
#define MT_CLS_INRANGE_CONTACTNUMBER 0x0009
|
||||
|
||||
/* vendor specific classes */
|
||||
#define MT_CLS_3M 0x0101
|
||||
#define MT_CLS_CYPRESS 0x0102
|
||||
#define MT_CLS_EGALAX 0x0103
|
||||
#define MT_CLS_EGALAX_SERIAL 0x0104
|
||||
|
||||
#define MT_DEFAULT_MAXCONTACT 10
|
||||
|
||||
@ -133,13 +135,16 @@ static int find_slot_from_contactid(struct mt_device *td)
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct mt_class mt_classes[] = {
|
||||
static struct mt_class mt_classes[] = {
|
||||
{ .name = MT_CLS_DEFAULT,
|
||||
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP },
|
||||
{ .name = MT_CLS_SERIAL,
|
||||
.quirks = MT_QUIRK_ALWAYS_VALID},
|
||||
{ .name = MT_CLS_CONFIDENCE,
|
||||
.quirks = MT_QUIRK_VALID_IS_CONFIDENCE },
|
||||
{ .name = MT_CLS_CONFIDENCE_CONTACT_ID,
|
||||
.quirks = MT_QUIRK_VALID_IS_CONFIDENCE |
|
||||
MT_QUIRK_SLOT_IS_CONTACTID },
|
||||
{ .name = MT_CLS_CONFIDENCE_MINUS_ONE,
|
||||
.quirks = MT_QUIRK_VALID_IS_CONFIDENCE |
|
||||
MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE },
|
||||
@ -155,6 +160,9 @@ struct mt_class mt_classes[] = {
|
||||
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
|
||||
MT_QUIRK_SLOT_IS_CONTACTID,
|
||||
.maxcontacts = 2 },
|
||||
{ .name = MT_CLS_INRANGE_CONTACTNUMBER,
|
||||
.quirks = MT_QUIRK_VALID_IS_INRANGE |
|
||||
MT_QUIRK_SLOT_IS_CONTACTNUMBER },
|
||||
|
||||
/*
|
||||
* vendor specific classes
|
||||
@ -171,9 +179,13 @@ struct mt_class mt_classes[] = {
|
||||
.maxcontacts = 10 },
|
||||
{ .name = MT_CLS_EGALAX,
|
||||
.quirks = MT_QUIRK_SLOT_IS_CONTACTID |
|
||||
MT_QUIRK_VALID_IS_INRANGE |
|
||||
MT_QUIRK_EGALAX_XYZ_FIXUP,
|
||||
.maxcontacts = 2,
|
||||
MT_QUIRK_VALID_IS_INRANGE,
|
||||
.sn_move = 4096,
|
||||
.sn_pressure = 32,
|
||||
},
|
||||
{ .name = MT_CLS_EGALAX_SERIAL,
|
||||
.quirks = MT_QUIRK_SLOT_IS_CONTACTID |
|
||||
MT_QUIRK_ALWAYS_VALID,
|
||||
.sn_move = 4096,
|
||||
.sn_pressure = 32,
|
||||
},
|
||||
@ -181,6 +193,44 @@ struct mt_class mt_classes[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static ssize_t mt_show_quirks(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
struct mt_device *td = hid_get_drvdata(hdev);
|
||||
|
||||
return sprintf(buf, "%u\n", td->mtclass.quirks);
|
||||
}
|
||||
|
||||
static ssize_t mt_set_quirks(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
struct mt_device *td = hid_get_drvdata(hdev);
|
||||
|
||||
unsigned long val;
|
||||
|
||||
if (kstrtoul(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
td->mtclass.quirks = val;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(quirks, S_IWUSR | S_IRUGO, mt_show_quirks, mt_set_quirks);
|
||||
|
||||
static struct attribute *sysfs_attrs[] = {
|
||||
&dev_attr_quirks.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group mt_attribute_group = {
|
||||
.attrs = sysfs_attrs
|
||||
};
|
||||
|
||||
static void mt_feature_mapping(struct hid_device *hdev,
|
||||
struct hid_field *field, struct hid_usage *usage)
|
||||
{
|
||||
@ -192,9 +242,9 @@ static void mt_feature_mapping(struct hid_device *hdev,
|
||||
break;
|
||||
case HID_DG_CONTACTMAX:
|
||||
td->maxcontacts = field->value[0];
|
||||
if (td->mtclass->maxcontacts)
|
||||
if (td->mtclass.maxcontacts)
|
||||
/* check if the maxcontacts is given by the class */
|
||||
td->maxcontacts = td->mtclass->maxcontacts;
|
||||
td->maxcontacts = td->mtclass.maxcontacts;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -214,8 +264,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
struct mt_device *td = hid_get_drvdata(hdev);
|
||||
struct mt_class *cls = td->mtclass;
|
||||
__s32 quirks = cls->quirks;
|
||||
struct mt_class *cls = &td->mtclass;
|
||||
|
||||
/* Only map fields from TouchScreen or TouchPad collections.
|
||||
* We need to ignore fields that belong to other collections
|
||||
@ -227,13 +276,17 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
else
|
||||
return 0;
|
||||
|
||||
/* eGalax devices provide a Digitizer.Stylus input which overrides
|
||||
* the correct Digitizers.Finger X/Y ranges.
|
||||
* Let's just ignore this input. */
|
||||
if (field->physical == HID_DG_STYLUS)
|
||||
return -1;
|
||||
|
||||
switch (usage->hid & HID_USAGE_PAGE) {
|
||||
|
||||
case HID_UP_GENDESK:
|
||||
switch (usage->hid) {
|
||||
case HID_GD_X:
|
||||
if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
|
||||
field->logical_maximum = 32760;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_X);
|
||||
set_abs(hi->input, ABS_MT_POSITION_X, field,
|
||||
@ -246,8 +299,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
}
|
||||
return 1;
|
||||
case HID_GD_Y:
|
||||
if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
|
||||
field->logical_maximum = 32760;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_Y);
|
||||
set_abs(hi->input, ABS_MT_POSITION_Y, field,
|
||||
@ -315,8 +366,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
}
|
||||
return 1;
|
||||
case HID_DG_TIPPRESSURE:
|
||||
if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
|
||||
field->logical_minimum = 0;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_PRESSURE);
|
||||
set_abs(hi->input, ABS_MT_PRESSURE, field,
|
||||
@ -363,7 +412,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
|
||||
static int mt_compute_slot(struct mt_device *td)
|
||||
{
|
||||
__s32 quirks = td->mtclass->quirks;
|
||||
__s32 quirks = td->mtclass.quirks;
|
||||
|
||||
if (quirks & MT_QUIRK_SLOT_IS_CONTACTID)
|
||||
return td->curdata.contactid;
|
||||
@ -407,7 +456,7 @@ static void mt_emit_event(struct mt_device *td, struct input_dev *input)
|
||||
|
||||
for (i = 0; i < td->maxcontacts; ++i) {
|
||||
struct mt_slot *s = &(td->slots[i]);
|
||||
if ((td->mtclass->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) &&
|
||||
if ((td->mtclass.quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) &&
|
||||
!s->seen_in_this_frame) {
|
||||
s->touch_state = false;
|
||||
}
|
||||
@ -444,7 +493,7 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct mt_device *td = hid_get_drvdata(hid);
|
||||
__s32 quirks = td->mtclass->quirks;
|
||||
__s32 quirks = td->mtclass.quirks;
|
||||
|
||||
if (hid->claimed & HID_CLAIMED_INPUT && td->slots) {
|
||||
switch (usage->hid) {
|
||||
@ -552,7 +601,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
dev_err(&hdev->dev, "cannot allocate multitouch data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
td->mtclass = mtclass;
|
||||
td->mtclass = *mtclass;
|
||||
td->inputmode = -1;
|
||||
td->last_mt_collection = -1;
|
||||
hid_set_drvdata(hdev, td);
|
||||
@ -574,6 +623,8 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
|
||||
|
||||
mt_set_input_mode(hdev);
|
||||
|
||||
return 0;
|
||||
@ -594,6 +645,7 @@ static int mt_reset_resume(struct hid_device *hdev)
|
||||
static void mt_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct mt_device *td = hid_get_drvdata(hdev);
|
||||
sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
|
||||
hid_hw_stop(hdev);
|
||||
kfree(td->slots);
|
||||
kfree(td);
|
||||
@ -609,12 +661,20 @@ static const struct hid_device_id mt_devices[] = {
|
||||
{ .driver_data = MT_CLS_3M,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_3M,
|
||||
USB_DEVICE_ID_3M2256) },
|
||||
{ .driver_data = MT_CLS_3M,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_3M,
|
||||
USB_DEVICE_ID_3M3266) },
|
||||
|
||||
/* ActionStar panels */
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR,
|
||||
USB_DEVICE_ID_ACTIONSTAR_1011) },
|
||||
|
||||
/* Atmel panels */
|
||||
{ .driver_data = MT_CLS_SERIAL,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_ATMEL,
|
||||
USB_DEVICE_ID_ATMEL_MULTITOUCH) },
|
||||
|
||||
/* Cando panels */
|
||||
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
|
||||
@ -645,23 +705,32 @@ static const struct hid_device_id mt_devices[] = {
|
||||
USB_DEVICE_ID_CYPRESS_TRUETOUCH) },
|
||||
|
||||
/* eGalax devices (resistive) */
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) },
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) },
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) },
|
||||
|
||||
/* eGalax devices (capacitive) */
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) },
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) },
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) },
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) },
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) },
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) },
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA) },
|
||||
{ .driver_data = MT_CLS_EGALAX,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) },
|
||||
{ .driver_data = MT_CLS_EGALAX_SERIAL,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
|
||||
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
|
||||
|
||||
/* Elo TouchSystems IntelliTouch Plus panel */
|
||||
{ .driver_data = MT_CLS_DUAL_NSMU_CONTACTID,
|
||||
@ -678,6 +747,11 @@ static const struct hid_device_id mt_devices[] = {
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH,
|
||||
USB_DEVICE_ID_GOODTOUCH_000f) },
|
||||
|
||||
/* Hanvon panels */
|
||||
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT,
|
||||
USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) },
|
||||
|
||||
/* Ideacom panel */
|
||||
{ .driver_data = MT_CLS_SERIAL,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM,
|
||||
@ -722,6 +796,17 @@ static const struct hid_device_id mt_devices[] = {
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT,
|
||||
USB_DEVICE_ID_PENMOUNT_PCI) },
|
||||
|
||||
/* PixArt optical touch screen */
|
||||
{ .driver_data = MT_CLS_INRANGE_CONTACTNUMBER,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_PIXART,
|
||||
USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) },
|
||||
{ .driver_data = MT_CLS_INRANGE_CONTACTNUMBER,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_PIXART,
|
||||
USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) },
|
||||
{ .driver_data = MT_CLS_INRANGE_CONTACTNUMBER,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_PIXART,
|
||||
USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) },
|
||||
|
||||
/* PixCir-based panels */
|
||||
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_HANVON,
|
||||
@ -730,6 +815,17 @@ static const struct hid_device_id mt_devices[] = {
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
|
||||
USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) },
|
||||
|
||||
/* Quanta-based panels */
|
||||
{ .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
|
||||
USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
|
||||
{ .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
|
||||
USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001) },
|
||||
{ .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
|
||||
USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) },
|
||||
|
||||
/* Stantum panels */
|
||||
{ .driver_data = MT_CLS_CONFIDENCE,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_STANTUM,
|
||||
@ -758,6 +854,35 @@ static const struct hid_device_id mt_devices[] = {
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_XAT,
|
||||
USB_DEVICE_ID_XAT_CSR) },
|
||||
|
||||
/* Xiroku */
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
|
||||
USB_DEVICE_ID_XIROKU_SPX) },
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
|
||||
USB_DEVICE_ID_XIROKU_MPX) },
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
|
||||
USB_DEVICE_ID_XIROKU_CSR) },
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
|
||||
USB_DEVICE_ID_XIROKU_SPX1) },
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
|
||||
USB_DEVICE_ID_XIROKU_MPX1) },
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
|
||||
USB_DEVICE_ID_XIROKU_CSR1) },
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
|
||||
USB_DEVICE_ID_XIROKU_SPX2) },
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
|
||||
USB_DEVICE_ID_XIROKU_MPX2) },
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
|
||||
USB_DEVICE_ID_XIROKU_CSR2) },
|
||||
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, mt_devices);
|
||||
|
@ -633,7 +633,7 @@ struct picolcd_fb_cleanup_item {
|
||||
struct picolcd_fb_cleanup_item *next;
|
||||
};
|
||||
static struct picolcd_fb_cleanup_item *fb_pending;
|
||||
DEFINE_SPINLOCK(fb_pending_lock);
|
||||
static DEFINE_SPINLOCK(fb_pending_lock);
|
||||
|
||||
static void picolcd_fb_do_cleanup(struct work_struct *data)
|
||||
{
|
||||
@ -658,7 +658,7 @@ static void picolcd_fb_do_cleanup(struct work_struct *data)
|
||||
} while (item);
|
||||
}
|
||||
|
||||
DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup);
|
||||
static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup);
|
||||
|
||||
static int picolcd_fb_open(struct fb_info *info, int u)
|
||||
{
|
||||
|
@ -1,261 +0,0 @@
|
||||
/*
|
||||
* HID driver for Quanta Optical Touch dual-touch panels
|
||||
*
|
||||
* Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/slab.h>
|
||||
|
||||
MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
|
||||
MODULE_DESCRIPTION("Quanta dual-touch panel");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
struct quanta_data {
|
||||
__u16 x, y;
|
||||
__u8 id;
|
||||
bool valid; /* valid finger data, or just placeholder? */
|
||||
bool first; /* is this the first finger in this frame? */
|
||||
bool activity_now; /* at least one active finger in this frame? */
|
||||
bool activity; /* at least one active finger previously? */
|
||||
};
|
||||
|
||||
static int quanta_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
switch (usage->hid & HID_USAGE_PAGE) {
|
||||
|
||||
case HID_UP_GENDESK:
|
||||
switch (usage->hid) {
|
||||
case HID_GD_X:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_X);
|
||||
/* touchscreen emulation */
|
||||
input_set_abs_params(hi->input, ABS_X,
|
||||
field->logical_minimum,
|
||||
field->logical_maximum, 0, 0);
|
||||
return 1;
|
||||
case HID_GD_Y:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_Y);
|
||||
/* touchscreen emulation */
|
||||
input_set_abs_params(hi->input, ABS_Y,
|
||||
field->logical_minimum,
|
||||
field->logical_maximum, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case HID_UP_DIGITIZER:
|
||||
switch (usage->hid) {
|
||||
case HID_DG_CONFIDENCE:
|
||||
case HID_DG_TIPSWITCH:
|
||||
case HID_DG_INPUTMODE:
|
||||
case HID_DG_DEVICEINDEX:
|
||||
case HID_DG_CONTACTCOUNT:
|
||||
case HID_DG_CONTACTMAX:
|
||||
case HID_DG_TIPPRESSURE:
|
||||
case HID_DG_WIDTH:
|
||||
case HID_DG_HEIGHT:
|
||||
return -1;
|
||||
case HID_DG_INRANGE:
|
||||
/* touchscreen emulation */
|
||||
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
|
||||
return 1;
|
||||
case HID_DG_CONTACTID:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_TRACKING_ID);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case 0xff000000:
|
||||
/* ignore vendor-specific features */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int quanta_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
if (usage->type == EV_KEY || usage->type == EV_ABS)
|
||||
clear_bit(usage->code, *bit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* this function is called when a whole finger has been parsed,
|
||||
* so that it can decide what to send to the input layer.
|
||||
*/
|
||||
static void quanta_filter_event(struct quanta_data *td, struct input_dev *input)
|
||||
{
|
||||
|
||||
td->first = !td->first; /* touchscreen emulation */
|
||||
|
||||
if (!td->valid) {
|
||||
/*
|
||||
* touchscreen emulation: if no finger in this frame is valid
|
||||
* and there previously was finger activity, this is a release
|
||||
*/
|
||||
if (!td->first && !td->activity_now && td->activity) {
|
||||
input_event(input, EV_KEY, BTN_TOUCH, 0);
|
||||
td->activity = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y);
|
||||
|
||||
input_mt_sync(input);
|
||||
td->valid = false;
|
||||
|
||||
/* touchscreen emulation: if first active finger in this frame... */
|
||||
if (!td->activity_now) {
|
||||
/* if there was no previous activity, emit touch event */
|
||||
if (!td->activity) {
|
||||
input_event(input, EV_KEY, BTN_TOUCH, 1);
|
||||
td->activity = true;
|
||||
}
|
||||
td->activity_now = true;
|
||||
/* and in any case this is our preferred finger */
|
||||
input_event(input, EV_ABS, ABS_X, td->x);
|
||||
input_event(input, EV_ABS, ABS_Y, td->y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int quanta_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct quanta_data *td = hid_get_drvdata(hid);
|
||||
|
||||
if (hid->claimed & HID_CLAIMED_INPUT) {
|
||||
struct input_dev *input = field->hidinput->input;
|
||||
|
||||
switch (usage->hid) {
|
||||
case HID_DG_INRANGE:
|
||||
td->valid = !!value;
|
||||
break;
|
||||
case HID_GD_X:
|
||||
td->x = value;
|
||||
break;
|
||||
case HID_GD_Y:
|
||||
td->y = value;
|
||||
quanta_filter_event(td, input);
|
||||
break;
|
||||
case HID_DG_CONTACTID:
|
||||
td->id = value;
|
||||
break;
|
||||
case HID_DG_CONTACTCOUNT:
|
||||
/* touch emulation: this is the last field in a frame */
|
||||
td->first = false;
|
||||
td->activity_now = false;
|
||||
break;
|
||||
case HID_DG_CONFIDENCE:
|
||||
case HID_DG_TIPSWITCH:
|
||||
/* avoid interference from generic hidinput handling */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* fallback to the generic hidinput handling */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* we have handled the hidinput part, now remains hiddev */
|
||||
if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
|
||||
hid->hiddev_hid_event(hid, field, usage, value);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int quanta_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct quanta_data *td;
|
||||
|
||||
td = kmalloc(sizeof(struct quanta_data), GFP_KERNEL);
|
||||
if (!td) {
|
||||
hid_err(hdev, "cannot allocate Quanta Touch data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
td->valid = false;
|
||||
td->activity = false;
|
||||
td->activity_now = false;
|
||||
td->first = false;
|
||||
hid_set_drvdata(hdev, td);
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (!ret)
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
|
||||
if (ret)
|
||||
kfree(td);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void quanta_remove(struct hid_device *hdev)
|
||||
{
|
||||
hid_hw_stop(hdev);
|
||||
kfree(hid_get_drvdata(hdev));
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
}
|
||||
|
||||
static const struct hid_device_id quanta_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
|
||||
USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
|
||||
USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, quanta_devices);
|
||||
|
||||
static const struct hid_usage_id quanta_grabbed_usages[] = {
|
||||
{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
|
||||
{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
|
||||
};
|
||||
|
||||
static struct hid_driver quanta_driver = {
|
||||
.name = "quanta-touch",
|
||||
.id_table = quanta_devices,
|
||||
.probe = quanta_probe,
|
||||
.remove = quanta_remove,
|
||||
.input_mapping = quanta_input_mapping,
|
||||
.input_mapped = quanta_input_mapped,
|
||||
.usage_table = quanta_grabbed_usages,
|
||||
.event = quanta_event,
|
||||
};
|
||||
|
||||
static int __init quanta_init(void)
|
||||
{
|
||||
return hid_register_driver(&quanta_driver);
|
||||
}
|
||||
|
||||
static void __exit quanta_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&quanta_driver);
|
||||
}
|
||||
|
||||
module_init(quanta_init);
|
||||
module_exit(quanta_exit);
|
||||
|
@ -49,12 +49,10 @@ int roccat_common_send(struct usb_device *usb_dev, uint report_id,
|
||||
char *buf;
|
||||
int len;
|
||||
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
buf = kmemdup(data, size, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buf, data, size);
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
HID_REQ_SET_REPORT,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
|
487
drivers/hid/hid-roccat-isku.c
Normal file
487
drivers/hid/hid-roccat-isku.c
Normal file
@ -0,0 +1,487 @@
|
||||
/*
|
||||
* Roccat Isku driver for Linux
|
||||
*
|
||||
* Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Roccat Isku is a gamer keyboard 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-isku.h"
|
||||
|
||||
static struct class *isku_class;
|
||||
|
||||
static void isku_profile_activated(struct isku_device *isku, uint new_profile)
|
||||
{
|
||||
isku->actual_profile = 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);
|
||||
}
|
||||
|
||||
static int isku_get_actual_profile(struct usb_device *usb_dev)
|
||||
{
|
||||
struct isku_actual_profile buf;
|
||||
int retval;
|
||||
|
||||
retval = isku_receive(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE,
|
||||
&buf, sizeof(struct isku_actual_profile));
|
||||
return retval ? retval : buf.actual_profile;
|
||||
}
|
||||
|
||||
static int isku_set_actual_profile(struct usb_device *usb_dev, int new_profile)
|
||||
{
|
||||
struct isku_actual_profile buf;
|
||||
|
||||
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,
|
||||
sizeof(struct isku_actual_profile));
|
||||
}
|
||||
|
||||
static ssize_t isku_sysfs_show_actual_profile(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct isku_device *isku =
|
||||
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", isku->actual_profile);
|
||||
}
|
||||
|
||||
static ssize_t isku_sysfs_set_actual_profile(struct device *dev,
|
||||
struct device_attribute *attr, char const *buf, size_t size)
|
||||
{
|
||||
struct isku_device *isku;
|
||||
struct usb_device *usb_dev;
|
||||
unsigned long profile;
|
||||
int retval;
|
||||
struct isku_roccat_report roccat_report;
|
||||
|
||||
dev = dev->parent->parent;
|
||||
isku = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
usb_dev = interface_to_usbdev(to_usb_interface(dev));
|
||||
|
||||
retval = strict_strtoul(buf, 10, &profile);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (profile > 4)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&isku->isku_lock);
|
||||
|
||||
retval = isku_set_actual_profile(usb_dev, profile);
|
||||
if (retval) {
|
||||
mutex_unlock(&isku->isku_lock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
isku_profile_activated(isku, profile);
|
||||
|
||||
roccat_report.event = ISKU_REPORT_BUTTON_EVENT_PROFILE;
|
||||
roccat_report.data1 = profile + 1;
|
||||
roccat_report.data2 = 0;
|
||||
roccat_report.profile = profile + 1;
|
||||
roccat_report_event(isku->chrdev_minor, (uint8_t const *)&roccat_report);
|
||||
|
||||
mutex_unlock(&isku->isku_lock);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static struct device_attribute isku_attributes[] = {
|
||||
__ATTR(actual_profile, 0660,
|
||||
isku_sysfs_show_actual_profile,
|
||||
isku_sysfs_set_actual_profile),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static ssize_t isku_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 isku_device *isku = 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(&isku->isku_lock);
|
||||
retval = isku_receive(usb_dev, command, buf, real_size);
|
||||
mutex_unlock(&isku->isku_lock);
|
||||
|
||||
return retval ? retval : real_size;
|
||||
}
|
||||
|
||||
static ssize_t isku_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 isku_device *isku = 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(&isku->isku_lock);
|
||||
retval = isku_send(usb_dev, command, (void *)buf, real_size);
|
||||
mutex_unlock(&isku->isku_lock);
|
||||
|
||||
return retval ? retval : real_size;
|
||||
}
|
||||
|
||||
#define ISKU_SYSFS_W(thingy, THINGY) \
|
||||
static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj, \
|
||||
struct bin_attribute *attr, char *buf, \
|
||||
loff_t off, size_t count) \
|
||||
{ \
|
||||
return isku_sysfs_write(fp, kobj, buf, off, count, \
|
||||
sizeof(struct isku_ ## thingy), ISKU_COMMAND_ ## THINGY); \
|
||||
}
|
||||
|
||||
#define ISKU_SYSFS_R(thingy, THINGY) \
|
||||
static ssize_t isku_sysfs_read_ ## thingy(struct file *fp, struct kobject *kobj, \
|
||||
struct bin_attribute *attr, char *buf, \
|
||||
loff_t off, size_t count) \
|
||||
{ \
|
||||
return isku_sysfs_read(fp, kobj, buf, off, count, \
|
||||
sizeof(struct isku_ ## thingy), ISKU_COMMAND_ ## THINGY); \
|
||||
}
|
||||
|
||||
#define ISKU_SYSFS_RW(thingy, THINGY) \
|
||||
ISKU_SYSFS_R(thingy, THINGY) \
|
||||
ISKU_SYSFS_W(thingy, THINGY)
|
||||
|
||||
#define ISKU_BIN_ATTR_RW(thingy) \
|
||||
{ \
|
||||
.attr = { .name = #thingy, .mode = 0660 }, \
|
||||
.size = sizeof(struct isku_ ## thingy), \
|
||||
.read = isku_sysfs_read_ ## thingy, \
|
||||
.write = isku_sysfs_write_ ## thingy \
|
||||
}
|
||||
|
||||
#define ISKU_BIN_ATTR_R(thingy) \
|
||||
{ \
|
||||
.attr = { .name = #thingy, .mode = 0440 }, \
|
||||
.size = sizeof(struct isku_ ## thingy), \
|
||||
.read = isku_sysfs_read_ ## thingy, \
|
||||
}
|
||||
|
||||
#define ISKU_BIN_ATTR_W(thingy) \
|
||||
{ \
|
||||
.attr = { .name = #thingy, .mode = 0220 }, \
|
||||
.size = sizeof(struct isku_ ## thingy), \
|
||||
.write = isku_sysfs_write_ ## thingy \
|
||||
}
|
||||
|
||||
ISKU_SYSFS_RW(macro, MACRO)
|
||||
ISKU_SYSFS_RW(keys_function, KEYS_FUNCTION)
|
||||
ISKU_SYSFS_RW(keys_easyzone, KEYS_EASYZONE)
|
||||
ISKU_SYSFS_RW(keys_media, KEYS_MEDIA)
|
||||
ISKU_SYSFS_RW(keys_thumbster, KEYS_THUMBSTER)
|
||||
ISKU_SYSFS_RW(keys_macro, KEYS_MACRO)
|
||||
ISKU_SYSFS_RW(keys_capslock, KEYS_CAPSLOCK)
|
||||
ISKU_SYSFS_RW(light, LIGHT)
|
||||
ISKU_SYSFS_RW(key_mask, KEY_MASK)
|
||||
ISKU_SYSFS_RW(last_set, LAST_SET)
|
||||
ISKU_SYSFS_W(talk, TALK)
|
||||
ISKU_SYSFS_R(info, INFO)
|
||||
ISKU_SYSFS_W(control, CONTROL)
|
||||
|
||||
static struct bin_attribute isku_bin_attributes[] = {
|
||||
ISKU_BIN_ATTR_RW(macro),
|
||||
ISKU_BIN_ATTR_RW(keys_function),
|
||||
ISKU_BIN_ATTR_RW(keys_easyzone),
|
||||
ISKU_BIN_ATTR_RW(keys_media),
|
||||
ISKU_BIN_ATTR_RW(keys_thumbster),
|
||||
ISKU_BIN_ATTR_RW(keys_macro),
|
||||
ISKU_BIN_ATTR_RW(keys_capslock),
|
||||
ISKU_BIN_ATTR_RW(light),
|
||||
ISKU_BIN_ATTR_RW(key_mask),
|
||||
ISKU_BIN_ATTR_RW(last_set),
|
||||
ISKU_BIN_ATTR_W(talk),
|
||||
ISKU_BIN_ATTR_R(info),
|
||||
ISKU_BIN_ATTR_W(control),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static int isku_init_isku_device_struct(struct usb_device *usb_dev,
|
||||
struct isku_device *isku)
|
||||
{
|
||||
int retval;
|
||||
|
||||
mutex_init(&isku->isku_lock);
|
||||
|
||||
retval = isku_get_actual_profile(usb_dev);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
isku_profile_activated(isku, retval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isku_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 isku_device *isku;
|
||||
int retval;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
!= ISKU_USB_INTERFACE_PROTOCOL) {
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
isku = kzalloc(sizeof(*isku), GFP_KERNEL);
|
||||
if (!isku) {
|
||||
hid_err(hdev, "can't alloc device descriptor\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
hid_set_drvdata(hdev, isku);
|
||||
|
||||
retval = isku_init_isku_device_struct(usb_dev, isku);
|
||||
if (retval) {
|
||||
hid_err(hdev, "couldn't init struct isku_device\n");
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
retval = roccat_connect(isku_class, hdev,
|
||||
sizeof(struct isku_roccat_report));
|
||||
if (retval < 0) {
|
||||
hid_err(hdev, "couldn't init char dev\n");
|
||||
} else {
|
||||
isku->chrdev_minor = retval;
|
||||
isku->roccat_claimed = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit_free:
|
||||
kfree(isku);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void isku_remove_specials(struct hid_device *hdev)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
struct isku_device *isku;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
!= ISKU_USB_INTERFACE_PROTOCOL)
|
||||
return;
|
||||
|
||||
isku = hid_get_drvdata(hdev);
|
||||
if (isku->roccat_claimed)
|
||||
roccat_disconnect(isku->chrdev_minor);
|
||||
kfree(isku);
|
||||
}
|
||||
|
||||
static int isku_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 = isku_init_specials(hdev);
|
||||
if (retval) {
|
||||
hid_err(hdev, "couldn't install keyboard\n");
|
||||
goto exit_stop;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_stop:
|
||||
hid_hw_stop(hdev);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void isku_remove(struct hid_device *hdev)
|
||||
{
|
||||
isku_remove_specials(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static void isku_keep_values_up_to_date(struct isku_device *isku,
|
||||
u8 const *data)
|
||||
{
|
||||
struct isku_report_button const *button_report;
|
||||
|
||||
switch (data[0]) {
|
||||
case ISKU_REPORT_NUMBER_BUTTON:
|
||||
button_report = (struct isku_report_button const *)data;
|
||||
switch (button_report->event) {
|
||||
case ISKU_REPORT_BUTTON_EVENT_PROFILE:
|
||||
isku_profile_activated(isku, button_report->data1 - 1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void isku_report_to_chrdev(struct isku_device const *isku,
|
||||
u8 const *data)
|
||||
{
|
||||
struct isku_roccat_report roccat_report;
|
||||
struct isku_report_button const *button_report;
|
||||
|
||||
if (data[0] != ISKU_REPORT_NUMBER_BUTTON)
|
||||
return;
|
||||
|
||||
button_report = (struct isku_report_button const *)data;
|
||||
|
||||
roccat_report.event = button_report->event;
|
||||
roccat_report.data1 = button_report->data1;
|
||||
roccat_report.data2 = button_report->data2;
|
||||
roccat_report.profile = isku->actual_profile + 1;
|
||||
roccat_report_event(isku->chrdev_minor,
|
||||
(uint8_t const *)&roccat_report);
|
||||
}
|
||||
|
||||
static int isku_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 isku_device *isku = hid_get_drvdata(hdev);
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
!= ISKU_USB_INTERFACE_PROTOCOL)
|
||||
return 0;
|
||||
|
||||
if (isku == NULL)
|
||||
return 0;
|
||||
|
||||
isku_keep_values_up_to_date(isku, data);
|
||||
|
||||
if (isku->roccat_claimed)
|
||||
isku_report_to_chrdev(isku, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id isku_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(hid, isku_devices);
|
||||
|
||||
static struct hid_driver isku_driver = {
|
||||
.name = "isku",
|
||||
.id_table = isku_devices,
|
||||
.probe = isku_probe,
|
||||
.remove = isku_remove,
|
||||
.raw_event = isku_raw_event
|
||||
};
|
||||
|
||||
static int __init isku_init(void)
|
||||
{
|
||||
int retval;
|
||||
isku_class = class_create(THIS_MODULE, "isku");
|
||||
if (IS_ERR(isku_class))
|
||||
return PTR_ERR(isku_class);
|
||||
isku_class->dev_attrs = isku_attributes;
|
||||
isku_class->dev_bin_attrs = isku_bin_attributes;
|
||||
|
||||
retval = hid_register_driver(&isku_driver);
|
||||
if (retval)
|
||||
class_destroy(isku_class);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit isku_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&isku_driver);
|
||||
class_destroy(isku_class);
|
||||
}
|
||||
|
||||
module_init(isku_init);
|
||||
module_exit(isku_exit);
|
||||
|
||||
MODULE_AUTHOR("Stefan Achatz");
|
||||
MODULE_DESCRIPTION("USB Roccat Isku driver");
|
||||
MODULE_LICENSE("GPL v2");
|
147
drivers/hid/hid-roccat-isku.h
Normal file
147
drivers/hid/hid-roccat-isku.h
Normal file
@ -0,0 +1,147 @@
|
||||
#ifndef __HID_ROCCAT_ISKU_H
|
||||
#define __HID_ROCCAT_ISKU_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
enum {
|
||||
ISKU_PROFILE_NUM = 5,
|
||||
ISKU_USB_INTERFACE_PROTOCOL = 0,
|
||||
};
|
||||
|
||||
struct isku_control {
|
||||
uint8_t command; /* ISKU_COMMAND_CONTROL */
|
||||
uint8_t value;
|
||||
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 */
|
||||
uint8_t actual_profile;
|
||||
} __packed;
|
||||
|
||||
struct isku_key_mask {
|
||||
uint8_t command; /* ISKU_COMMAND_KEY_MASK */
|
||||
uint8_t size; /* 6 */
|
||||
uint8_t profile_number; /* 0-4 */
|
||||
uint8_t mask;
|
||||
uint16_t checksum;
|
||||
} __packed;
|
||||
|
||||
struct isku_keys_function {
|
||||
uint8_t data[0x29];
|
||||
} __packed;
|
||||
|
||||
struct isku_keys_easyzone {
|
||||
uint8_t data[0x41];
|
||||
} __packed;
|
||||
|
||||
struct isku_keys_media {
|
||||
uint8_t data[0x1d];
|
||||
} __packed;
|
||||
|
||||
struct isku_keys_thumbster {
|
||||
uint8_t data[0x17];
|
||||
} __packed;
|
||||
|
||||
struct isku_keys_macro {
|
||||
uint8_t data[0x23];
|
||||
} __packed;
|
||||
|
||||
struct isku_keys_capslock {
|
||||
uint8_t data[0x6];
|
||||
} __packed;
|
||||
|
||||
struct isku_macro {
|
||||
uint8_t data[0x823];
|
||||
} __packed;
|
||||
|
||||
struct isku_light {
|
||||
uint8_t data[0xa];
|
||||
} __packed;
|
||||
|
||||
struct isku_info {
|
||||
uint8_t data[2];
|
||||
uint8_t firmware_version;
|
||||
uint8_t unknown[3];
|
||||
} __packed;
|
||||
|
||||
struct isku_talk {
|
||||
uint8_t data[0x10];
|
||||
} __packed;
|
||||
|
||||
struct isku_last_set {
|
||||
uint8_t data[0x14];
|
||||
} __packed;
|
||||
|
||||
enum isku_commands {
|
||||
ISKU_COMMAND_CONTROL = 0x4,
|
||||
ISKU_COMMAND_ACTUAL_PROFILE = 0x5,
|
||||
ISKU_COMMAND_KEY_MASK = 0x7,
|
||||
ISKU_COMMAND_KEYS_FUNCTION = 0x8,
|
||||
ISKU_COMMAND_KEYS_EASYZONE = 0x9,
|
||||
ISKU_COMMAND_KEYS_MEDIA = 0xa,
|
||||
ISKU_COMMAND_KEYS_THUMBSTER = 0xb,
|
||||
ISKU_COMMAND_KEYS_MACRO = 0xd,
|
||||
ISKU_COMMAND_MACRO = 0xe,
|
||||
ISKU_COMMAND_INFO = 0xf,
|
||||
ISKU_COMMAND_LIGHT = 0x10,
|
||||
ISKU_COMMAND_KEYS_CAPSLOCK = 0x13,
|
||||
ISKU_COMMAND_LAST_SET = 0x14,
|
||||
ISKU_COMMAND_15 = 0x15,
|
||||
ISKU_COMMAND_TALK = 0x16,
|
||||
ISKU_COMMAND_FIRMWARE_WRITE = 0x1b,
|
||||
ISKU_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c,
|
||||
};
|
||||
|
||||
struct isku_report_button {
|
||||
uint8_t number; /* ISKU_REPORT_NUMBER_BUTTON */
|
||||
uint8_t zero;
|
||||
uint8_t event;
|
||||
uint8_t data1;
|
||||
uint8_t data2;
|
||||
};
|
||||
|
||||
enum isku_report_numbers {
|
||||
ISKU_REPORT_NUMBER_BUTTON = 3,
|
||||
};
|
||||
|
||||
enum isku_report_button_events {
|
||||
ISKU_REPORT_BUTTON_EVENT_PROFILE = 0x2,
|
||||
};
|
||||
|
||||
struct isku_roccat_report {
|
||||
uint8_t event;
|
||||
uint8_t data1;
|
||||
uint8_t data2;
|
||||
uint8_t profile;
|
||||
} __packed;
|
||||
|
||||
struct isku_device {
|
||||
int roccat_claimed;
|
||||
int chrdev_minor;
|
||||
|
||||
struct mutex isku_lock;
|
||||
|
||||
int actual_profile;
|
||||
};
|
||||
|
||||
#endif
|
@ -78,12 +78,10 @@ static int kone_send(struct usb_device *usb_dev, uint usb_command,
|
||||
char *buf;
|
||||
int len;
|
||||
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
buf = kmemdup(data, size, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buf, data, size);
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
HID_REQ_SET_REPORT,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
|
@ -9,6 +9,7 @@
|
||||
* Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
|
||||
* Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
|
||||
* Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
|
||||
* Copyright (c) 2011 Przemysław Firszt <przemo@firszt.eu>
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -33,6 +34,7 @@
|
||||
struct wacom_data {
|
||||
__u16 tool;
|
||||
unsigned char butstate;
|
||||
__u8 features;
|
||||
unsigned char high_speed;
|
||||
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
|
||||
int battery_capacity;
|
||||
@ -107,6 +109,19 @@ static int wacom_ac_get_property(struct power_supply *psy,
|
||||
}
|
||||
#endif
|
||||
|
||||
static void wacom_set_features(struct hid_device *hdev)
|
||||
{
|
||||
int ret;
|
||||
__u8 rep_data[2];
|
||||
|
||||
/*set high speed, tablet mode*/
|
||||
rep_data[0] = 0x03;
|
||||
rep_data[1] = 0x20;
|
||||
ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
|
||||
HID_FEATURE_REPORT);
|
||||
return;
|
||||
}
|
||||
|
||||
static void wacom_poke(struct hid_device *hdev, u8 speed)
|
||||
{
|
||||
struct wacom_data *wdata = hid_get_drvdata(hdev);
|
||||
@ -177,26 +192,13 @@ static ssize_t wacom_store_speed(struct device *dev,
|
||||
static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
wacom_show_speed, wacom_store_speed);
|
||||
|
||||
static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
u8 *raw_data, int size)
|
||||
static int wacom_gr_parse_report(struct hid_device *hdev,
|
||||
struct wacom_data *wdata,
|
||||
struct input_dev *input, unsigned char *data)
|
||||
{
|
||||
struct wacom_data *wdata = hid_get_drvdata(hdev);
|
||||
struct hid_input *hidinput;
|
||||
struct input_dev *input;
|
||||
unsigned char *data = (unsigned char *) raw_data;
|
||||
int tool, x, y, rw;
|
||||
|
||||
if (!(hdev->claimed & HID_CLAIMED_INPUT))
|
||||
return 0;
|
||||
|
||||
tool = 0;
|
||||
hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
|
||||
input = hidinput->input;
|
||||
|
||||
/* Check if this is a tablet report */
|
||||
if (data[0] != 0x03)
|
||||
return 0;
|
||||
|
||||
/* Get X & Y positions */
|
||||
x = le16_to_cpu(*(__le16 *) &data[2]);
|
||||
y = le16_to_cpu(*(__le16 *) &data[4]);
|
||||
@ -304,6 +306,121 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void wacom_i4_parse_pen_report(struct wacom_data *wdata,
|
||||
struct input_dev *input, unsigned char *data)
|
||||
{
|
||||
__u16 x, y, pressure;
|
||||
__u32 id;
|
||||
|
||||
switch (data[1]) {
|
||||
case 0x80: /* Out of proximity report */
|
||||
wdata->tool = 0;
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
input_report_abs(input, ABS_PRESSURE, 0);
|
||||
input_report_key(input, wdata->tool, 0);
|
||||
input_sync(input);
|
||||
break;
|
||||
case 0xC2: /* Tool report */
|
||||
id = ((data[2] << 4) | (data[3] >> 4) |
|
||||
((data[7] & 0x0f) << 20) |
|
||||
((data[8] & 0xf0) << 12)) & 0xfffff;
|
||||
|
||||
switch (id) {
|
||||
case 0x802:
|
||||
wdata->tool = BTN_TOOL_PEN;
|
||||
break;
|
||||
case 0x80A:
|
||||
wdata->tool = BTN_TOOL_RUBBER;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default: /* Position/pressure report */
|
||||
x = data[2] << 9 | data[3] << 1 | ((data[9] & 0x02) >> 1);
|
||||
y = data[4] << 9 | data[5] << 1 | (data[9] & 0x01);
|
||||
pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5)
|
||||
| (data[1] & 0x01);
|
||||
|
||||
input_report_key(input, BTN_TOUCH, pressure > 1);
|
||||
|
||||
input_report_key(input, BTN_STYLUS, data[1] & 0x02);
|
||||
input_report_key(input, BTN_STYLUS2, data[1] & 0x04);
|
||||
input_report_key(input, wdata->tool, 1);
|
||||
input_report_abs(input, ABS_X, x);
|
||||
input_report_abs(input, ABS_Y, y);
|
||||
input_report_abs(input, ABS_PRESSURE, pressure);
|
||||
input_sync(input);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void wacom_i4_parse_report(struct hid_device *hdev,
|
||||
struct wacom_data *wdata,
|
||||
struct input_dev *input, unsigned char *data)
|
||||
{
|
||||
switch (data[0]) {
|
||||
case 0x00: /* Empty report */
|
||||
break;
|
||||
case 0x02: /* Pen report */
|
||||
wacom_i4_parse_pen_report(wdata, input, data);
|
||||
break;
|
||||
case 0x03: /* Features Report */
|
||||
wdata->features = data[2];
|
||||
break;
|
||||
case 0x0C: /* Button report */
|
||||
break;
|
||||
default:
|
||||
hid_err(hdev, "Unknown report: %d,%d\n", data[0], data[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
u8 *raw_data, int size)
|
||||
{
|
||||
struct wacom_data *wdata = hid_get_drvdata(hdev);
|
||||
struct hid_input *hidinput;
|
||||
struct input_dev *input;
|
||||
unsigned char *data = (unsigned char *) raw_data;
|
||||
int i;
|
||||
|
||||
if (!(hdev->claimed & HID_CLAIMED_INPUT))
|
||||
return 0;
|
||||
|
||||
hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
|
||||
input = hidinput->input;
|
||||
|
||||
/* Check if this is a tablet report */
|
||||
if (data[0] != 0x03)
|
||||
return 0;
|
||||
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
|
||||
return wacom_gr_parse_report(hdev, wdata, input, data);
|
||||
break;
|
||||
case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
|
||||
i = 1;
|
||||
|
||||
switch (data[0]) {
|
||||
case 0x04:
|
||||
wacom_i4_parse_report(hdev, wdata, input, data + i);
|
||||
i += 10;
|
||||
/* fall through */
|
||||
case 0x03:
|
||||
wacom_i4_parse_report(hdev, wdata, input, data + i);
|
||||
i += 10;
|
||||
wacom_i4_parse_report(hdev, wdata, input, data + i);
|
||||
break;
|
||||
default:
|
||||
hid_err(hdev, "Unknown report: %d,%d size:%d\n",
|
||||
data[0], data[1], size);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage, unsigned long **bit,
|
||||
int *max)
|
||||
@ -338,10 +455,19 @@ static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
__set_bit(BTN_TOOL_RUBBER, input->keybit);
|
||||
__set_bit(BTN_TOOL_MOUSE, input->keybit);
|
||||
|
||||
input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
|
||||
input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
|
||||
input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
|
||||
input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
|
||||
break;
|
||||
case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
|
||||
input_set_abs_params(input, ABS_X, 0, 40640, 4, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, 25400, 4, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, 2047, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -378,8 +504,16 @@ static int wacom_probe(struct hid_device *hdev,
|
||||
hid_warn(hdev,
|
||||
"can't create sysfs speed attribute err: %d\n", ret);
|
||||
|
||||
/* Set Wacom mode 2 with high reporting speed */
|
||||
wacom_poke(hdev, 1);
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
|
||||
/* Set Wacom mode 2 with high reporting speed */
|
||||
wacom_poke(hdev, 1);
|
||||
break;
|
||||
case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
|
||||
wdata->features = 0;
|
||||
wacom_set_features(hdev);
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
|
||||
wdata->battery.properties = wacom_battery_props;
|
||||
@ -441,6 +575,7 @@ static void wacom_remove(struct hid_device *hdev)
|
||||
|
||||
static const struct hid_device_id wacom_devices[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
|
||||
|
||||
{ }
|
||||
};
|
||||
|
@ -20,91 +20,7 @@
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define WIIMOTE_VERSION "0.2"
|
||||
#define WIIMOTE_NAME "Nintendo Wii Remote"
|
||||
#define WIIMOTE_BUFSIZE 32
|
||||
|
||||
struct wiimote_buf {
|
||||
__u8 data[HID_MAX_BUFFER_SIZE];
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct wiimote_state {
|
||||
spinlock_t lock;
|
||||
__u8 flags;
|
||||
__u8 accel_split[2];
|
||||
|
||||
/* synchronous cmd requests */
|
||||
struct mutex sync;
|
||||
struct completion ready;
|
||||
int cmd;
|
||||
__u32 opt;
|
||||
|
||||
/* results of synchronous requests */
|
||||
__u8 cmd_battery;
|
||||
__u8 cmd_err;
|
||||
};
|
||||
|
||||
struct wiimote_data {
|
||||
struct hid_device *hdev;
|
||||
struct input_dev *input;
|
||||
struct led_classdev *leds[4];
|
||||
struct input_dev *accel;
|
||||
struct input_dev *ir;
|
||||
struct power_supply battery;
|
||||
|
||||
spinlock_t qlock;
|
||||
__u8 head;
|
||||
__u8 tail;
|
||||
struct wiimote_buf outq[WIIMOTE_BUFSIZE];
|
||||
struct work_struct worker;
|
||||
|
||||
struct wiimote_state state;
|
||||
};
|
||||
|
||||
#define WIIPROTO_FLAG_LED1 0x01
|
||||
#define WIIPROTO_FLAG_LED2 0x02
|
||||
#define WIIPROTO_FLAG_LED3 0x04
|
||||
#define WIIPROTO_FLAG_LED4 0x08
|
||||
#define WIIPROTO_FLAG_RUMBLE 0x10
|
||||
#define WIIPROTO_FLAG_ACCEL 0x20
|
||||
#define WIIPROTO_FLAG_IR_BASIC 0x40
|
||||
#define WIIPROTO_FLAG_IR_EXT 0x80
|
||||
#define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */
|
||||
#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
|
||||
WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
|
||||
#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \
|
||||
WIIPROTO_FLAG_IR_FULL)
|
||||
|
||||
/* return flag for led \num */
|
||||
#define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1))
|
||||
|
||||
enum wiiproto_reqs {
|
||||
WIIPROTO_REQ_NULL = 0x0,
|
||||
WIIPROTO_REQ_RUMBLE = 0x10,
|
||||
WIIPROTO_REQ_LED = 0x11,
|
||||
WIIPROTO_REQ_DRM = 0x12,
|
||||
WIIPROTO_REQ_IR1 = 0x13,
|
||||
WIIPROTO_REQ_SREQ = 0x15,
|
||||
WIIPROTO_REQ_WMEM = 0x16,
|
||||
WIIPROTO_REQ_RMEM = 0x17,
|
||||
WIIPROTO_REQ_IR2 = 0x1a,
|
||||
WIIPROTO_REQ_STATUS = 0x20,
|
||||
WIIPROTO_REQ_DATA = 0x21,
|
||||
WIIPROTO_REQ_RETURN = 0x22,
|
||||
WIIPROTO_REQ_DRM_K = 0x30,
|
||||
WIIPROTO_REQ_DRM_KA = 0x31,
|
||||
WIIPROTO_REQ_DRM_KE = 0x32,
|
||||
WIIPROTO_REQ_DRM_KAI = 0x33,
|
||||
WIIPROTO_REQ_DRM_KEE = 0x34,
|
||||
WIIPROTO_REQ_DRM_KAE = 0x35,
|
||||
WIIPROTO_REQ_DRM_KIE = 0x36,
|
||||
WIIPROTO_REQ_DRM_KAIE = 0x37,
|
||||
WIIPROTO_REQ_DRM_E = 0x3d,
|
||||
WIIPROTO_REQ_DRM_SKAI1 = 0x3e,
|
||||
WIIPROTO_REQ_DRM_SKAI2 = 0x3f,
|
||||
};
|
||||
#include "hid-wiimote.h"
|
||||
|
||||
enum wiiproto_keys {
|
||||
WIIPROTO_KEY_LEFT,
|
||||
@ -139,52 +55,6 @@ static enum power_supply_property wiimote_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_CAPACITY
|
||||
};
|
||||
|
||||
/* requires the state.lock spinlock to be held */
|
||||
static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd,
|
||||
__u32 opt)
|
||||
{
|
||||
return wdata->state.cmd == cmd && wdata->state.opt == opt;
|
||||
}
|
||||
|
||||
/* requires the state.lock spinlock to be held */
|
||||
static inline void wiimote_cmd_complete(struct wiimote_data *wdata)
|
||||
{
|
||||
wdata->state.cmd = WIIPROTO_REQ_NULL;
|
||||
complete(&wdata->state.ready);
|
||||
}
|
||||
|
||||
static inline int wiimote_cmd_acquire(struct wiimote_data *wdata)
|
||||
{
|
||||
return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0;
|
||||
}
|
||||
|
||||
/* requires the state.lock spinlock to be held */
|
||||
static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd,
|
||||
__u32 opt)
|
||||
{
|
||||
INIT_COMPLETION(wdata->state.ready);
|
||||
wdata->state.cmd = cmd;
|
||||
wdata->state.opt = opt;
|
||||
}
|
||||
|
||||
static inline void wiimote_cmd_release(struct wiimote_data *wdata)
|
||||
{
|
||||
mutex_unlock(&wdata->state.sync);
|
||||
}
|
||||
|
||||
static inline int wiimote_cmd_wait(struct wiimote_data *wdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ);
|
||||
if (ret < 0)
|
||||
return -ERESTARTSYS;
|
||||
else if (ret == 0)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
|
||||
size_t count)
|
||||
{
|
||||
@ -329,6 +199,7 @@ static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
|
||||
static __u8 select_drm(struct wiimote_data *wdata)
|
||||
{
|
||||
__u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR;
|
||||
bool ext = wiiext_active(wdata);
|
||||
|
||||
if (ir == WIIPROTO_FLAG_IR_BASIC) {
|
||||
if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
|
||||
@ -340,14 +211,21 @@ static __u8 select_drm(struct wiimote_data *wdata)
|
||||
} else if (ir == WIIPROTO_FLAG_IR_FULL) {
|
||||
return WIIPROTO_REQ_DRM_SKAI1;
|
||||
} else {
|
||||
if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
|
||||
return WIIPROTO_REQ_DRM_KA;
|
||||
else
|
||||
return WIIPROTO_REQ_DRM_K;
|
||||
if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) {
|
||||
if (ext)
|
||||
return WIIPROTO_REQ_DRM_KAE;
|
||||
else
|
||||
return WIIPROTO_REQ_DRM_KA;
|
||||
} else {
|
||||
if (ext)
|
||||
return WIIPROTO_REQ_DRM_KE;
|
||||
else
|
||||
return WIIPROTO_REQ_DRM_K;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
|
||||
void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
|
||||
{
|
||||
__u8 cmd[3];
|
||||
|
||||
@ -358,6 +236,7 @@ static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
|
||||
cmd[1] = 0;
|
||||
cmd[2] = drm;
|
||||
|
||||
wdata->state.drm = drm;
|
||||
wiiproto_keep_rumble(wdata, &cmd[1]);
|
||||
wiimote_queue(wdata, cmd, sizeof(cmd));
|
||||
}
|
||||
@ -440,8 +319,33 @@ static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom,
|
||||
wiimote_queue(wdata, cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom, __u32 offset,
|
||||
__u16 size)
|
||||
{
|
||||
__u8 cmd[7];
|
||||
|
||||
if (size == 0) {
|
||||
hid_warn(wdata->hdev, "Invalid length %d rmem request\n", size);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd[0] = WIIPROTO_REQ_RMEM;
|
||||
cmd[1] = 0;
|
||||
cmd[2] = (offset >> 16) & 0xff;
|
||||
cmd[3] = (offset >> 8) & 0xff;
|
||||
cmd[4] = offset & 0xff;
|
||||
cmd[5] = (size >> 8) & 0xff;
|
||||
cmd[6] = size & 0xff;
|
||||
|
||||
if (!eeprom)
|
||||
cmd[1] |= 0x04;
|
||||
|
||||
wiiproto_keep_rumble(wdata, &cmd[1]);
|
||||
wiimote_queue(wdata, cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
/* requries the cmd-mutex to be held */
|
||||
static int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
|
||||
int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
|
||||
const __u8 *wmem, __u8 size)
|
||||
{
|
||||
unsigned long flags;
|
||||
@ -459,6 +363,36 @@ static int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* requries the cmd-mutex to be held */
|
||||
ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, __u8 *rmem,
|
||||
__u8 size)
|
||||
{
|
||||
unsigned long flags;
|
||||
ssize_t ret;
|
||||
|
||||
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||
wdata->state.cmd_read_size = size;
|
||||
wdata->state.cmd_read_buf = rmem;
|
||||
wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, offset & 0xffff);
|
||||
wiiproto_req_rreg(wdata, offset, size);
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||
|
||||
ret = wiimote_cmd_wait(wdata);
|
||||
|
||||
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||
wdata->state.cmd_read_buf = NULL;
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||
|
||||
if (!ret) {
|
||||
if (wdata->state.cmd_read_size == 0)
|
||||
ret = -EIO;
|
||||
else
|
||||
ret = wdata->state.cmd_read_size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wiimote_battery_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
@ -862,6 +796,8 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
|
||||
/* on status reports the drm is reset so we need to resend the drm */
|
||||
wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
|
||||
|
||||
wiiext_event(wdata, payload[2] & 0x02);
|
||||
|
||||
if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) {
|
||||
wdata->state.cmd_battery = payload[5];
|
||||
wiimote_cmd_complete(wdata);
|
||||
@ -870,7 +806,23 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
|
||||
|
||||
static void handler_data(struct wiimote_data *wdata, const __u8 *payload)
|
||||
{
|
||||
__u16 offset = payload[3] << 8 | payload[4];
|
||||
__u8 size = (payload[2] >> 4) + 1;
|
||||
__u8 err = payload[2] & 0x0f;
|
||||
|
||||
handler_keys(wdata, payload);
|
||||
|
||||
if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_RMEM, offset)) {
|
||||
if (err)
|
||||
size = 0;
|
||||
else if (size > wdata->state.cmd_read_size)
|
||||
size = wdata->state.cmd_read_size;
|
||||
|
||||
wdata->state.cmd_read_size = size;
|
||||
if (wdata->state.cmd_read_buf)
|
||||
memcpy(wdata->state.cmd_read_buf, &payload[5], size);
|
||||
wiimote_cmd_complete(wdata);
|
||||
}
|
||||
}
|
||||
|
||||
static void handler_return(struct wiimote_data *wdata, const __u8 *payload)
|
||||
@ -898,6 +850,7 @@ static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload)
|
||||
static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload)
|
||||
{
|
||||
handler_keys(wdata, payload);
|
||||
wiiext_handle(wdata, &payload[2]);
|
||||
}
|
||||
|
||||
static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
|
||||
@ -914,6 +867,7 @@ static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
|
||||
static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload)
|
||||
{
|
||||
handler_keys(wdata, payload);
|
||||
wiiext_handle(wdata, &payload[2]);
|
||||
}
|
||||
|
||||
static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
|
||||
@ -924,12 +878,14 @@ static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
|
||||
ir_to_input2(wdata, &payload[7], false);
|
||||
ir_to_input3(wdata, &payload[9], true);
|
||||
input_sync(wdata->ir);
|
||||
wiiext_handle(wdata, &payload[12]);
|
||||
}
|
||||
|
||||
static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload)
|
||||
{
|
||||
handler_keys(wdata, payload);
|
||||
handler_accel(wdata, payload);
|
||||
wiiext_handle(wdata, &payload[5]);
|
||||
}
|
||||
|
||||
static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
|
||||
@ -941,10 +897,12 @@ static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
|
||||
ir_to_input2(wdata, &payload[10], false);
|
||||
ir_to_input3(wdata, &payload[12], true);
|
||||
input_sync(wdata->ir);
|
||||
wiiext_handle(wdata, &payload[15]);
|
||||
}
|
||||
|
||||
static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload)
|
||||
{
|
||||
wiiext_handle(wdata, payload);
|
||||
}
|
||||
|
||||
static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
|
||||
@ -1182,6 +1140,7 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
|
||||
spin_lock_init(&wdata->state.lock);
|
||||
init_completion(&wdata->state.ready);
|
||||
mutex_init(&wdata->state.sync);
|
||||
wdata->state.drm = WIIPROTO_REQ_DRM_K;
|
||||
|
||||
return wdata;
|
||||
|
||||
@ -1196,6 +1155,8 @@ err:
|
||||
|
||||
static void wiimote_destroy(struct wiimote_data *wdata)
|
||||
{
|
||||
wiidebug_deinit(wdata);
|
||||
wiiext_deinit(wdata);
|
||||
wiimote_leds_destroy(wdata);
|
||||
|
||||
power_supply_unregister(&wdata->battery);
|
||||
@ -1214,6 +1175,8 @@ static int wiimote_hid_probe(struct hid_device *hdev,
|
||||
struct wiimote_data *wdata;
|
||||
int ret;
|
||||
|
||||
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
|
||||
|
||||
wdata = wiimote_create(hdev);
|
||||
if (!wdata) {
|
||||
hid_err(hdev, "Can't alloc device\n");
|
||||
@ -1267,6 +1230,14 @@ static int wiimote_hid_probe(struct hid_device *hdev,
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
ret = wiiext_init(wdata);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
ret = wiidebug_init(wdata);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
hid_info(hdev, "New device registered\n");
|
||||
|
||||
/* by default set led1 after device initialization */
|
||||
@ -1343,4 +1314,3 @@ module_exit(wiimote_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
|
||||
MODULE_DESCRIPTION(WIIMOTE_NAME " Device Driver");
|
||||
MODULE_VERSION(WIIMOTE_VERSION);
|
227
drivers/hid/hid-wiimote-debug.c
Normal file
227
drivers/hid/hid-wiimote-debug.c
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Debug support for HID Nintendo Wiimote devices
|
||||
* Copyright (c) 2011 David Herrmann
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/debugfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "hid-wiimote.h"
|
||||
|
||||
struct wiimote_debug {
|
||||
struct wiimote_data *wdata;
|
||||
struct dentry *eeprom;
|
||||
struct dentry *drm;
|
||||
};
|
||||
|
||||
static int wiidebug_eeprom_open(struct inode *i, struct file *f)
|
||||
{
|
||||
f->private_data = i->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t wiidebug_eeprom_read(struct file *f, char __user *u, size_t s,
|
||||
loff_t *off)
|
||||
{
|
||||
struct wiimote_debug *dbg = f->private_data;
|
||||
struct wiimote_data *wdata = dbg->wdata;
|
||||
unsigned long flags;
|
||||
ssize_t ret;
|
||||
char buf[16];
|
||||
__u16 size;
|
||||
|
||||
if (s == 0)
|
||||
return -EINVAL;
|
||||
if (*off > 0xffffff)
|
||||
return 0;
|
||||
if (s > 16)
|
||||
s = 16;
|
||||
|
||||
ret = wiimote_cmd_acquire(wdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||
wdata->state.cmd_read_size = s;
|
||||
wdata->state.cmd_read_buf = buf;
|
||||
wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, *off & 0xffff);
|
||||
wiiproto_req_reeprom(wdata, *off, s);
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||
|
||||
ret = wiimote_cmd_wait(wdata);
|
||||
if (!ret)
|
||||
size = wdata->state.cmd_read_size;
|
||||
|
||||
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||
wdata->state.cmd_read_buf = NULL;
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||
|
||||
wiimote_cmd_release(wdata);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
else if (size == 0)
|
||||
return -EIO;
|
||||
|
||||
if (copy_to_user(u, buf, size))
|
||||
return -EFAULT;
|
||||
|
||||
*off += size;
|
||||
ret = size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations wiidebug_eeprom_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = wiidebug_eeprom_open,
|
||||
.read = wiidebug_eeprom_read,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static const char *wiidebug_drmmap[] = {
|
||||
[WIIPROTO_REQ_NULL] = "NULL",
|
||||
[WIIPROTO_REQ_DRM_K] = "K",
|
||||
[WIIPROTO_REQ_DRM_KA] = "KA",
|
||||
[WIIPROTO_REQ_DRM_KE] = "KE",
|
||||
[WIIPROTO_REQ_DRM_KAI] = "KAI",
|
||||
[WIIPROTO_REQ_DRM_KEE] = "KEE",
|
||||
[WIIPROTO_REQ_DRM_KAE] = "KAE",
|
||||
[WIIPROTO_REQ_DRM_KIE] = "KIE",
|
||||
[WIIPROTO_REQ_DRM_KAIE] = "KAIE",
|
||||
[WIIPROTO_REQ_DRM_E] = "E",
|
||||
[WIIPROTO_REQ_DRM_SKAI1] = "SKAI1",
|
||||
[WIIPROTO_REQ_DRM_SKAI2] = "SKAI2",
|
||||
[WIIPROTO_REQ_MAX] = NULL
|
||||
};
|
||||
|
||||
static int wiidebug_drm_show(struct seq_file *f, void *p)
|
||||
{
|
||||
struct wiimote_debug *dbg = f->private;
|
||||
const char *str = NULL;
|
||||
unsigned long flags;
|
||||
__u8 drm;
|
||||
|
||||
spin_lock_irqsave(&dbg->wdata->state.lock, flags);
|
||||
drm = dbg->wdata->state.drm;
|
||||
spin_unlock_irqrestore(&dbg->wdata->state.lock, flags);
|
||||
|
||||
if (drm < WIIPROTO_REQ_MAX)
|
||||
str = wiidebug_drmmap[drm];
|
||||
if (!str)
|
||||
str = "unknown";
|
||||
|
||||
seq_printf(f, "%s\n", str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wiidebug_drm_open(struct inode *i, struct file *f)
|
||||
{
|
||||
return single_open(f, wiidebug_drm_show, i->i_private);
|
||||
}
|
||||
|
||||
static ssize_t wiidebug_drm_write(struct file *f, const char __user *u,
|
||||
size_t s, loff_t *off)
|
||||
{
|
||||
struct wiimote_debug *dbg = f->private_data;
|
||||
unsigned long flags;
|
||||
char buf[16];
|
||||
ssize_t len;
|
||||
int i;
|
||||
|
||||
if (s == 0)
|
||||
return -EINVAL;
|
||||
|
||||
len = min((size_t) 15, s);
|
||||
if (copy_from_user(buf, u, len))
|
||||
return -EFAULT;
|
||||
|
||||
buf[15] = 0;
|
||||
|
||||
for (i = 0; i < WIIPROTO_REQ_MAX; ++i) {
|
||||
if (!wiidebug_drmmap[i])
|
||||
continue;
|
||||
if (!strcasecmp(buf, wiidebug_drmmap[i]))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == WIIPROTO_REQ_MAX)
|
||||
i = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
spin_lock_irqsave(&dbg->wdata->state.lock, flags);
|
||||
wiiproto_req_drm(dbg->wdata, (__u8) i);
|
||||
spin_unlock_irqrestore(&dbg->wdata->state.lock, flags);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct file_operations wiidebug_drm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = wiidebug_drm_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.write = wiidebug_drm_write,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
int wiidebug_init(struct wiimote_data *wdata)
|
||||
{
|
||||
struct wiimote_debug *dbg;
|
||||
unsigned long flags;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
|
||||
if (!dbg)
|
||||
return -ENOMEM;
|
||||
|
||||
dbg->wdata = wdata;
|
||||
|
||||
dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR,
|
||||
dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops);
|
||||
if (!dbg->eeprom)
|
||||
goto err;
|
||||
|
||||
dbg->drm = debugfs_create_file("drm", S_IRUSR,
|
||||
dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops);
|
||||
if (!dbg->drm)
|
||||
goto err_drm;
|
||||
|
||||
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||
wdata->debug = dbg;
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
err_drm:
|
||||
debugfs_remove(dbg->eeprom);
|
||||
err:
|
||||
kfree(dbg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void wiidebug_deinit(struct wiimote_data *wdata)
|
||||
{
|
||||
struct wiimote_debug *dbg = wdata->debug;
|
||||
unsigned long flags;
|
||||
|
||||
if (!dbg)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||
wdata->debug = NULL;
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||
|
||||
debugfs_remove(dbg->drm);
|
||||
debugfs_remove(dbg->eeprom);
|
||||
kfree(dbg);
|
||||
}
|
752
drivers/hid/hid-wiimote-ext.c
Normal file
752
drivers/hid/hid-wiimote-ext.c
Normal file
@ -0,0 +1,752 @@
|
||||
/*
|
||||
* HID driver for Nintendo Wiimote extension devices
|
||||
* Copyright (c) 2011 David Herrmann
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/atomic.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "hid-wiimote.h"
|
||||
|
||||
struct wiimote_ext {
|
||||
struct wiimote_data *wdata;
|
||||
struct work_struct worker;
|
||||
struct input_dev *input;
|
||||
struct input_dev *mp_input;
|
||||
|
||||
atomic_t opened;
|
||||
atomic_t mp_opened;
|
||||
bool plugged;
|
||||
bool mp_plugged;
|
||||
bool motionp;
|
||||
__u8 ext_type;
|
||||
};
|
||||
|
||||
enum wiiext_type {
|
||||
WIIEXT_NONE, /* placeholder */
|
||||
WIIEXT_CLASSIC, /* Nintendo classic controller */
|
||||
WIIEXT_NUNCHUCK, /* Nintendo nunchuck controller */
|
||||
};
|
||||
|
||||
enum wiiext_keys {
|
||||
WIIEXT_KEY_C,
|
||||
WIIEXT_KEY_Z,
|
||||
WIIEXT_KEY_A,
|
||||
WIIEXT_KEY_B,
|
||||
WIIEXT_KEY_X,
|
||||
WIIEXT_KEY_Y,
|
||||
WIIEXT_KEY_ZL,
|
||||
WIIEXT_KEY_ZR,
|
||||
WIIEXT_KEY_PLUS,
|
||||
WIIEXT_KEY_MINUS,
|
||||
WIIEXT_KEY_HOME,
|
||||
WIIEXT_KEY_LEFT,
|
||||
WIIEXT_KEY_RIGHT,
|
||||
WIIEXT_KEY_UP,
|
||||
WIIEXT_KEY_DOWN,
|
||||
WIIEXT_KEY_LT,
|
||||
WIIEXT_KEY_RT,
|
||||
WIIEXT_KEY_COUNT
|
||||
};
|
||||
|
||||
static __u16 wiiext_keymap[] = {
|
||||
BTN_C, /* WIIEXT_KEY_C */
|
||||
BTN_Z, /* WIIEXT_KEY_Z */
|
||||
BTN_A, /* WIIEXT_KEY_A */
|
||||
BTN_B, /* WIIEXT_KEY_B */
|
||||
BTN_X, /* WIIEXT_KEY_X */
|
||||
BTN_Y, /* WIIEXT_KEY_Y */
|
||||
BTN_TL2, /* WIIEXT_KEY_ZL */
|
||||
BTN_TR2, /* WIIEXT_KEY_ZR */
|
||||
KEY_NEXT, /* WIIEXT_KEY_PLUS */
|
||||
KEY_PREVIOUS, /* WIIEXT_KEY_MINUS */
|
||||
BTN_MODE, /* WIIEXT_KEY_HOME */
|
||||
KEY_LEFT, /* WIIEXT_KEY_LEFT */
|
||||
KEY_RIGHT, /* WIIEXT_KEY_RIGHT */
|
||||
KEY_UP, /* WIIEXT_KEY_UP */
|
||||
KEY_DOWN, /* WIIEXT_KEY_DOWN */
|
||||
BTN_TL, /* WIIEXT_KEY_LT */
|
||||
BTN_TR, /* WIIEXT_KEY_RT */
|
||||
};
|
||||
|
||||
/* diable all extensions */
|
||||
static void ext_disable(struct wiimote_ext *ext)
|
||||
{
|
||||
unsigned long flags;
|
||||
__u8 wmem = 0x55;
|
||||
|
||||
if (!wiimote_cmd_acquire(ext->wdata)) {
|
||||
wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem));
|
||||
wiimote_cmd_release(ext->wdata);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ext->wdata->state.lock, flags);
|
||||
ext->motionp = false;
|
||||
ext->ext_type = WIIEXT_NONE;
|
||||
wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL);
|
||||
spin_unlock_irqrestore(&ext->wdata->state.lock, flags);
|
||||
}
|
||||
|
||||
static bool motionp_read(struct wiimote_ext *ext)
|
||||
{
|
||||
__u8 rmem[2], wmem;
|
||||
ssize_t ret;
|
||||
bool avail = false;
|
||||
|
||||
if (!atomic_read(&ext->mp_opened))
|
||||
return false;
|
||||
|
||||
if (wiimote_cmd_acquire(ext->wdata))
|
||||
return false;
|
||||
|
||||
/* initialize motion plus */
|
||||
wmem = 0x55;
|
||||
ret = wiimote_cmd_write(ext->wdata, 0xa600f0, &wmem, sizeof(wmem));
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
/* read motion plus ID */
|
||||
ret = wiimote_cmd_read(ext->wdata, 0xa600fe, rmem, 2);
|
||||
if (ret == 2 || rmem[1] == 0x5)
|
||||
avail = true;
|
||||
|
||||
error:
|
||||
wiimote_cmd_release(ext->wdata);
|
||||
return avail;
|
||||
}
|
||||
|
||||
static __u8 ext_read(struct wiimote_ext *ext)
|
||||
{
|
||||
ssize_t ret;
|
||||
__u8 rmem[2], wmem;
|
||||
__u8 type = WIIEXT_NONE;
|
||||
|
||||
if (!ext->plugged || !atomic_read(&ext->opened))
|
||||
return WIIEXT_NONE;
|
||||
|
||||
if (wiimote_cmd_acquire(ext->wdata))
|
||||
return WIIEXT_NONE;
|
||||
|
||||
/* initialize extension */
|
||||
wmem = 0x55;
|
||||
ret = wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem));
|
||||
if (!ret) {
|
||||
/* disable encryption */
|
||||
wmem = 0x0;
|
||||
wiimote_cmd_write(ext->wdata, 0xa400fb, &wmem, sizeof(wmem));
|
||||
}
|
||||
|
||||
/* read extension ID */
|
||||
ret = wiimote_cmd_read(ext->wdata, 0xa400fe, rmem, 2);
|
||||
if (ret == 2) {
|
||||
if (rmem[0] == 0 && rmem[1] == 0)
|
||||
type = WIIEXT_NUNCHUCK;
|
||||
else if (rmem[0] == 0x01 && rmem[1] == 0x01)
|
||||
type = WIIEXT_CLASSIC;
|
||||
}
|
||||
|
||||
wiimote_cmd_release(ext->wdata);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static void ext_enable(struct wiimote_ext *ext, bool motionp, __u8 ext_type)
|
||||
{
|
||||
unsigned long flags;
|
||||
__u8 wmem;
|
||||
int ret;
|
||||
|
||||
if (motionp) {
|
||||
if (wiimote_cmd_acquire(ext->wdata))
|
||||
return;
|
||||
|
||||
if (ext_type == WIIEXT_CLASSIC)
|
||||
wmem = 0x07;
|
||||
else if (ext_type == WIIEXT_NUNCHUCK)
|
||||
wmem = 0x05;
|
||||
else
|
||||
wmem = 0x04;
|
||||
|
||||
ret = wiimote_cmd_write(ext->wdata, 0xa600fe, &wmem, sizeof(wmem));
|
||||
wiimote_cmd_release(ext->wdata);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ext->wdata->state.lock, flags);
|
||||
ext->motionp = motionp;
|
||||
ext->ext_type = ext_type;
|
||||
wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL);
|
||||
spin_unlock_irqrestore(&ext->wdata->state.lock, flags);
|
||||
}
|
||||
|
||||
static void wiiext_worker(struct work_struct *work)
|
||||
{
|
||||
struct wiimote_ext *ext = container_of(work, struct wiimote_ext,
|
||||
worker);
|
||||
bool motionp;
|
||||
__u8 ext_type;
|
||||
|
||||
ext_disable(ext);
|
||||
motionp = motionp_read(ext);
|
||||
ext_type = ext_read(ext);
|
||||
ext_enable(ext, motionp, ext_type);
|
||||
}
|
||||
|
||||
/* schedule work only once, otherwise mark for reschedule */
|
||||
static void wiiext_schedule(struct wiimote_ext *ext)
|
||||
{
|
||||
queue_work(system_nrt_wq, &ext->worker);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reacts on extension port events
|
||||
* Whenever the driver gets an event from the wiimote that an extension has been
|
||||
* plugged or unplugged, this funtion shall be called. It checks what extensions
|
||||
* are connected and initializes and activates them.
|
||||
* This can be called in atomic context. The initialization is done in a
|
||||
* separate worker thread. The state.lock spinlock must be held by the caller.
|
||||
*/
|
||||
void wiiext_event(struct wiimote_data *wdata, bool plugged)
|
||||
{
|
||||
if (!wdata->ext)
|
||||
return;
|
||||
|
||||
if (wdata->ext->plugged == plugged)
|
||||
return;
|
||||
|
||||
wdata->ext->plugged = plugged;
|
||||
|
||||
if (!plugged)
|
||||
wdata->ext->mp_plugged = false;
|
||||
|
||||
/*
|
||||
* We need to call wiiext_schedule(wdata->ext) here, however, the
|
||||
* extension initialization logic is not fully understood and so
|
||||
* automatic initialization is not supported, yet.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the current DRM mode should contain extension data and false
|
||||
* if there is no interest in extension data.
|
||||
* All supported extensions send 6 byte extension data so any DRM that contains
|
||||
* extension bytes is fine.
|
||||
* The caller must hold the state.lock spinlock.
|
||||
*/
|
||||
bool wiiext_active(struct wiimote_data *wdata)
|
||||
{
|
||||
if (!wdata->ext)
|
||||
return false;
|
||||
|
||||
return wdata->ext->motionp || wdata->ext->ext_type;
|
||||
}
|
||||
|
||||
static void handler_motionp(struct wiimote_ext *ext, const __u8 *payload)
|
||||
{
|
||||
__s32 x, y, z;
|
||||
bool plugged;
|
||||
|
||||
/* | 8 7 6 5 4 3 | 2 | 1 |
|
||||
* -----+------------------------------+-----+-----+
|
||||
* 1 | Yaw Speed <7:0> |
|
||||
* 2 | Roll Speed <7:0> |
|
||||
* 3 | Pitch Speed <7:0> |
|
||||
* -----+------------------------------+-----+-----+
|
||||
* 4 | Yaw Speed <13:8> | Yaw |Pitch|
|
||||
* -----+------------------------------+-----+-----+
|
||||
* 5 | Roll Speed <13:8> |Roll | Ext |
|
||||
* -----+------------------------------+-----+-----+
|
||||
* 6 | Pitch Speed <13:8> | 1 | 0 |
|
||||
* -----+------------------------------+-----+-----+
|
||||
* The single bits Yaw, Roll, Pitch in the lower right corner specify
|
||||
* whether the wiimote is rotating fast (0) or slow (1). Speed for slow
|
||||
* roation is 440 deg/s and for fast rotation 2000 deg/s. To get a
|
||||
* linear scale we multiply by 2000/440 = ~4.5454 which is 18 for fast
|
||||
* and 9 for slow.
|
||||
* If the wiimote is not rotating the sensor reports 2^13 = 8192.
|
||||
* Ext specifies whether an extension is connected to the motionp.
|
||||
*/
|
||||
|
||||
x = payload[0];
|
||||
y = payload[1];
|
||||
z = payload[2];
|
||||
|
||||
x |= (((__u16)payload[3]) << 6) & 0xff00;
|
||||
y |= (((__u16)payload[4]) << 6) & 0xff00;
|
||||
z |= (((__u16)payload[5]) << 6) & 0xff00;
|
||||
|
||||
x -= 8192;
|
||||
y -= 8192;
|
||||
z -= 8192;
|
||||
|
||||
if (!(payload[3] & 0x02))
|
||||
x *= 18;
|
||||
else
|
||||
x *= 9;
|
||||
if (!(payload[4] & 0x02))
|
||||
y *= 18;
|
||||
else
|
||||
y *= 9;
|
||||
if (!(payload[3] & 0x01))
|
||||
z *= 18;
|
||||
else
|
||||
z *= 9;
|
||||
|
||||
input_report_abs(ext->mp_input, ABS_RX, x);
|
||||
input_report_abs(ext->mp_input, ABS_RY, y);
|
||||
input_report_abs(ext->mp_input, ABS_RZ, z);
|
||||
input_sync(ext->mp_input);
|
||||
|
||||
plugged = payload[5] & 0x01;
|
||||
if (plugged != ext->mp_plugged)
|
||||
ext->mp_plugged = plugged;
|
||||
}
|
||||
|
||||
static void handler_nunchuck(struct wiimote_ext *ext, const __u8 *payload)
|
||||
{
|
||||
__s16 x, y, z, bx, by;
|
||||
|
||||
/* Byte | 8 7 | 6 5 | 4 3 | 2 | 1 |
|
||||
* -----+----------+---------+---------+----+-----+
|
||||
* 1 | Button X <7:0> |
|
||||
* 2 | Button Y <7:0> |
|
||||
* -----+----------+---------+---------+----+-----+
|
||||
* 3 | Speed X <9:2> |
|
||||
* 4 | Speed Y <9:2> |
|
||||
* 5 | Speed Z <9:2> |
|
||||
* -----+----------+---------+---------+----+-----+
|
||||
* 6 | Z <1:0> | Y <1:0> | X <1:0> | BC | BZ |
|
||||
* -----+----------+---------+---------+----+-----+
|
||||
* Button X/Y is the analog stick. Speed X, Y and Z are the
|
||||
* accelerometer data in the same format as the wiimote's accelerometer.
|
||||
* The 6th byte contains the LSBs of the accelerometer data.
|
||||
* BC and BZ are the C and Z buttons: 0 means pressed
|
||||
*
|
||||
* If reported interleaved with motionp, then the layout changes. The
|
||||
* 5th and 6th byte changes to:
|
||||
* -----+-----------------------------------+-----+
|
||||
* 5 | Speed Z <9:3> | EXT |
|
||||
* -----+--------+-----+-----+----+----+----+-----+
|
||||
* 6 |Z <2:1> |Y <1>|X <1>| BC | BZ | 0 | 0 |
|
||||
* -----+--------+-----+-----+----+----+----+-----+
|
||||
* All three accelerometer values lose their LSB. The other data is
|
||||
* still available but slightly moved.
|
||||
*
|
||||
* Center data for button values is 128. Center value for accelerometer
|
||||
* values it 512 / 0x200
|
||||
*/
|
||||
|
||||
bx = payload[0];
|
||||
by = payload[1];
|
||||
bx -= 128;
|
||||
by -= 128;
|
||||
|
||||
x = payload[2] << 2;
|
||||
y = payload[3] << 2;
|
||||
z = payload[4] << 2;
|
||||
|
||||
if (ext->motionp) {
|
||||
x |= (payload[5] >> 3) & 0x02;
|
||||
y |= (payload[5] >> 4) & 0x02;
|
||||
z &= ~0x4;
|
||||
z |= (payload[5] >> 5) & 0x06;
|
||||
} else {
|
||||
x |= (payload[5] >> 2) & 0x03;
|
||||
y |= (payload[5] >> 4) & 0x03;
|
||||
z |= (payload[5] >> 6) & 0x03;
|
||||
}
|
||||
|
||||
x -= 0x200;
|
||||
y -= 0x200;
|
||||
z -= 0x200;
|
||||
|
||||
input_report_abs(ext->input, ABS_HAT0X, bx);
|
||||
input_report_abs(ext->input, ABS_HAT0Y, by);
|
||||
|
||||
input_report_abs(ext->input, ABS_RX, x);
|
||||
input_report_abs(ext->input, ABS_RY, y);
|
||||
input_report_abs(ext->input, ABS_RZ, z);
|
||||
|
||||
if (ext->motionp) {
|
||||
input_report_key(ext->input,
|
||||
wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x04));
|
||||
input_report_key(ext->input,
|
||||
wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x08));
|
||||
} else {
|
||||
input_report_key(ext->input,
|
||||
wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x01));
|
||||
input_report_key(ext->input,
|
||||
wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x02));
|
||||
}
|
||||
|
||||
input_sync(ext->input);
|
||||
}
|
||||
|
||||
static void handler_classic(struct wiimote_ext *ext, const __u8 *payload)
|
||||
{
|
||||
__s8 rx, ry, lx, ly, lt, rt;
|
||||
|
||||
/* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
|
||||
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
* 1 | RX <5:4> | LX <5:0> |
|
||||
* 2 | RX <3:2> | LY <5:0> |
|
||||
* -----+-----+-----+-----+-----------------------------+
|
||||
* 3 |RX<1>| LT <5:4> | RY <5:1> |
|
||||
* -----+-----+-----------+-----------------------------+
|
||||
* 4 | LT <3:1> | RT <5:1> |
|
||||
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
* 5 | BDR | BDD | BLT | B- | BH | B+ | BRT | 1 |
|
||||
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
* 6 | BZL | BB | BY | BA | BX | BZR | BDL | BDU |
|
||||
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
* All buttons are 0 if pressed
|
||||
* RX and RY are right analog stick
|
||||
* LX and LY are left analog stick
|
||||
* LT is left trigger, RT is right trigger
|
||||
* BLT is 0 if left trigger is fully pressed
|
||||
* BRT is 0 if right trigger is fully pressed
|
||||
* BDR, BDD, BDL, BDU form the D-Pad with right, down, left, up buttons
|
||||
* BZL is left Z button and BZR is right Z button
|
||||
* B-, BH, B+ are +, HOME and - buttons
|
||||
* BB, BY, BA, BX are A, B, X, Y buttons
|
||||
* LSB of RX, RY, LT, and RT are not transmitted and always 0.
|
||||
*
|
||||
* With motionp enabled it changes slightly to this:
|
||||
* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
|
||||
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
* 1 | RX <4:3> | LX <5:1> | BDU |
|
||||
* 2 | RX <2:1> | LY <5:1> | BDL |
|
||||
* -----+-----+-----+-----+-----------------------+-----+
|
||||
* 3 |RX<0>| LT <4:3> | RY <4:0> |
|
||||
* -----+-----+-----------+-----------------------------+
|
||||
* 4 | LT <2:0> | RT <4:0> |
|
||||
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
* 5 | BDR | BDD | BLT | B- | BH | B+ | BRT | EXT |
|
||||
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
* 6 | BZL | BB | BY | BA | BX | BZR | 0 | 0 |
|
||||
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
* Only the LSBs of LX and LY are lost. BDU and BDL are moved, the rest
|
||||
* is the same as before.
|
||||
*/
|
||||
|
||||
if (ext->motionp) {
|
||||
lx = payload[0] & 0x3e;
|
||||
ly = payload[0] & 0x3e;
|
||||
} else {
|
||||
lx = payload[0] & 0x3f;
|
||||
ly = payload[0] & 0x3f;
|
||||
}
|
||||
|
||||
rx = (payload[0] >> 3) & 0x14;
|
||||
rx |= (payload[1] >> 5) & 0x06;
|
||||
rx |= (payload[2] >> 7) & 0x01;
|
||||
ry = payload[2] & 0x1f;
|
||||
|
||||
rt = payload[3] & 0x1f;
|
||||
lt = (payload[2] >> 2) & 0x18;
|
||||
lt |= (payload[3] >> 5) & 0x07;
|
||||
|
||||
rx <<= 1;
|
||||
ry <<= 1;
|
||||
rt <<= 1;
|
||||
lt <<= 1;
|
||||
|
||||
input_report_abs(ext->input, ABS_HAT1X, lx - 0x20);
|
||||
input_report_abs(ext->input, ABS_HAT1Y, ly - 0x20);
|
||||
input_report_abs(ext->input, ABS_HAT2X, rx - 0x20);
|
||||
input_report_abs(ext->input, ABS_HAT2Y, ry - 0x20);
|
||||
input_report_abs(ext->input, ABS_HAT3X, rt - 0x20);
|
||||
input_report_abs(ext->input, ABS_HAT3Y, lt - 0x20);
|
||||
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RIGHT],
|
||||
!!(payload[4] & 0x80));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_DOWN],
|
||||
!!(payload[4] & 0x40));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LT],
|
||||
!!(payload[4] & 0x20));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_MINUS],
|
||||
!!(payload[4] & 0x10));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_HOME],
|
||||
!!(payload[4] & 0x08));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_PLUS],
|
||||
!!(payload[4] & 0x04));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RT],
|
||||
!!(payload[4] & 0x02));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZL],
|
||||
!!(payload[5] & 0x80));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_B],
|
||||
!!(payload[5] & 0x40));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_Y],
|
||||
!!(payload[5] & 0x20));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_A],
|
||||
!!(payload[5] & 0x10));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_X],
|
||||
!!(payload[5] & 0x08));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZR],
|
||||
!!(payload[5] & 0x04));
|
||||
|
||||
if (ext->motionp) {
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP],
|
||||
!!(payload[0] & 0x01));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT],
|
||||
!!(payload[1] & 0x01));
|
||||
} else {
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP],
|
||||
!!(payload[5] & 0x01));
|
||||
input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT],
|
||||
!!(payload[5] & 0x02));
|
||||
}
|
||||
|
||||
input_sync(ext->input);
|
||||
}
|
||||
|
||||
/* call this with state.lock spinlock held */
|
||||
void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
|
||||
{
|
||||
struct wiimote_ext *ext = wdata->ext;
|
||||
|
||||
if (!ext)
|
||||
return;
|
||||
|
||||
if (ext->motionp && (payload[5] & 0x02)) {
|
||||
handler_motionp(ext, payload);
|
||||
} else if (ext->ext_type == WIIEXT_NUNCHUCK) {
|
||||
handler_nunchuck(ext, payload);
|
||||
} else if (ext->ext_type == WIIEXT_CLASSIC) {
|
||||
handler_classic(ext, payload);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct wiimote_data *wdata = dev_to_wii(dev);
|
||||
__u8 type = WIIEXT_NONE;
|
||||
bool motionp = false;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||
if (wdata->ext) {
|
||||
motionp = wdata->ext->motionp;
|
||||
type = wdata->ext->ext_type;
|
||||
}
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||
|
||||
if (type == WIIEXT_NUNCHUCK) {
|
||||
if (motionp)
|
||||
return sprintf(buf, "motionp+nunchuck\n");
|
||||
else
|
||||
return sprintf(buf, "nunchuck\n");
|
||||
} else if (type == WIIEXT_CLASSIC) {
|
||||
if (motionp)
|
||||
return sprintf(buf, "motionp+classic\n");
|
||||
else
|
||||
return sprintf(buf, "classic\n");
|
||||
} else {
|
||||
if (motionp)
|
||||
return sprintf(buf, "motionp\n");
|
||||
else
|
||||
return sprintf(buf, "none\n");
|
||||
}
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(extension, S_IRUGO, wiiext_show, NULL);
|
||||
|
||||
static int wiiext_input_open(struct input_dev *dev)
|
||||
{
|
||||
struct wiimote_ext *ext = input_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = hid_hw_open(ext->wdata->hdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
atomic_inc(&ext->opened);
|
||||
wiiext_schedule(ext);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wiiext_input_close(struct input_dev *dev)
|
||||
{
|
||||
struct wiimote_ext *ext = input_get_drvdata(dev);
|
||||
|
||||
atomic_dec(&ext->opened);
|
||||
wiiext_schedule(ext);
|
||||
hid_hw_close(ext->wdata->hdev);
|
||||
}
|
||||
|
||||
static int wiiext_mp_open(struct input_dev *dev)
|
||||
{
|
||||
struct wiimote_ext *ext = input_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = hid_hw_open(ext->wdata->hdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
atomic_inc(&ext->mp_opened);
|
||||
wiiext_schedule(ext);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wiiext_mp_close(struct input_dev *dev)
|
||||
{
|
||||
struct wiimote_ext *ext = input_get_drvdata(dev);
|
||||
|
||||
atomic_dec(&ext->mp_opened);
|
||||
wiiext_schedule(ext);
|
||||
hid_hw_close(ext->wdata->hdev);
|
||||
}
|
||||
|
||||
/* Initializes the extension driver of a wiimote */
|
||||
int wiiext_init(struct wiimote_data *wdata)
|
||||
{
|
||||
struct wiimote_ext *ext;
|
||||
unsigned long flags;
|
||||
int ret, i;
|
||||
|
||||
ext = kzalloc(sizeof(*ext), GFP_KERNEL);
|
||||
if (!ext)
|
||||
return -ENOMEM;
|
||||
|
||||
ext->wdata = wdata;
|
||||
INIT_WORK(&ext->worker, wiiext_worker);
|
||||
|
||||
ext->input = input_allocate_device();
|
||||
if (!ext->input) {
|
||||
ret = -ENOMEM;
|
||||
goto err_input;
|
||||
}
|
||||
|
||||
input_set_drvdata(ext->input, ext);
|
||||
ext->input->open = wiiext_input_open;
|
||||
ext->input->close = wiiext_input_close;
|
||||
ext->input->dev.parent = &wdata->hdev->dev;
|
||||
ext->input->id.bustype = wdata->hdev->bus;
|
||||
ext->input->id.vendor = wdata->hdev->vendor;
|
||||
ext->input->id.product = wdata->hdev->product;
|
||||
ext->input->id.version = wdata->hdev->version;
|
||||
ext->input->name = WIIMOTE_NAME " Extension";
|
||||
|
||||
set_bit(EV_KEY, ext->input->evbit);
|
||||
for (i = 0; i < WIIEXT_KEY_COUNT; ++i)
|
||||
set_bit(wiiext_keymap[i], ext->input->keybit);
|
||||
|
||||
set_bit(EV_ABS, ext->input->evbit);
|
||||
set_bit(ABS_HAT0X, ext->input->absbit);
|
||||
set_bit(ABS_HAT0Y, ext->input->absbit);
|
||||
set_bit(ABS_HAT1X, ext->input->absbit);
|
||||
set_bit(ABS_HAT1Y, ext->input->absbit);
|
||||
set_bit(ABS_HAT2X, ext->input->absbit);
|
||||
set_bit(ABS_HAT2Y, ext->input->absbit);
|
||||
set_bit(ABS_HAT3X, ext->input->absbit);
|
||||
set_bit(ABS_HAT3Y, ext->input->absbit);
|
||||
input_set_abs_params(ext->input, ABS_HAT0X, -120, 120, 2, 4);
|
||||
input_set_abs_params(ext->input, ABS_HAT0Y, -120, 120, 2, 4);
|
||||
input_set_abs_params(ext->input, ABS_HAT1X, -30, 30, 1, 1);
|
||||
input_set_abs_params(ext->input, ABS_HAT1Y, -30, 30, 1, 1);
|
||||
input_set_abs_params(ext->input, ABS_HAT2X, -30, 30, 1, 1);
|
||||
input_set_abs_params(ext->input, ABS_HAT2Y, -30, 30, 1, 1);
|
||||
input_set_abs_params(ext->input, ABS_HAT3X, -30, 30, 1, 1);
|
||||
input_set_abs_params(ext->input, ABS_HAT3Y, -30, 30, 1, 1);
|
||||
set_bit(ABS_RX, ext->input->absbit);
|
||||
set_bit(ABS_RY, ext->input->absbit);
|
||||
set_bit(ABS_RZ, ext->input->absbit);
|
||||
input_set_abs_params(ext->input, ABS_RX, -500, 500, 2, 4);
|
||||
input_set_abs_params(ext->input, ABS_RY, -500, 500, 2, 4);
|
||||
input_set_abs_params(ext->input, ABS_RZ, -500, 500, 2, 4);
|
||||
|
||||
ret = input_register_device(ext->input);
|
||||
if (ret) {
|
||||
input_free_device(ext->input);
|
||||
goto err_input;
|
||||
}
|
||||
|
||||
ext->mp_input = input_allocate_device();
|
||||
if (!ext->mp_input) {
|
||||
ret = -ENOMEM;
|
||||
goto err_mp;
|
||||
}
|
||||
|
||||
input_set_drvdata(ext->mp_input, ext);
|
||||
ext->mp_input->open = wiiext_mp_open;
|
||||
ext->mp_input->close = wiiext_mp_close;
|
||||
ext->mp_input->dev.parent = &wdata->hdev->dev;
|
||||
ext->mp_input->id.bustype = wdata->hdev->bus;
|
||||
ext->mp_input->id.vendor = wdata->hdev->vendor;
|
||||
ext->mp_input->id.product = wdata->hdev->product;
|
||||
ext->mp_input->id.version = wdata->hdev->version;
|
||||
ext->mp_input->name = WIIMOTE_NAME " Motion+";
|
||||
|
||||
set_bit(EV_ABS, ext->mp_input->evbit);
|
||||
set_bit(ABS_RX, ext->mp_input->absbit);
|
||||
set_bit(ABS_RY, ext->mp_input->absbit);
|
||||
set_bit(ABS_RZ, ext->mp_input->absbit);
|
||||
input_set_abs_params(ext->mp_input, ABS_RX, -160000, 160000, 4, 8);
|
||||
input_set_abs_params(ext->mp_input, ABS_RY, -160000, 160000, 4, 8);
|
||||
input_set_abs_params(ext->mp_input, ABS_RZ, -160000, 160000, 4, 8);
|
||||
|
||||
ret = input_register_device(ext->mp_input);
|
||||
if (ret) {
|
||||
input_free_device(ext->mp_input);
|
||||
goto err_mp;
|
||||
}
|
||||
|
||||
ret = device_create_file(&wdata->hdev->dev, &dev_attr_extension);
|
||||
if (ret)
|
||||
goto err_dev;
|
||||
|
||||
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||
wdata->ext = ext;
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
err_dev:
|
||||
input_unregister_device(ext->mp_input);
|
||||
err_mp:
|
||||
input_unregister_device(ext->input);
|
||||
err_input:
|
||||
kfree(ext);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Deinitializes the extension driver of a wiimote */
|
||||
void wiiext_deinit(struct wiimote_data *wdata)
|
||||
{
|
||||
struct wiimote_ext *ext = wdata->ext;
|
||||
unsigned long flags;
|
||||
|
||||
if (!ext)
|
||||
return;
|
||||
|
||||
/*
|
||||
* We first unset wdata->ext to avoid further input from the wiimote
|
||||
* core. The worker thread does not access this pointer so it is not
|
||||
* affected by this.
|
||||
* We kill the worker after this so it does not get respawned during
|
||||
* deinitialization.
|
||||
*/
|
||||
|
||||
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||
wdata->ext = NULL;
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||
|
||||
device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
|
||||
input_unregister_device(ext->mp_input);
|
||||
input_unregister_device(ext->input);
|
||||
|
||||
cancel_work_sync(&ext->worker);
|
||||
kfree(ext);
|
||||
}
|
208
drivers/hid/hid-wiimote.h
Normal file
208
drivers/hid/hid-wiimote.h
Normal file
@ -0,0 +1,208 @@
|
||||
#ifndef __HID_WIIMOTE_H
|
||||
#define __HID_WIIMOTE_H
|
||||
|
||||
/*
|
||||
* HID driver for Nintendo Wiimote devices
|
||||
* Copyright (c) 2011 David Herrmann
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/completion.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define WIIMOTE_NAME "Nintendo Wii Remote"
|
||||
#define WIIMOTE_BUFSIZE 32
|
||||
|
||||
#define WIIPROTO_FLAG_LED1 0x01
|
||||
#define WIIPROTO_FLAG_LED2 0x02
|
||||
#define WIIPROTO_FLAG_LED3 0x04
|
||||
#define WIIPROTO_FLAG_LED4 0x08
|
||||
#define WIIPROTO_FLAG_RUMBLE 0x10
|
||||
#define WIIPROTO_FLAG_ACCEL 0x20
|
||||
#define WIIPROTO_FLAG_IR_BASIC 0x40
|
||||
#define WIIPROTO_FLAG_IR_EXT 0x80
|
||||
#define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */
|
||||
#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
|
||||
WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
|
||||
#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \
|
||||
WIIPROTO_FLAG_IR_FULL)
|
||||
|
||||
/* return flag for led \num */
|
||||
#define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1))
|
||||
|
||||
struct wiimote_buf {
|
||||
__u8 data[HID_MAX_BUFFER_SIZE];
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct wiimote_state {
|
||||
spinlock_t lock;
|
||||
__u8 flags;
|
||||
__u8 accel_split[2];
|
||||
__u8 drm;
|
||||
|
||||
/* synchronous cmd requests */
|
||||
struct mutex sync;
|
||||
struct completion ready;
|
||||
int cmd;
|
||||
__u32 opt;
|
||||
|
||||
/* results of synchronous requests */
|
||||
__u8 cmd_battery;
|
||||
__u8 cmd_err;
|
||||
__u8 *cmd_read_buf;
|
||||
__u8 cmd_read_size;
|
||||
};
|
||||
|
||||
struct wiimote_data {
|
||||
struct hid_device *hdev;
|
||||
struct input_dev *input;
|
||||
struct led_classdev *leds[4];
|
||||
struct input_dev *accel;
|
||||
struct input_dev *ir;
|
||||
struct power_supply battery;
|
||||
struct wiimote_ext *ext;
|
||||
struct wiimote_debug *debug;
|
||||
|
||||
spinlock_t qlock;
|
||||
__u8 head;
|
||||
__u8 tail;
|
||||
struct wiimote_buf outq[WIIMOTE_BUFSIZE];
|
||||
struct work_struct worker;
|
||||
|
||||
struct wiimote_state state;
|
||||
};
|
||||
|
||||
enum wiiproto_reqs {
|
||||
WIIPROTO_REQ_NULL = 0x0,
|
||||
WIIPROTO_REQ_RUMBLE = 0x10,
|
||||
WIIPROTO_REQ_LED = 0x11,
|
||||
WIIPROTO_REQ_DRM = 0x12,
|
||||
WIIPROTO_REQ_IR1 = 0x13,
|
||||
WIIPROTO_REQ_SREQ = 0x15,
|
||||
WIIPROTO_REQ_WMEM = 0x16,
|
||||
WIIPROTO_REQ_RMEM = 0x17,
|
||||
WIIPROTO_REQ_IR2 = 0x1a,
|
||||
WIIPROTO_REQ_STATUS = 0x20,
|
||||
WIIPROTO_REQ_DATA = 0x21,
|
||||
WIIPROTO_REQ_RETURN = 0x22,
|
||||
WIIPROTO_REQ_DRM_K = 0x30,
|
||||
WIIPROTO_REQ_DRM_KA = 0x31,
|
||||
WIIPROTO_REQ_DRM_KE = 0x32,
|
||||
WIIPROTO_REQ_DRM_KAI = 0x33,
|
||||
WIIPROTO_REQ_DRM_KEE = 0x34,
|
||||
WIIPROTO_REQ_DRM_KAE = 0x35,
|
||||
WIIPROTO_REQ_DRM_KIE = 0x36,
|
||||
WIIPROTO_REQ_DRM_KAIE = 0x37,
|
||||
WIIPROTO_REQ_DRM_E = 0x3d,
|
||||
WIIPROTO_REQ_DRM_SKAI1 = 0x3e,
|
||||
WIIPROTO_REQ_DRM_SKAI2 = 0x3f,
|
||||
WIIPROTO_REQ_MAX
|
||||
};
|
||||
|
||||
#define dev_to_wii(pdev) hid_get_drvdata(container_of(pdev, struct hid_device, \
|
||||
dev))
|
||||
|
||||
extern void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm);
|
||||
extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
|
||||
const __u8 *wmem, __u8 size);
|
||||
extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset,
|
||||
__u8 *rmem, __u8 size);
|
||||
|
||||
#define wiiproto_req_rreg(wdata, os, sz) \
|
||||
wiiproto_req_rmem((wdata), false, (os), (sz))
|
||||
#define wiiproto_req_reeprom(wdata, os, sz) \
|
||||
wiiproto_req_rmem((wdata), true, (os), (sz))
|
||||
extern void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom,
|
||||
__u32 offset, __u16 size);
|
||||
|
||||
#ifdef CONFIG_HID_WIIMOTE_EXT
|
||||
|
||||
extern int wiiext_init(struct wiimote_data *wdata);
|
||||
extern void wiiext_deinit(struct wiimote_data *wdata);
|
||||
extern void wiiext_event(struct wiimote_data *wdata, bool plugged);
|
||||
extern bool wiiext_active(struct wiimote_data *wdata);
|
||||
extern void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload);
|
||||
|
||||
#else
|
||||
|
||||
static inline int wiiext_init(void *u) { return 0; }
|
||||
static inline void wiiext_deinit(void *u) { }
|
||||
static inline void wiiext_event(void *u, bool p) { }
|
||||
static inline bool wiiext_active(void *u) { return false; }
|
||||
static inline void wiiext_handle(void *u, const __u8 *p) { }
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
extern int wiidebug_init(struct wiimote_data *wdata);
|
||||
extern void wiidebug_deinit(struct wiimote_data *wdata);
|
||||
|
||||
#else
|
||||
|
||||
static inline int wiidebug_init(void *u) { return 0; }
|
||||
static inline void wiidebug_deinit(void *u) { }
|
||||
|
||||
#endif
|
||||
|
||||
/* requires the state.lock spinlock to be held */
|
||||
static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd,
|
||||
__u32 opt)
|
||||
{
|
||||
return wdata->state.cmd == cmd && wdata->state.opt == opt;
|
||||
}
|
||||
|
||||
/* requires the state.lock spinlock to be held */
|
||||
static inline void wiimote_cmd_complete(struct wiimote_data *wdata)
|
||||
{
|
||||
wdata->state.cmd = WIIPROTO_REQ_NULL;
|
||||
complete(&wdata->state.ready);
|
||||
}
|
||||
|
||||
static inline int wiimote_cmd_acquire(struct wiimote_data *wdata)
|
||||
{
|
||||
return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0;
|
||||
}
|
||||
|
||||
/* requires the state.lock spinlock to be held */
|
||||
static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd,
|
||||
__u32 opt)
|
||||
{
|
||||
INIT_COMPLETION(wdata->state.ready);
|
||||
wdata->state.cmd = cmd;
|
||||
wdata->state.opt = opt;
|
||||
}
|
||||
|
||||
static inline void wiimote_cmd_release(struct wiimote_data *wdata)
|
||||
{
|
||||
mutex_unlock(&wdata->state.sync);
|
||||
}
|
||||
|
||||
static inline int wiimote_cmd_wait(struct wiimote_data *wdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ);
|
||||
if (ret < 0)
|
||||
return -ERESTARTSYS;
|
||||
else if (ret == 0)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -197,16 +197,24 @@ static int usbhid_restart_out_queue(struct usbhid_device *usbhid)
|
||||
{
|
||||
struct hid_device *hid = usb_get_intfdata(usbhid->intf);
|
||||
int kicked;
|
||||
int r;
|
||||
|
||||
if (!hid)
|
||||
return 0;
|
||||
|
||||
if ((kicked = (usbhid->outhead != usbhid->outtail))) {
|
||||
dbg("Kicking head %d tail %d", usbhid->outhead, usbhid->outtail);
|
||||
|
||||
r = usb_autopm_get_interface_async(usbhid->intf);
|
||||
if (r < 0)
|
||||
return r;
|
||||
/* Asynchronously flush queue. */
|
||||
set_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||
if (hid_submit_out(hid)) {
|
||||
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||
wake_up(&usbhid->wait);
|
||||
usb_autopm_put_interface_async(usbhid->intf);
|
||||
}
|
||||
wake_up(&usbhid->wait);
|
||||
}
|
||||
return kicked;
|
||||
}
|
||||
@ -215,6 +223,7 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
|
||||
{
|
||||
struct hid_device *hid = usb_get_intfdata(usbhid->intf);
|
||||
int kicked;
|
||||
int r;
|
||||
|
||||
WARN_ON(hid == NULL);
|
||||
if (!hid)
|
||||
@ -222,10 +231,17 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
|
||||
|
||||
if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) {
|
||||
dbg("Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail);
|
||||
|
||||
r = usb_autopm_get_interface_async(usbhid->intf);
|
||||
if (r < 0)
|
||||
return r;
|
||||
/* Asynchronously flush queue. */
|
||||
set_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||
if (hid_submit_ctrl(hid)) {
|
||||
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||
wake_up(&usbhid->wait);
|
||||
usb_autopm_put_interface_async(usbhid->intf);
|
||||
}
|
||||
wake_up(&usbhid->wait);
|
||||
}
|
||||
return kicked;
|
||||
}
|
||||
@ -304,30 +320,21 @@ static int hid_submit_out(struct hid_device *hid)
|
||||
report = usbhid->out[usbhid->outtail].report;
|
||||
raw_report = usbhid->out[usbhid->outtail].raw_report;
|
||||
|
||||
r = usb_autopm_get_interface_async(usbhid->intf);
|
||||
if (r < 0)
|
||||
return -1;
|
||||
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 the device hasn't been woken, we leave the output
|
||||
* to resume()
|
||||
*/
|
||||
if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) {
|
||||
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);
|
||||
dbg_hid("submitting out urb\n");
|
||||
|
||||
dbg_hid("submitting out urb\n");
|
||||
|
||||
if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) {
|
||||
hid_err(hid, "usb_submit_urb(out) failed\n");
|
||||
usb_autopm_put_interface_async(usbhid->intf);
|
||||
return -1;
|
||||
}
|
||||
usbhid->last_out = jiffies;
|
||||
r = usb_submit_urb(usbhid->urbout, GFP_ATOMIC);
|
||||
if (r < 0) {
|
||||
hid_err(hid, "usb_submit_urb(out) failed: %d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
usbhid->last_out = jiffies;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -343,50 +350,48 @@ static int hid_submit_ctrl(struct hid_device *hid)
|
||||
raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report;
|
||||
dir = usbhid->ctrl[usbhid->ctrltail].dir;
|
||||
|
||||
r = usb_autopm_get_interface_async(usbhid->intf);
|
||||
if (r < 0)
|
||||
return -1;
|
||||
if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) {
|
||||
len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
|
||||
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);
|
||||
} else {
|
||||
int maxpacket, padlen;
|
||||
len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
|
||||
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);
|
||||
} else {
|
||||
int maxpacket, padlen;
|
||||
|
||||
usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
|
||||
maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0);
|
||||
if (maxpacket > 0) {
|
||||
padlen = DIV_ROUND_UP(len, maxpacket);
|
||||
padlen *= maxpacket;
|
||||
if (padlen > usbhid->bufsize)
|
||||
padlen = usbhid->bufsize;
|
||||
} else
|
||||
padlen = 0;
|
||||
usbhid->urbctrl->transfer_buffer_length = padlen;
|
||||
}
|
||||
usbhid->urbctrl->dev = hid_to_usb_dev(hid);
|
||||
|
||||
usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
|
||||
usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
|
||||
usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
|
||||
usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
|
||||
usbhid->cr->wLength = cpu_to_le16(len);
|
||||
|
||||
dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
|
||||
usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
|
||||
usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
|
||||
|
||||
if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
|
||||
usb_autopm_put_interface_async(usbhid->intf);
|
||||
hid_err(hid, "usb_submit_urb(ctrl) failed\n");
|
||||
return -1;
|
||||
}
|
||||
usbhid->last_ctrl = jiffies;
|
||||
usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
|
||||
maxpacket = usb_maxpacket(hid_to_usb_dev(hid),
|
||||
usbhid->urbctrl->pipe, 0);
|
||||
if (maxpacket > 0) {
|
||||
padlen = DIV_ROUND_UP(len, maxpacket);
|
||||
padlen *= maxpacket;
|
||||
if (padlen > usbhid->bufsize)
|
||||
padlen = usbhid->bufsize;
|
||||
} else
|
||||
padlen = 0;
|
||||
usbhid->urbctrl->transfer_buffer_length = padlen;
|
||||
}
|
||||
usbhid->urbctrl->dev = hid_to_usb_dev(hid);
|
||||
|
||||
usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
|
||||
usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT :
|
||||
HID_REQ_GET_REPORT;
|
||||
usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) |
|
||||
report->id);
|
||||
usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
|
||||
usbhid->cr->wLength = cpu_to_le16(len);
|
||||
|
||||
dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
|
||||
usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" :
|
||||
"Get_Report",
|
||||
usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
|
||||
|
||||
r = usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC);
|
||||
if (r < 0) {
|
||||
hid_err(hid, "usb_submit_urb(ctrl) failed: %d\n", r);
|
||||
return r;
|
||||
}
|
||||
usbhid->last_ctrl = jiffies;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -423,11 +428,8 @@ static void hid_irq_out(struct urb *urb)
|
||||
else
|
||||
usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
|
||||
|
||||
if (usbhid->outhead != usbhid->outtail) {
|
||||
if (hid_submit_out(hid)) {
|
||||
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||
wake_up(&usbhid->wait);
|
||||
}
|
||||
if (usbhid->outhead != usbhid->outtail && !hid_submit_out(hid)) {
|
||||
/* Successfully submitted next urb in queue */
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
return;
|
||||
}
|
||||
@ -474,13 +476,9 @@ static void hid_ctrl(struct urb *urb)
|
||||
else
|
||||
usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
|
||||
|
||||
if (usbhid->ctrlhead != usbhid->ctrltail) {
|
||||
if (hid_submit_ctrl(hid)) {
|
||||
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||
wake_up(&usbhid->wait);
|
||||
}
|
||||
if (usbhid->ctrlhead != usbhid->ctrltail && !hid_submit_ctrl(hid)) {
|
||||
/* Successfully submitted next urb in queue */
|
||||
spin_unlock(&usbhid->lock);
|
||||
usb_autopm_put_interface_async(usbhid->intf);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -515,9 +513,23 @@ 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;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) {
|
||||
if (hid_submit_out(hid))
|
||||
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
|
||||
@ -549,9 +561,23 @@ 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 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;
|
||||
|
||||
if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) {
|
||||
if (hid_submit_ctrl(hid))
|
||||
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
|
||||
@ -576,6 +602,30 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbhid_submit_report);
|
||||
|
||||
/* Workqueue routine to send requests to change LEDs */
|
||||
static void hid_led(struct work_struct *work)
|
||||
{
|
||||
struct usbhid_device *usbhid =
|
||||
container_of(work, struct usbhid_device, led_work);
|
||||
struct hid_device *hid = usbhid->hid;
|
||||
struct hid_field *field;
|
||||
unsigned long flags;
|
||||
|
||||
field = hidinput_get_led_field(hid);
|
||||
if (!field) {
|
||||
hid_warn(hid, "LED event field not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&usbhid->lock, flags);
|
||||
if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) {
|
||||
usbhid->ledcount = hidinput_count_leds(hid);
|
||||
hid_dbg(usbhid->hid, "New ledcount = %u\n", usbhid->ledcount);
|
||||
__usbhid_submit_report(hid, field->report, USB_DIR_OUT);
|
||||
}
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
}
|
||||
|
||||
static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
@ -595,17 +645,15 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un
|
||||
return -1;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&usbhid->lock, flags);
|
||||
hid_set_field(field, offset, value);
|
||||
if (value) {
|
||||
spin_lock_irqsave(&usbhid->lock, flags);
|
||||
usbhid->ledcount++;
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
} else {
|
||||
spin_lock_irqsave(&usbhid->lock, flags);
|
||||
usbhid->ledcount--;
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
}
|
||||
usbhid_submit_report(hid, field->report, USB_DIR_OUT);
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
|
||||
/*
|
||||
* Defer performing requested LED action.
|
||||
* This is more likely gather all LED changes into a single URB.
|
||||
*/
|
||||
schedule_work(&usbhid->led_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1100,7 +1148,7 @@ static void usbhid_stop(struct hid_device *hid)
|
||||
return;
|
||||
|
||||
clear_bit(HID_STARTED, &usbhid->iofl);
|
||||
spin_lock_irq(&usbhid->lock); /* Sync with error handler */
|
||||
spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */
|
||||
set_bit(HID_DISCONNECTED, &usbhid->iofl);
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
usb_kill_urb(usbhid->urbin);
|
||||
@ -1234,6 +1282,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
|
||||
setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
|
||||
spin_lock_init(&usbhid->lock);
|
||||
|
||||
INIT_WORK(&usbhid->led_work, hid_led);
|
||||
|
||||
ret = hid_add_device(hid);
|
||||
if (ret) {
|
||||
if (ret != -ENODEV)
|
||||
@ -1266,6 +1316,7 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid)
|
||||
{
|
||||
del_timer_sync(&usbhid->io_retry);
|
||||
cancel_work_sync(&usbhid->reset_work);
|
||||
cancel_work_sync(&usbhid->led_work);
|
||||
}
|
||||
|
||||
static void hid_cease_io(struct usbhid_device *usbhid)
|
||||
@ -1367,16 +1418,6 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!ignoreled && PMSG_IS_AUTO(message)) {
|
||||
spin_lock_irq(&usbhid->lock);
|
||||
if (test_bit(HID_LED_ON, &usbhid->iofl)) {
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
usbhid_mark_busy(usbhid);
|
||||
return -EBUSY;
|
||||
}
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
}
|
||||
|
||||
hid_cancel_delayed_stuff(usbhid);
|
||||
hid_cease_io(usbhid);
|
||||
|
||||
|
@ -47,6 +47,7 @@ static const struct hid_blacklist {
|
||||
|
||||
{ USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
|
||||
|
||||
{ USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
|
||||
@ -67,6 +68,9 @@ static const struct hid_blacklist {
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
|
||||
|
@ -55,7 +55,6 @@ struct usb_interface *usbhid_find_interface(int minor);
|
||||
#define HID_STARTED 8
|
||||
#define HID_REPORTED_IDLE 9
|
||||
#define HID_KEYS_PRESSED 10
|
||||
#define HID_LED_ON 11
|
||||
|
||||
/*
|
||||
* USB-specific HID struct, to be pointed to
|
||||
@ -97,6 +96,8 @@ struct usbhid_device {
|
||||
struct work_struct reset_work; /* Task context for resets */
|
||||
wait_queue_head_t wait; /* For sleeping */
|
||||
int ledcount; /* counting the number of active leds */
|
||||
|
||||
struct work_struct led_work; /* Task context for setting LEDs */
|
||||
};
|
||||
|
||||
#define hid_to_usb_dev(hid_dev) \
|
||||
|
@ -64,6 +64,32 @@ static const unsigned char usb_kbd_keycode[256] = {
|
||||
150,158,159,128,136,177,178,176,142,152,173,140
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct usb_kbd - state of each attached keyboard
|
||||
* @dev: input device associated with this keyboard
|
||||
* @usbdev: usb device associated with this keyboard
|
||||
* @old: data received in the past from the @irq URB representing which
|
||||
* keys were pressed. By comparing with the current list of keys
|
||||
* that are pressed, we are able to see key releases.
|
||||
* @irq: URB for receiving a list of keys that are pressed when a
|
||||
* new key is pressed or a key that was pressed is released.
|
||||
* @led: URB for sending LEDs (e.g. numlock, ...)
|
||||
* @newleds: data that will be sent with the @led URB representing which LEDs
|
||||
should be on
|
||||
* @name: Name of the keyboard. @dev's name field points to this buffer
|
||||
* @phys: Physical path of the keyboard. @dev's phys field points to this
|
||||
* buffer
|
||||
* @new: Buffer for the @irq URB
|
||||
* @cr: Control request for @led URB
|
||||
* @leds: Buffer for the @led URB
|
||||
* @new_dma: DMA address for @irq URB
|
||||
* @leds_dma: DMA address for @led URB
|
||||
* @leds_lock: spinlock that protects @leds, @newleds, and @led_urb_submitted
|
||||
* @led_urb_submitted: indicates whether @led is in progress, i.e. it has been
|
||||
* submitted and its completion handler has not returned yet
|
||||
* without resubmitting @led
|
||||
*/
|
||||
struct usb_kbd {
|
||||
struct input_dev *dev;
|
||||
struct usb_device *usbdev;
|
||||
@ -78,6 +104,10 @@ struct usb_kbd {
|
||||
unsigned char *leds;
|
||||
dma_addr_t new_dma;
|
||||
dma_addr_t leds_dma;
|
||||
|
||||
spinlock_t leds_lock;
|
||||
bool led_urb_submitted;
|
||||
|
||||
};
|
||||
|
||||
static void usb_kbd_irq(struct urb *urb)
|
||||
@ -136,44 +166,66 @@ resubmit:
|
||||
static int usb_kbd_event(struct input_dev *dev, unsigned int type,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct usb_kbd *kbd = input_get_drvdata(dev);
|
||||
|
||||
if (type != EV_LED)
|
||||
return -1;
|
||||
|
||||
spin_lock_irqsave(&kbd->leds_lock, flags);
|
||||
kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
|
||||
(!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
|
||||
(!!test_bit(LED_NUML, dev->led));
|
||||
|
||||
if (kbd->led->status == -EINPROGRESS)
|
||||
if (kbd->led_urb_submitted){
|
||||
spin_unlock_irqrestore(&kbd->leds_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*(kbd->leds) == kbd->newleds)
|
||||
if (*(kbd->leds) == kbd->newleds){
|
||||
spin_unlock_irqrestore(&kbd->leds_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*(kbd->leds) = kbd->newleds;
|
||||
|
||||
kbd->led->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
|
||||
pr_err("usb_submit_urb(leds) failed\n");
|
||||
|
||||
else
|
||||
kbd->led_urb_submitted = true;
|
||||
|
||||
spin_unlock_irqrestore(&kbd->leds_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_led(struct urb *urb)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct usb_kbd *kbd = urb->context;
|
||||
|
||||
if (urb->status)
|
||||
hid_warn(urb->dev, "led urb status %d received\n",
|
||||
urb->status);
|
||||
|
||||
if (*(kbd->leds) == kbd->newleds)
|
||||
spin_lock_irqsave(&kbd->leds_lock, flags);
|
||||
|
||||
if (*(kbd->leds) == kbd->newleds){
|
||||
kbd->led_urb_submitted = false;
|
||||
spin_unlock_irqrestore(&kbd->leds_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
*(kbd->leds) = kbd->newleds;
|
||||
|
||||
kbd->led->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
|
||||
if (usb_submit_urb(kbd->led, GFP_ATOMIC)){
|
||||
hid_err(urb->dev, "usb_submit_urb(leds) failed\n");
|
||||
kbd->led_urb_submitted = false;
|
||||
}
|
||||
spin_unlock_irqrestore(&kbd->leds_lock, flags);
|
||||
|
||||
}
|
||||
|
||||
static int usb_kbd_open(struct input_dev *dev)
|
||||
@ -252,6 +304,7 @@ static int usb_kbd_probe(struct usb_interface *iface,
|
||||
|
||||
kbd->usbdev = dev;
|
||||
kbd->dev = input_dev;
|
||||
spin_lock_init(&kbd->leds_lock);
|
||||
|
||||
if (dev->manufacturer)
|
||||
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
|
||||
@ -334,6 +387,7 @@ static void usb_kbd_disconnect(struct usb_interface *intf)
|
||||
if (kbd) {
|
||||
usb_kill_urb(kbd->irq);
|
||||
input_unregister_device(kbd->dev);
|
||||
usb_kill_urb(kbd->led);
|
||||
usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
|
||||
kfree(kbd);
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ static const struct usb_device_id id_table[] = {
|
||||
.driver_info = DELCOM_VISUAL_SIGNAL_INDICATOR },
|
||||
{ USB_DEVICE(0x1d34, 0x0004),
|
||||
.driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
|
||||
{ USB_DEVICE(0x1d34, 0x000a),
|
||||
.driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
@ -72,6 +72,7 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
/*
|
||||
* We parse each description item into this structure. Short items data
|
||||
@ -190,6 +191,7 @@ struct hid_item {
|
||||
#define HID_UP_UNDEFINED 0x00000000
|
||||
#define HID_UP_GENDESK 0x00010000
|
||||
#define HID_UP_SIMULATION 0x00020000
|
||||
#define HID_UP_GENDEVCTRLS 0x00060000
|
||||
#define HID_UP_KEYBOARD 0x00070000
|
||||
#define HID_UP_LED 0x00080000
|
||||
#define HID_UP_BUTTON 0x00090000
|
||||
@ -239,6 +241,8 @@ struct hid_item {
|
||||
#define HID_GD_RIGHT 0x00010092
|
||||
#define HID_GD_LEFT 0x00010093
|
||||
|
||||
#define HID_DC_BATTERYSTRENGTH 0x00060020
|
||||
|
||||
#define HID_DG_DIGITIZER 0x000d0001
|
||||
#define HID_DG_PEN 0x000d0002
|
||||
#define HID_DG_LIGHTPEN 0x000d0003
|
||||
@ -482,6 +486,19 @@ struct hid_device { /* device report descriptor */
|
||||
struct hid_driver *driver;
|
||||
struct hid_ll_driver *ll_driver;
|
||||
|
||||
#ifdef CONFIG_HID_BATTERY_STRENGTH
|
||||
/*
|
||||
* Power supply information for HID devices which report
|
||||
* battery strength. power_supply is registered iff
|
||||
* battery.name is non-NULL.
|
||||
*/
|
||||
struct power_supply battery;
|
||||
__s32 battery_min;
|
||||
__s32 battery_max;
|
||||
__s32 battery_report_type;
|
||||
__s32 battery_report_id;
|
||||
#endif
|
||||
|
||||
unsigned int status; /* see STAT flags above */
|
||||
unsigned claimed; /* Claimed by hidinput, hiddev? */
|
||||
unsigned quirks; /* Various quirks the device can pull on us */
|
||||
@ -712,6 +729,8 @@ extern void hidinput_disconnect(struct hid_device *);
|
||||
int hid_set_field(struct hid_field *, unsigned, __s32);
|
||||
int hid_input_report(struct hid_device *, int type, u8 *, int, int);
|
||||
int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field);
|
||||
struct hid_field *hidinput_get_led_field(struct hid_device *hid);
|
||||
unsigned int hidinput_count_leds(struct hid_device *hid);
|
||||
void hid_output_report(struct hid_report *report, __u8 *data);
|
||||
struct hid_device *hid_allocate_device(void);
|
||||
struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
|
||||
@ -719,6 +738,8 @@ int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
|
||||
int hid_check_keys_pressed(struct hid_device *hid);
|
||||
int hid_connect(struct hid_device *hid, unsigned int connect_mask);
|
||||
void hid_disconnect(struct hid_device *hid);
|
||||
const struct hid_device_id *hid_match_id(struct hid_device *hdev,
|
||||
const struct hid_device_id *id);
|
||||
|
||||
/**
|
||||
* hid_map_usage - map usage input bits
|
||||
|
Loading…
Reference in New Issue
Block a user