Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID updates from Jiri Kosina: - quirk for devices that need to be pulled in much more aggresive way than mandated, by Johan Hovold - robustification of sanity checking of incoming reports in RMI driver, by Benjamin Tissoires - fixes, updates, and new HW support to SONY driver, by Frank Praznik - port of uHID to the new transport layer layout, by David Herrmann - robustification of Clear-Halt/reset in USB HID, by Alan Stern - native support for hopefully any future HID compliant wacom tablet. Those found on the various laptops (ISDv4/5) already are HID compliant and they should work in the future without any modification of the kernel. Written by Benjamin Tissoires. - a lot more simple fixes and device ID additions all over the place * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (45 commits) HID: uHID: fix excepted report type HID: usbhid: add another mouse that needs QUIRK_ALWAYS_POLL HID: wacom: implement the finger part of the HID generic handling HID: wacom: implement generic HID handling for pen generic devices HID: wacom: move allocation of inputs earlier HID: wacom: split out input allocation and registration HID: wacom: rename failN with some meaningful information HID: sony: Update the DualShock 4 touchpad resolution HID: wacom: fix timeout on probe for some wacoms HID: sony: Set touchpad bits in the input_configured callback HID: sony: Update file header and correct comments HID: sony: Corrections for the DualShock 4 HID descriptor HID: rmi: check sanity of the incoming report HID: wacom: make the WL connection friendly for the desktop HID: wacom - enable LED support for Wireless Intuos5/Pro HID: wacom - remove report_id from wacom_get_report interface HID: wacom - Clean up of sysfs HID: wacom - Add default permission defines for sysfs attributes HID: usbhid: fix PIXART optical mouse HID: Add Holtek USB ID 04d9:a0c2 ETEKCITY Scroll ...
This commit is contained in:
commit
39520eea19
23 changed files with 1184 additions and 473 deletions
|
@ -1,28 +1,13 @@
|
|||
UHID - User-space I/O driver support for HID subsystem
|
||||
========================================================
|
||||
|
||||
The HID subsystem needs two kinds of drivers. In this document we call them:
|
||||
UHID allows user-space to implement HID transport drivers. Please see
|
||||
hid-transport.txt for an introduction into HID transport drivers. This document
|
||||
relies heavily on the definitions declared there.
|
||||
|
||||
1. The "HID I/O Driver" is the driver that performs raw data I/O to the
|
||||
low-level device. Internally, they register an hid_ll_driver structure with
|
||||
the HID core. They perform device setup, read raw data from the device and
|
||||
push it into the HID subsystem and they provide a callback so the HID
|
||||
subsystem can send data to the device.
|
||||
|
||||
2. The "HID Device Driver" is the driver that parses HID reports and reacts on
|
||||
them. There are generic drivers like "generic-usb" and "generic-bluetooth"
|
||||
which adhere to the HID specification and provide the standardizes features.
|
||||
But there may be special drivers and quirks for each non-standard device out
|
||||
there. Internally, they use the hid_driver structure.
|
||||
|
||||
Historically, the USB stack was the first subsystem to provide an HID I/O
|
||||
Driver. However, other standards like Bluetooth have adopted the HID specs and
|
||||
may provide HID I/O Drivers, too. The UHID driver allows to implement HID I/O
|
||||
Drivers in user-space and feed the data into the kernel HID-subsystem.
|
||||
|
||||
This allows user-space to operate on the same level as USB-HID, Bluetooth-HID
|
||||
and similar. It does not provide a way to write HID Device Drivers, though. Use
|
||||
hidraw for this purpose.
|
||||
With UHID, a user-space transport driver can create kernel hid-devices for each
|
||||
device connected to the user-space controlled bus. The UHID API defines the I/O
|
||||
events provided from the kernel to user-space and vice versa.
|
||||
|
||||
There is an example user-space application in ./samples/uhid/uhid-example.c
|
||||
|
||||
|
@ -42,8 +27,9 @@ by setting O_NONBLOCK.
|
|||
struct uhid_event {
|
||||
__u32 type;
|
||||
union {
|
||||
struct uhid_create_req create;
|
||||
struct uhid_data_req data;
|
||||
struct uhid_create2_req create2;
|
||||
struct uhid_output_req output;
|
||||
struct uhid_input2_req input2;
|
||||
...
|
||||
} u;
|
||||
};
|
||||
|
@ -54,8 +40,11 @@ multiple write()'s. A single event must always be sent as a whole. Furthermore,
|
|||
only a single event can be sent per read() or write(). Pending data is ignored.
|
||||
If you want to handle multiple events in a single syscall, then use vectored
|
||||
I/O with readv()/writev().
|
||||
The "type" field defines the payload. For each type, there is a
|
||||
payload-structure available in the union "u" (except for empty payloads). This
|
||||
payload contains management and/or device data.
|
||||
|
||||
The first thing you should do is sending an UHID_CREATE event. This will
|
||||
The first thing you should do is sending an UHID_CREATE2 event. This will
|
||||
register the device. UHID will respond with an UHID_START event. You can now
|
||||
start sending data to and reading data from UHID. However, unless UHID sends the
|
||||
UHID_OPEN event, the internally attached HID Device Driver has no user attached.
|
||||
|
@ -69,12 +58,20 @@ ref-counting for you.
|
|||
You may decide to ignore UHID_OPEN/UHID_CLOSE, though. I/O is allowed even
|
||||
though the device may have no users.
|
||||
|
||||
If you want to send data to the HID subsystem, you send an HID_INPUT event with
|
||||
your raw data payload. If the kernel wants to send data to the device, you will
|
||||
read an UHID_OUTPUT or UHID_OUTPUT_EV event.
|
||||
If you want to send data on the interrupt channel to the HID subsystem, you send
|
||||
an HID_INPUT2 event with your raw data payload. If the kernel wants to send data
|
||||
on the interrupt channel to the device, you will read an UHID_OUTPUT event.
|
||||
Data requests on the control channel are currently limited to GET_REPORT and
|
||||
SET_REPORT (no other data reports on the control channel are defined so far).
|
||||
Those requests are always synchronous. That means, the kernel sends
|
||||
UHID_GET_REPORT and UHID_SET_REPORT events and requires you to forward them to
|
||||
the device on the control channel. Once the device responds, you must forward
|
||||
the response via UHID_GET_REPORT_REPLY and UHID_SET_REPORT_REPLY to the kernel.
|
||||
The kernel blocks internal driver-execution during such round-trips (times out
|
||||
after a hard-coded period).
|
||||
|
||||
If your device disconnects, you should send an UHID_DESTROY event. This will
|
||||
unregister the device. You can now send UHID_CREATE again to register a new
|
||||
unregister the device. You can now send UHID_CREATE2 again to register a new
|
||||
device.
|
||||
If you close() the fd, the device is automatically unregistered and destroyed
|
||||
internally.
|
||||
|
@ -82,73 +79,79 @@ internally.
|
|||
write()
|
||||
-------
|
||||
write() allows you to modify the state of the device and feed input data into
|
||||
the kernel. The following types are supported: UHID_CREATE, UHID_DESTROY and
|
||||
UHID_INPUT. The kernel will parse the event immediately and if the event ID is
|
||||
the kernel. The kernel will parse the event immediately and if the event ID is
|
||||
not supported, it will return -EOPNOTSUPP. If the payload is invalid, then
|
||||
-EINVAL is returned, otherwise, the amount of data that was read is returned and
|
||||
the request was handled successfully.
|
||||
|
||||
UHID_CREATE:
|
||||
This creates the internal HID device. No I/O is possible until you send this
|
||||
event to the kernel. The payload is of type struct uhid_create_req and
|
||||
contains information about your device. You can start I/O now.
|
||||
the request was handled successfully. O_NONBLOCK does not affect write() as
|
||||
writes are always handled immediately in a non-blocking fashion. Future requests
|
||||
might make use of O_NONBLOCK, though.
|
||||
|
||||
UHID_CREATE2:
|
||||
Same as UHID_CREATE, but the HID report descriptor data (rd_data) is an array
|
||||
inside struct uhid_create2_req, instead of a pointer to a separate array.
|
||||
Enables use from languages that don't support pointers, e.g. Python.
|
||||
This creates the internal HID device. No I/O is possible until you send this
|
||||
event to the kernel. The payload is of type struct uhid_create2_req and
|
||||
contains information about your device. You can start I/O now.
|
||||
|
||||
UHID_DESTROY:
|
||||
This destroys the internal HID device. No further I/O will be accepted. There
|
||||
may still be pending messages that you can receive with read() but no further
|
||||
UHID_INPUT events can be sent to the kernel.
|
||||
You can create a new device by sending UHID_CREATE again. There is no need to
|
||||
You can create a new device by sending UHID_CREATE2 again. There is no need to
|
||||
reopen the character device.
|
||||
|
||||
UHID_INPUT:
|
||||
You must send UHID_CREATE before sending input to the kernel! This event
|
||||
contains a data-payload. This is the raw data that you read from your device.
|
||||
The kernel will parse the HID reports and react on it.
|
||||
|
||||
UHID_INPUT2:
|
||||
Same as UHID_INPUT, but the data array is the last field of uhid_input2_req.
|
||||
Enables userspace to write only the required bytes to kernel (ev.type +
|
||||
ev.u.input2.size + the part of the data array that matters), instead of
|
||||
the entire struct uhid_input2_req.
|
||||
You must send UHID_CREATE2 before sending input to the kernel! This event
|
||||
contains a data-payload. This is the raw data that you read from your device
|
||||
on the interrupt channel. The kernel will parse the HID reports.
|
||||
|
||||
UHID_FEATURE_ANSWER:
|
||||
If you receive a UHID_FEATURE request you must answer with this request. You
|
||||
must copy the "id" field from the request into the answer. Set the "err" field
|
||||
to 0 if no error occurred or to EIO if an I/O error occurred.
|
||||
UHID_GET_REPORT_REPLY:
|
||||
If you receive a UHID_GET_REPORT request you must answer with this request.
|
||||
You must copy the "id" field from the request into the answer. Set the "err"
|
||||
field to 0 if no error occurred or to EIO if an I/O error occurred.
|
||||
If "err" is 0 then you should fill the buffer of the answer with the results
|
||||
of the feature request and set "size" correspondingly.
|
||||
of the GET_REPORT request and set "size" correspondingly.
|
||||
|
||||
UHID_SET_REPORT_REPLY:
|
||||
This is the SET_REPORT equivalent of UHID_GET_REPORT_REPLY. Unlike GET_REPORT,
|
||||
SET_REPORT never returns a data buffer, therefore, it's sufficient to set the
|
||||
"id" and "err" fields correctly.
|
||||
|
||||
read()
|
||||
------
|
||||
read() will return a queued output report. These output reports can be of type
|
||||
UHID_START, UHID_STOP, UHID_OPEN, UHID_CLOSE, UHID_OUTPUT or UHID_OUTPUT_EV. No
|
||||
reaction is required to any of them but you should handle them according to your
|
||||
needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads.
|
||||
read() will return a queued output report. No reaction is required to any of
|
||||
them but you should handle them according to your needs.
|
||||
|
||||
UHID_START:
|
||||
This is sent when the HID device is started. Consider this as an answer to
|
||||
UHID_CREATE. This is always the first event that is sent.
|
||||
UHID_CREATE2. This is always the first event that is sent. Note that this
|
||||
event might not be available immediately after write(UHID_CREATE2) returns.
|
||||
Device drivers might required delayed setups.
|
||||
This event contains a payload of type uhid_start_req. The "dev_flags" field
|
||||
describes special behaviors of a device. The following flags are defined:
|
||||
UHID_DEV_NUMBERED_FEATURE_REPORTS:
|
||||
UHID_DEV_NUMBERED_OUTPUT_REPORTS:
|
||||
UHID_DEV_NUMBERED_INPUT_REPORTS:
|
||||
Each of these flags defines whether a given report-type uses numbered
|
||||
reports. If numbered reports are used for a type, all messages from
|
||||
the kernel already have the report-number as prefix. Otherwise, no
|
||||
prefix is added by the kernel.
|
||||
For messages sent by user-space to the kernel, you must adjust the
|
||||
prefixes according to these flags.
|
||||
|
||||
UHID_STOP:
|
||||
This is sent when the HID device is stopped. Consider this as an answer to
|
||||
UHID_DESTROY.
|
||||
If the kernel HID device driver closes the device manually (that is, you
|
||||
didn't send UHID_DESTROY) then you should consider this device closed and send
|
||||
an UHID_DESTROY event. You may want to reregister your device, though. This is
|
||||
always the last message that is sent to you unless you reopen the device with
|
||||
UHID_CREATE.
|
||||
If you didn't destroy your device via UHID_DESTROY, but the kernel sends an
|
||||
UHID_STOP event, this should usually be ignored. It means that the kernel
|
||||
reloaded/changed the device driver loaded on your HID device (or some other
|
||||
maintenance actions happened).
|
||||
You can usually ignored any UHID_STOP events safely.
|
||||
|
||||
UHID_OPEN:
|
||||
This is sent when the HID device is opened. That is, the data that the HID
|
||||
device provides is read by some other process. You may ignore this event but
|
||||
it is useful for power-management. As long as you haven't received this event
|
||||
there is actually no other process that reads your data so there is no need to
|
||||
send UHID_INPUT events to the kernel.
|
||||
send UHID_INPUT2 events to the kernel.
|
||||
|
||||
UHID_CLOSE:
|
||||
This is sent when there are no more processes which read the HID data. It is
|
||||
|
@ -156,27 +159,29 @@ needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads.
|
|||
|
||||
UHID_OUTPUT:
|
||||
This is sent if the HID device driver wants to send raw data to the I/O
|
||||
device. You should read the payload and forward it to the device. The payload
|
||||
is of type "struct uhid_data_req".
|
||||
device on the interrupt channel. You should read the payload and forward it to
|
||||
the device. The payload is of type "struct uhid_data_req".
|
||||
This may be received even though you haven't received UHID_OPEN, yet.
|
||||
|
||||
UHID_OUTPUT_EV (obsolete):
|
||||
Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This
|
||||
is called for force-feedback, LED or similar events which are received through
|
||||
an input device by the HID subsystem. You should convert this into raw reports
|
||||
and send them to your device similar to events of type UHID_OUTPUT.
|
||||
This is no longer sent by newer kernels. Instead, HID core converts it into a
|
||||
raw output report and sends it via UHID_OUTPUT.
|
||||
UHID_GET_REPORT:
|
||||
This event is sent if the kernel driver wants to perform a GET_REPORT request
|
||||
on the control channeld as described in the HID specs. The report-type and
|
||||
report-number are available in the payload.
|
||||
The kernel serializes GET_REPORT requests so there will never be two in
|
||||
parallel. However, if you fail to respond with a UHID_GET_REPORT_REPLY, the
|
||||
request might silently time out.
|
||||
Once you read a GET_REPORT request, you shall forward it to the hid device and
|
||||
remember the "id" field in the payload. Once your hid device responds to the
|
||||
GET_REPORT (or if it fails), you must send a UHID_GET_REPORT_REPLY to the
|
||||
kernel with the exact same "id" as in the request. If the request already
|
||||
timed out, the kernel will ignore the response silently. The "id" field is
|
||||
never re-used, so conflicts cannot happen.
|
||||
|
||||
UHID_FEATURE:
|
||||
This event is sent if the kernel driver wants to perform a feature request as
|
||||
described in the HID specs. The report-type and report-number are available in
|
||||
the payload.
|
||||
The kernel serializes feature requests so there will never be two in parallel.
|
||||
However, if you fail to respond with a UHID_FEATURE_ANSWER in a time-span of 5
|
||||
seconds, then the requests will be dropped and a new one might be sent.
|
||||
Therefore, the payload also contains an "id" field that identifies every
|
||||
request.
|
||||
UHID_SET_REPORT:
|
||||
This is the SET_REPORT equivalent of UHID_GET_REPORT. On receipt, you shall
|
||||
send a SET_REPORT request to your hid device. Once it replies, you must tell
|
||||
the kernel about it via UHID_SET_REPORT_REPLY.
|
||||
The same restrictions as for UHID_GET_REPORT apply.
|
||||
|
||||
Document by:
|
||||
David Herrmann <dh.herrmann@googlemail.com>
|
||||
----------------------------------------------------
|
||||
Written 2012, David Herrmann <dh.herrmann@gmail.com>
|
||||
|
|
|
@ -530,6 +530,17 @@ config PANTHERLORD_FF
|
|||
Say Y here if you have a PantherLord/GreenAsia based game controller
|
||||
or adapter and want to enable force feedback support for it.
|
||||
|
||||
config HID_PENMOUNT
|
||||
tristate "Penmount touch device"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
This selects a driver for the PenMount 6000 touch controller.
|
||||
|
||||
The driver works around a problem in the report descript allowing
|
||||
the userspace to touch events instead of mouse events.
|
||||
|
||||
Say Y here if you have a Penmount based touch controller.
|
||||
|
||||
config HID_PETALYNX
|
||||
tristate "Petalynx Maxter remote control"
|
||||
depends on HID
|
||||
|
|
|
@ -71,6 +71,7 @@ obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o
|
|||
obj-$(CONFIG_HID_ORTEK) += hid-ortek.o
|
||||
obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
|
||||
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
|
||||
obj-$(CONFIG_HID_PENMOUNT) += hid-penmount.o
|
||||
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
|
||||
obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o
|
||||
hid-picolcd-y += hid-picolcd_core.o
|
||||
|
|
|
@ -52,7 +52,7 @@ EXPORT_SYMBOL_GPL(hid_debug);
|
|||
|
||||
static int hid_ignore_special_drivers = 0;
|
||||
module_param_named(ignore_special_drivers, hid_ignore_special_drivers, int, 0600);
|
||||
MODULE_PARM_DESC(debug, "Ignore any special drivers and handle all devices by generic driver");
|
||||
MODULE_PARM_DESC(ignore_special_drivers, "Ignore any special drivers and handle all devices by generic driver");
|
||||
|
||||
/*
|
||||
* Register a new report for a device.
|
||||
|
@ -1591,6 +1591,9 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
|
|||
if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev))
|
||||
hdev->claimed |= HID_CLAIMED_HIDRAW;
|
||||
|
||||
if (connect_mask & HID_CONNECT_DRIVER)
|
||||
hdev->claimed |= HID_CLAIMED_DRIVER;
|
||||
|
||||
/* Drivers with the ->raw_event callback set are not required to connect
|
||||
* to any other listener. */
|
||||
if (!hdev->claimed && !hdev->driver->raw_event) {
|
||||
|
@ -1793,6 +1796,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) },
|
||||
|
@ -1880,6 +1884,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_6000) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
|
||||
#if IS_ENABLED(CONFIG_HID_ROCCAT)
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
* and Zalman ZM-GM1
|
||||
* - USB ID 04d9:a081, sold as SHARKOON DarkGlider Gaming mouse
|
||||
* - USB ID 04d9:a072, sold as LEETGION Hellion Gaming Mouse
|
||||
* - USB ID 04d9:a0c2, sold as ETEKCITY Scroll T-140 Gaming Mouse
|
||||
*/
|
||||
|
||||
static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
|
@ -42,6 +43,7 @@ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067:
|
||||
case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072:
|
||||
case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2:
|
||||
if (*rsize >= 122 && rdesc[115] == 0xff && rdesc[116] == 0x7f
|
||||
&& rdesc[120] == 0xff && rdesc[121] == 0x7f) {
|
||||
hid_info(hdev, "Fixing up report descriptor\n");
|
||||
|
@ -74,6 +76,8 @@ static const struct hid_device_id holtek_mouse_devices[] = {
|
|||
USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
|
||||
USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
|
||||
USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, holtek_mouse_devices);
|
||||
|
|
|
@ -296,6 +296,9 @@
|
|||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7
|
||||
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
|
||||
|
||||
#define USB_VENDOR_ID_ELAN 0x04f3
|
||||
#define USB_DEVICE_ID_ELAN_TOUCHSCREEN 0x0089
|
||||
|
||||
#define USB_VENDOR_ID_ELECOM 0x056e
|
||||
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
|
||||
|
||||
|
@ -479,6 +482,7 @@
|
|||
#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070 0xa070
|
||||
#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072 0xa072
|
||||
#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081 0xa081
|
||||
#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2 0xa0c2
|
||||
#define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096 0xa096
|
||||
|
||||
#define USB_VENDOR_ID_IMATION 0x0718
|
||||
|
@ -722,6 +726,7 @@
|
|||
#define USB_DEVICE_ID_PENMOUNT_PCI 0x3500
|
||||
#define USB_DEVICE_ID_PENMOUNT_1610 0x1610
|
||||
#define USB_DEVICE_ID_PENMOUNT_1640 0x1640
|
||||
#define USB_DEVICE_ID_PENMOUNT_6000 0x6000
|
||||
|
||||
#define USB_VENDOR_ID_PETALYNX 0x18b1
|
||||
#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037
|
||||
|
@ -733,6 +738,8 @@
|
|||
#define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff
|
||||
|
||||
#define USB_VENDOR_ID_PIXART 0x093a
|
||||
#define USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2 0x0137
|
||||
#define USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE 0x2510
|
||||
#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
|
||||
|
|
|
@ -599,6 +599,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
/* These usage IDs map directly to the usage codes. */
|
||||
case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
|
||||
case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
|
||||
if (field->flags & HID_MAIN_ITEM_RELATIVE)
|
||||
map_rel(usage->hid & 0xf);
|
||||
else
|
||||
map_abs_clear(usage->hid & 0xf);
|
||||
break;
|
||||
|
||||
case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
|
||||
if (field->flags & HID_MAIN_ITEM_RELATIVE)
|
||||
map_rel(usage->hid & 0xf);
|
||||
|
|
|
@ -385,18 +385,6 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
|
|||
|
||||
djdev = djrcv_dev->paired_dj_devices[dj_report->device_index];
|
||||
|
||||
if (!djdev) {
|
||||
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
|
||||
" is NULL, index %d\n", dj_report->device_index);
|
||||
kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
|
||||
|
||||
if (schedule_work(&djrcv_dev->work) == 0) {
|
||||
dbg_hid("%s: did not schedule the work item, was already "
|
||||
"queued\n", __func__);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
memset(reportbuffer, 0, sizeof(reportbuffer));
|
||||
|
||||
for (i = 0; i < NUMBER_OF_HID_REPORTS; i++) {
|
||||
|
@ -421,18 +409,6 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
|
|||
|
||||
dj_device = djrcv_dev->paired_dj_devices[dj_report->device_index];
|
||||
|
||||
if (dj_device == NULL) {
|
||||
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
|
||||
" is NULL, index %d\n", dj_report->device_index);
|
||||
kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
|
||||
|
||||
if (schedule_work(&djrcv_dev->work) == 0) {
|
||||
dbg_hid("%s: did not schedule the work item, was already "
|
||||
"queued\n", __func__);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((dj_report->report_type > ARRAY_SIZE(hid_reportid_size_map) - 1) ||
|
||||
(hid_reportid_size_map[dj_report->report_type] == 0)) {
|
||||
dbg_hid("invalid report type:%x\n", dj_report->report_type);
|
||||
|
@ -701,8 +677,17 @@ static int logi_dj_raw_event(struct hid_device *hdev,
|
|||
}
|
||||
|
||||
spin_lock_irqsave(&djrcv_dev->lock, flags);
|
||||
|
||||
if (!djrcv_dev->paired_dj_devices[dj_report->device_index]) {
|
||||
/* received an event for an unknown device, bail out */
|
||||
logi_dj_recv_queue_notification(djrcv_dev, dj_report);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (dj_report->report_type) {
|
||||
case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
|
||||
/* pairing notifications are handled above the switch */
|
||||
break;
|
||||
case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
|
||||
logi_dj_recv_queue_notification(djrcv_dev, dj_report);
|
||||
break;
|
||||
|
@ -715,6 +700,8 @@ static int logi_dj_raw_event(struct hid_device *hdev,
|
|||
default:
|
||||
logi_dj_recv_forward_report(djrcv_dev, dj_report);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
|
||||
|
||||
return true;
|
||||
|
|
49
drivers/hid/hid-penmount.c
Normal file
49
drivers/hid/hid-penmount.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* HID driver for PenMount touchscreens
|
||||
*
|
||||
* Copyright (c) 2014 Christian Gmeiner <christian.gmeiner <at> gmail.com>
|
||||
*
|
||||
* based on hid-penmount copyrighted by
|
||||
* PenMount Touch Solutions <penmount <at> seed.net.tw>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/hid.h>
|
||||
#include "hid-ids.h"
|
||||
|
||||
static int penmount_input_mapping(struct hid_device *hdev,
|
||||
struct hid_input *hi, struct hid_field *field,
|
||||
struct hid_usage *usage, unsigned long **bit, int *max)
|
||||
{
|
||||
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
|
||||
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id penmount_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_6000) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, penmount_devices);
|
||||
|
||||
static struct hid_driver penmount_driver = {
|
||||
.name = "hid-penmount",
|
||||
.id_table = penmount_devices,
|
||||
.input_mapping = penmount_input_mapping,
|
||||
};
|
||||
|
||||
module_hid_driver(penmount_driver);
|
||||
|
||||
MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
|
||||
MODULE_DESCRIPTION("PenMount HID TouchScreen driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -351,8 +351,8 @@ static int picolcd_raw_event(struct hid_device *hdev,
|
|||
return 1;
|
||||
|
||||
if (size > 64) {
|
||||
hid_warn(hdev, "invalid size value (%d) for picolcd raw event\n",
|
||||
size);
|
||||
hid_warn(hdev, "invalid size value (%d) for picolcd raw event (%d)\n",
|
||||
size, report->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -320,10 +320,7 @@ static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data,
|
|||
int offset;
|
||||
int i;
|
||||
|
||||
if (size < hdata->f11.report_size)
|
||||
return 0;
|
||||
|
||||
if (!(irq & hdata->f11.irq_mask))
|
||||
if (!(irq & hdata->f11.irq_mask) || size <= 0)
|
||||
return 0;
|
||||
|
||||
offset = (hdata->max_fingers >> 2) + 1;
|
||||
|
@ -332,9 +329,19 @@ static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data,
|
|||
int fs_bit_position = (i & 0x3) << 1;
|
||||
int finger_state = (data[fs_byte_position] >> fs_bit_position) &
|
||||
0x03;
|
||||
int position = offset + 5 * i;
|
||||
|
||||
rmi_f11_process_touch(hdata, i, finger_state,
|
||||
&data[offset + 5 * i]);
|
||||
if (position + 5 > size) {
|
||||
/* partial report, go on with what we received */
|
||||
printk_once(KERN_WARNING
|
||||
"%s %s: Detected incomplete finger report. Finger reports may occasionally get dropped on this platform.\n",
|
||||
dev_driver_string(&hdev->dev),
|
||||
dev_name(&hdev->dev));
|
||||
hid_dbg(hdev, "Incomplete finger report\n");
|
||||
break;
|
||||
}
|
||||
|
||||
rmi_f11_process_touch(hdata, i, finger_state, &data[position]);
|
||||
}
|
||||
input_mt_sync_frame(hdata->input);
|
||||
input_sync(hdata->input);
|
||||
|
@ -352,6 +359,11 @@ static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data,
|
|||
if (!(irq & hdata->f30.irq_mask))
|
||||
return 0;
|
||||
|
||||
if (size < (int)hdata->f30.report_size) {
|
||||
hid_warn(hdev, "Click Button pressed, but the click data is missing\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < hdata->gpio_led_count; i++) {
|
||||
if (test_bit(i, &hdata->button_mask)) {
|
||||
value = (data[i / 8] >> (i & 0x07)) & BIT(0);
|
||||
|
@ -412,9 +424,29 @@ static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int rmi_check_sanity(struct hid_device *hdev, u8 *data, int size)
|
||||
{
|
||||
int valid_size = size;
|
||||
/*
|
||||
* On the Dell XPS 13 9333, the bus sometimes get confused and fills
|
||||
* the report with a sentinel value "ff". Synaptics told us that such
|
||||
* behavior does not comes from the touchpad itself, so we filter out
|
||||
* such reports here.
|
||||
*/
|
||||
|
||||
while ((data[valid_size - 1] == 0xff) && valid_size > 0)
|
||||
valid_size--;
|
||||
|
||||
return valid_size;
|
||||
}
|
||||
|
||||
static int rmi_raw_event(struct hid_device *hdev,
|
||||
struct hid_report *report, u8 *data, int size)
|
||||
{
|
||||
size = rmi_check_sanity(hdev, data, size);
|
||||
if (size < 2)
|
||||
return 0;
|
||||
|
||||
switch (data[0]) {
|
||||
case RMI_READ_DATA_REPORT_ID:
|
||||
return rmi_read_data_event(hdev, data, size);
|
||||
|
|
|
@ -708,6 +708,9 @@ static const struct hid_device_id sensor_hub_devices[] = {
|
|||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
|
||||
USB_DEVICE_ID_MS_TYPE_COVER_2),
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
|
||||
USB_DEVICE_ID_STM_HID_SENSOR),
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
|
||||
USB_DEVICE_ID_STM_HID_SENSOR_1),
|
||||
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* HID driver for Sony / PS2 / PS3 BD devices.
|
||||
* HID driver for Sony / PS2 / PS3 / PS4 BD devices.
|
||||
*
|
||||
* Copyright (c) 1999 Andreas Gal
|
||||
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||
|
@ -8,6 +8,7 @@
|
|||
* Copyright (c) 2012 David Dillow <dave@thedillows.org>
|
||||
* Copyright (c) 2006-2013 Jiri Kosina
|
||||
* Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
|
||||
* Copyright (c) 2014 Frank Praznik <frank.praznik@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -176,7 +177,7 @@ static u8 dualshock4_usb_rdesc[] = {
|
|||
0x75, 0x06, /* Report Size (6), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x25, 0x7F, /* Logical Maximum (127), */
|
||||
0x25, 0x3F, /* Logical Maximum (63), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x33, /* Usage (Rx), */
|
||||
|
@ -200,14 +201,14 @@ static u8 dualshock4_usb_rdesc[] = {
|
|||
0x81, 0x02, /* Input (Variable), */
|
||||
0x19, 0x43, /* Usage Minimum (43h), */
|
||||
0x29, 0x45, /* Usage Maximum (45h), */
|
||||
0x16, 0xFF, 0xBF, /* Logical Minimum (-16385), */
|
||||
0x26, 0x00, 0x40, /* Logical Maximum (16384), */
|
||||
0x16, 0x00, 0xE0, /* Logical Minimum (-8192), */
|
||||
0x26, 0xFF, 0x1F, /* Logical Maximum (8191), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
0x09, 0x21, /* Usage (21h), */
|
||||
0x15, 0x00, /* Logical Minimum (0), */
|
||||
0x25, 0xFF, /* Logical Maximum (255), */
|
||||
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x95, 0x27, /* Report Count (39), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
|
@ -395,11 +396,11 @@ static u8 dualshock4_usb_rdesc[] = {
|
|||
|
||||
/*
|
||||
* The default behavior of the Dualshock 4 is to send reports using report
|
||||
* type 1 when running over Bluetooth. However, as soon as it receives a
|
||||
* report of type 17 to set the LEDs or rumble it starts returning it's state
|
||||
* in report 17 instead of 1. Since report 17 is undefined in the default HID
|
||||
* type 1 when running over Bluetooth. However, when feature report 2 is
|
||||
* requested during the controller initialization it starts sending input
|
||||
* reports in report 17. Since report 17 is undefined in the default HID
|
||||
* descriptor the button and axis definitions must be moved to report 17 or
|
||||
* the HID layer won't process the received input once a report is sent.
|
||||
* the HID layer won't process the received input.
|
||||
*/
|
||||
static u8 dualshock4_bt_rdesc[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
|
@ -509,8 +510,8 @@ static u8 dualshock4_bt_rdesc[] = {
|
|||
0x81, 0x02, /* Input (Variable), */
|
||||
0x19, 0x43, /* Usage Minimum (43h), */
|
||||
0x29, 0x45, /* Usage Maximum (45h), */
|
||||
0x16, 0xFF, 0xBF, /* Logical Minimum (-16385), */
|
||||
0x26, 0x00, 0x40, /* Logical Maximum (16384), */
|
||||
0x16, 0x00, 0xE0, /* Logical Minimum (-8192), */
|
||||
0x26, 0xFF, 0x1F, /* Logical Maximum (8191), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
||||
|
@ -935,12 +936,13 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
|
|||
if (rd[30] >= 0xee) {
|
||||
battery_capacity = 100;
|
||||
battery_charging = !(rd[30] & 0x01);
|
||||
cable_state = 1;
|
||||
} else {
|
||||
__u8 index = rd[30] <= 5 ? rd[30] : 5;
|
||||
battery_capacity = sixaxis_battery_capacity[index];
|
||||
battery_charging = 0;
|
||||
cable_state = 0;
|
||||
}
|
||||
cable_state = !(rd[31] & 0x04);
|
||||
|
||||
spin_lock_irqsave(&sc->lock, flags);
|
||||
sc->cable_state = cable_state;
|
||||
|
@ -1082,6 +1084,38 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sony_register_touchpad(struct hid_input *hi, int touch_count,
|
||||
int w, int h)
|
||||
{
|
||||
struct input_dev *input_dev = hi->input;
|
||||
int ret;
|
||||
|
||||
ret = input_mt_init_slots(input_dev, touch_count, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sony_input_configured(struct hid_device *hdev,
|
||||
struct hid_input *hidinput)
|
||||
{
|
||||
struct sony_sc *sc = hid_get_drvdata(hdev);
|
||||
|
||||
/*
|
||||
* The Dualshock 4 touchpad supports 2 touches and has a
|
||||
* resolution of 1920x942 (44.86 dots/mm).
|
||||
*/
|
||||
if (sc->quirks & DUALSHOCK4_CONTROLLER) {
|
||||
if (sony_register_touchpad(hidinput, 2, 1920, 942) != 0)
|
||||
hid_err(sc->hdev,
|
||||
"Unable to initialize multi-touch slots\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
|
||||
* to "operational". Without this, the ps3 controller will not report any
|
||||
|
@ -1654,26 +1688,6 @@ static void sony_battery_remove(struct sony_sc *sc)
|
|||
sc->battery.name = NULL;
|
||||
}
|
||||
|
||||
static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
|
||||
int w, int h)
|
||||
{
|
||||
struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
|
||||
struct hid_input, list);
|
||||
struct input_dev *input_dev = hidinput->input;
|
||||
int ret;
|
||||
|
||||
ret = input_mt_init_slots(input_dev, touch_count, 0);
|
||||
if (ret < 0) {
|
||||
hid_err(sc->hdev, "Unable to initialize multi-touch slots\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a controller is plugged in via USB while already connected via Bluetooth
|
||||
* it will show up as two devices. A global list of connected controllers and
|
||||
|
@ -1923,13 +1937,6 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
goto err_stop;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The Dualshock 4 touchpad supports 2 touches and has a
|
||||
* resolution of 1920x940.
|
||||
*/
|
||||
ret = sony_register_touchpad(sc, 2, 1920, 940);
|
||||
if (ret < 0)
|
||||
goto err_stop;
|
||||
|
||||
sony_init_work(sc, dualshock4_state_worker);
|
||||
} else {
|
||||
|
@ -2037,13 +2044,14 @@ static const struct hid_device_id sony_devices[] = {
|
|||
MODULE_DEVICE_TABLE(hid, sony_devices);
|
||||
|
||||
static struct hid_driver sony_driver = {
|
||||
.name = "sony",
|
||||
.id_table = sony_devices,
|
||||
.input_mapping = sony_mapping,
|
||||
.probe = sony_probe,
|
||||
.remove = sony_remove,
|
||||
.report_fixup = sony_report_fixup,
|
||||
.raw_event = sony_raw_event
|
||||
.name = "sony",
|
||||
.id_table = sony_devices,
|
||||
.input_mapping = sony_mapping,
|
||||
.input_configured = sony_input_configured,
|
||||
.probe = sony_probe,
|
||||
.remove = sony_remove,
|
||||
.report_fixup = sony_report_fixup,
|
||||
.raw_event = sony_raw_event
|
||||
};
|
||||
|
||||
static int __init sony_init(void)
|
||||
|
|
|
@ -208,10 +208,10 @@ static int thingm_init_rgb(struct thingm_rgb *rgb)
|
|||
|
||||
static void thingm_remove_rgb(struct thingm_rgb *rgb)
|
||||
{
|
||||
flush_work(&rgb->work);
|
||||
led_classdev_unregister(&rgb->red.ldev);
|
||||
led_classdev_unregister(&rgb->green.ldev);
|
||||
led_classdev_unregister(&rgb->blue.ldev);
|
||||
flush_work(&rgb->work);
|
||||
}
|
||||
|
||||
static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
|
@ -250,6 +250,7 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
|
||||
if (!tdev->fwinfo) {
|
||||
hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
|
||||
err = -ENODEV;
|
||||
goto stop;
|
||||
}
|
||||
|
||||
|
@ -286,10 +287,10 @@ static void thingm_remove(struct hid_device *hdev)
|
|||
struct thingm_device *tdev = hid_get_drvdata(hdev);
|
||||
int i;
|
||||
|
||||
hid_hw_stop(hdev);
|
||||
|
||||
for (i = 0; i < tdev->fwinfo->numrgb; ++i)
|
||||
thingm_remove_rgb(tdev->rgb + i);
|
||||
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static const struct hid_device_id thingm_table[] = {
|
||||
|
|
|
@ -44,10 +44,12 @@ struct uhid_device {
|
|||
__u8 tail;
|
||||
struct uhid_event *outq[UHID_BUFSIZE];
|
||||
|
||||
/* blocking GET_REPORT support; state changes protected by qlock */
|
||||
struct mutex report_lock;
|
||||
wait_queue_head_t report_wait;
|
||||
atomic_t report_done;
|
||||
atomic_t report_id;
|
||||
bool report_running;
|
||||
u32 report_id;
|
||||
u32 report_type;
|
||||
struct uhid_event report_buf;
|
||||
};
|
||||
|
||||
|
@ -90,8 +92,27 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event)
|
|||
static int uhid_hid_start(struct hid_device *hid)
|
||||
{
|
||||
struct uhid_device *uhid = hid->driver_data;
|
||||
struct uhid_event *ev;
|
||||
unsigned long flags;
|
||||
|
||||
return uhid_queue_event(uhid, UHID_START);
|
||||
ev = kzalloc(sizeof(*ev), GFP_KERNEL);
|
||||
if (!ev)
|
||||
return -ENOMEM;
|
||||
|
||||
ev->type = UHID_START;
|
||||
|
||||
if (hid->report_enum[HID_FEATURE_REPORT].numbered)
|
||||
ev->u.start.dev_flags |= UHID_DEV_NUMBERED_FEATURE_REPORTS;
|
||||
if (hid->report_enum[HID_OUTPUT_REPORT].numbered)
|
||||
ev->u.start.dev_flags |= UHID_DEV_NUMBERED_OUTPUT_REPORTS;
|
||||
if (hid->report_enum[HID_INPUT_REPORT].numbered)
|
||||
ev->u.start.dev_flags |= UHID_DEV_NUMBERED_INPUT_REPORTS;
|
||||
|
||||
spin_lock_irqsave(&uhid->qlock, flags);
|
||||
uhid_queue(uhid, ev);
|
||||
spin_unlock_irqrestore(&uhid->qlock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void uhid_hid_stop(struct hid_device *hid)
|
||||
|
@ -123,87 +144,169 @@ static int uhid_hid_parse(struct hid_device *hid)
|
|||
return hid_parse_report(hid, uhid->rd_data, uhid->rd_size);
|
||||
}
|
||||
|
||||
static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum,
|
||||
__u8 *buf, size_t count, unsigned char rtype)
|
||||
/* must be called with report_lock held */
|
||||
static int __uhid_report_queue_and_wait(struct uhid_device *uhid,
|
||||
struct uhid_event *ev,
|
||||
__u32 *report_id)
|
||||
{
|
||||
struct uhid_device *uhid = hid->driver_data;
|
||||
__u8 report_type;
|
||||
struct uhid_event *ev;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
size_t uninitialized_var(len);
|
||||
struct uhid_feature_answer_req *req;
|
||||
|
||||
spin_lock_irqsave(&uhid->qlock, flags);
|
||||
*report_id = ++uhid->report_id;
|
||||
uhid->report_type = ev->type + 1;
|
||||
uhid->report_running = true;
|
||||
uhid_queue(uhid, ev);
|
||||
spin_unlock_irqrestore(&uhid->qlock, flags);
|
||||
|
||||
ret = wait_event_interruptible_timeout(uhid->report_wait,
|
||||
!uhid->report_running || !uhid->running,
|
||||
5 * HZ);
|
||||
if (!ret || !uhid->running || uhid->report_running)
|
||||
ret = -EIO;
|
||||
else if (ret < 0)
|
||||
ret = -ERESTARTSYS;
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
uhid->report_running = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void uhid_report_wake_up(struct uhid_device *uhid, u32 id,
|
||||
const struct uhid_event *ev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&uhid->qlock, flags);
|
||||
|
||||
/* id for old report; drop it silently */
|
||||
if (uhid->report_type != ev->type || uhid->report_id != id)
|
||||
goto unlock;
|
||||
if (!uhid->report_running)
|
||||
goto unlock;
|
||||
|
||||
memcpy(&uhid->report_buf, ev, sizeof(*ev));
|
||||
uhid->report_running = false;
|
||||
wake_up_interruptible(&uhid->report_wait);
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&uhid->qlock, flags);
|
||||
}
|
||||
|
||||
static int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum,
|
||||
u8 *buf, size_t count, u8 rtype)
|
||||
{
|
||||
struct uhid_device *uhid = hid->driver_data;
|
||||
struct uhid_get_report_reply_req *req;
|
||||
struct uhid_event *ev;
|
||||
int ret;
|
||||
|
||||
if (!uhid->running)
|
||||
return -EIO;
|
||||
|
||||
ev = kzalloc(sizeof(*ev), GFP_KERNEL);
|
||||
if (!ev)
|
||||
return -ENOMEM;
|
||||
|
||||
ev->type = UHID_GET_REPORT;
|
||||
ev->u.get_report.rnum = rnum;
|
||||
ev->u.get_report.rtype = rtype;
|
||||
|
||||
ret = mutex_lock_interruptible(&uhid->report_lock);
|
||||
if (ret) {
|
||||
kfree(ev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* this _always_ takes ownership of @ev */
|
||||
ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.get_report.id);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
req = &uhid->report_buf.u.get_report_reply;
|
||||
if (req->err) {
|
||||
ret = -EIO;
|
||||
} else {
|
||||
ret = min3(count, (size_t)req->size, (size_t)UHID_DATA_MAX);
|
||||
memcpy(buf, req->data, ret);
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&uhid->report_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uhid_hid_set_report(struct hid_device *hid, unsigned char rnum,
|
||||
const u8 *buf, size_t count, u8 rtype)
|
||||
{
|
||||
struct uhid_device *uhid = hid->driver_data;
|
||||
struct uhid_event *ev;
|
||||
int ret;
|
||||
|
||||
if (!uhid->running || count > UHID_DATA_MAX)
|
||||
return -EIO;
|
||||
|
||||
ev = kzalloc(sizeof(*ev), GFP_KERNEL);
|
||||
if (!ev)
|
||||
return -ENOMEM;
|
||||
|
||||
ev->type = UHID_SET_REPORT;
|
||||
ev->u.set_report.rnum = rnum;
|
||||
ev->u.set_report.rtype = rtype;
|
||||
ev->u.set_report.size = count;
|
||||
memcpy(ev->u.set_report.data, buf, count);
|
||||
|
||||
ret = mutex_lock_interruptible(&uhid->report_lock);
|
||||
if (ret) {
|
||||
kfree(ev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* this _always_ takes ownership of @ev */
|
||||
ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.set_report.id);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
if (uhid->report_buf.u.set_report_reply.err)
|
||||
ret = -EIO;
|
||||
else
|
||||
ret = count;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&uhid->report_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uhid_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
|
||||
__u8 *buf, size_t len, unsigned char rtype,
|
||||
int reqtype)
|
||||
{
|
||||
u8 u_rtype;
|
||||
|
||||
switch (rtype) {
|
||||
case HID_FEATURE_REPORT:
|
||||
report_type = UHID_FEATURE_REPORT;
|
||||
u_rtype = UHID_FEATURE_REPORT;
|
||||
break;
|
||||
case HID_OUTPUT_REPORT:
|
||||
report_type = UHID_OUTPUT_REPORT;
|
||||
u_rtype = UHID_OUTPUT_REPORT;
|
||||
break;
|
||||
case HID_INPUT_REPORT:
|
||||
report_type = UHID_INPUT_REPORT;
|
||||
u_rtype = UHID_INPUT_REPORT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mutex_lock_interruptible(&uhid->report_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ev = kzalloc(sizeof(*ev), GFP_KERNEL);
|
||||
if (!ev) {
|
||||
ret = -ENOMEM;
|
||||
goto unlock;
|
||||
switch (reqtype) {
|
||||
case HID_REQ_GET_REPORT:
|
||||
return uhid_hid_get_report(hid, reportnum, buf, len, u_rtype);
|
||||
case HID_REQ_SET_REPORT:
|
||||
return uhid_hid_set_report(hid, reportnum, buf, len, u_rtype);
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&uhid->qlock, flags);
|
||||
ev->type = UHID_FEATURE;
|
||||
ev->u.feature.id = atomic_inc_return(&uhid->report_id);
|
||||
ev->u.feature.rnum = rnum;
|
||||
ev->u.feature.rtype = report_type;
|
||||
|
||||
atomic_set(&uhid->report_done, 0);
|
||||
uhid_queue(uhid, ev);
|
||||
spin_unlock_irqrestore(&uhid->qlock, flags);
|
||||
|
||||
ret = wait_event_interruptible_timeout(uhid->report_wait,
|
||||
atomic_read(&uhid->report_done), 5 * HZ);
|
||||
|
||||
/*
|
||||
* Make sure "uhid->running" is cleared on shutdown before
|
||||
* "uhid->report_done" is set.
|
||||
*/
|
||||
smp_rmb();
|
||||
if (!ret || !uhid->running) {
|
||||
ret = -EIO;
|
||||
} else if (ret < 0) {
|
||||
ret = -ERESTARTSYS;
|
||||
} else {
|
||||
spin_lock_irqsave(&uhid->qlock, flags);
|
||||
req = &uhid->report_buf.u.feature_answer;
|
||||
|
||||
if (req->err) {
|
||||
ret = -EIO;
|
||||
} else {
|
||||
ret = 0;
|
||||
len = min(count,
|
||||
min_t(size_t, req->size, UHID_DATA_MAX));
|
||||
memcpy(buf, req->data, len);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&uhid->qlock, flags);
|
||||
}
|
||||
|
||||
atomic_set(&uhid->report_done, 1);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&uhid->report_lock);
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
|
||||
|
@ -250,29 +353,14 @@ static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
|
|||
return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT);
|
||||
}
|
||||
|
||||
static int uhid_raw_request(struct hid_device *hid, unsigned char reportnum,
|
||||
__u8 *buf, size_t len, unsigned char rtype,
|
||||
int reqtype)
|
||||
{
|
||||
switch (reqtype) {
|
||||
case HID_REQ_GET_REPORT:
|
||||
return uhid_hid_get_raw(hid, reportnum, buf, len, rtype);
|
||||
case HID_REQ_SET_REPORT:
|
||||
/* TODO: implement proper SET_REPORT functionality */
|
||||
return -ENOSYS;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static struct hid_ll_driver uhid_hid_driver = {
|
||||
.start = uhid_hid_start,
|
||||
.stop = uhid_hid_stop,
|
||||
.open = uhid_hid_open,
|
||||
.close = uhid_hid_close,
|
||||
.parse = uhid_hid_parse,
|
||||
.raw_request = uhid_hid_raw_request,
|
||||
.output_report = uhid_hid_output_report,
|
||||
.raw_request = uhid_raw_request,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
@ -363,101 +451,40 @@ static int uhid_event_from_user(const char __user *buffer, size_t len,
|
|||
}
|
||||
#endif
|
||||
|
||||
static int uhid_dev_create(struct uhid_device *uhid,
|
||||
const struct uhid_event *ev)
|
||||
{
|
||||
struct hid_device *hid;
|
||||
int ret;
|
||||
|
||||
if (uhid->running)
|
||||
return -EALREADY;
|
||||
|
||||
uhid->rd_size = ev->u.create.rd_size;
|
||||
if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL);
|
||||
if (!uhid->rd_data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(uhid->rd_data, ev->u.create.rd_data,
|
||||
uhid->rd_size)) {
|
||||
ret = -EFAULT;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
hid = hid_allocate_device();
|
||||
if (IS_ERR(hid)) {
|
||||
ret = PTR_ERR(hid);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
strncpy(hid->name, ev->u.create.name, 127);
|
||||
hid->name[127] = 0;
|
||||
strncpy(hid->phys, ev->u.create.phys, 63);
|
||||
hid->phys[63] = 0;
|
||||
strncpy(hid->uniq, ev->u.create.uniq, 63);
|
||||
hid->uniq[63] = 0;
|
||||
|
||||
hid->ll_driver = &uhid_hid_driver;
|
||||
hid->bus = ev->u.create.bus;
|
||||
hid->vendor = ev->u.create.vendor;
|
||||
hid->product = ev->u.create.product;
|
||||
hid->version = ev->u.create.version;
|
||||
hid->country = ev->u.create.country;
|
||||
hid->driver_data = uhid;
|
||||
hid->dev.parent = uhid_misc.this_device;
|
||||
|
||||
uhid->hid = hid;
|
||||
uhid->running = true;
|
||||
|
||||
ret = hid_add_device(hid);
|
||||
if (ret) {
|
||||
hid_err(hid, "Cannot register HID device\n");
|
||||
goto err_hid;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_hid:
|
||||
hid_destroy_device(hid);
|
||||
uhid->hid = NULL;
|
||||
uhid->running = false;
|
||||
err_free:
|
||||
kfree(uhid->rd_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uhid_dev_create2(struct uhid_device *uhid,
|
||||
const struct uhid_event *ev)
|
||||
{
|
||||
struct hid_device *hid;
|
||||
size_t rd_size, len;
|
||||
void *rd_data;
|
||||
int ret;
|
||||
|
||||
if (uhid->running)
|
||||
return -EALREADY;
|
||||
|
||||
uhid->rd_size = ev->u.create2.rd_size;
|
||||
if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
|
||||
rd_size = ev->u.create2.rd_size;
|
||||
if (rd_size <= 0 || rd_size > HID_MAX_DESCRIPTOR_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
uhid->rd_data = kmemdup(ev->u.create2.rd_data, uhid->rd_size,
|
||||
GFP_KERNEL);
|
||||
if (!uhid->rd_data)
|
||||
rd_data = kmemdup(ev->u.create2.rd_data, rd_size, GFP_KERNEL);
|
||||
if (!rd_data)
|
||||
return -ENOMEM;
|
||||
|
||||
uhid->rd_size = rd_size;
|
||||
uhid->rd_data = rd_data;
|
||||
|
||||
hid = hid_allocate_device();
|
||||
if (IS_ERR(hid)) {
|
||||
ret = PTR_ERR(hid);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
strncpy(hid->name, ev->u.create2.name, 127);
|
||||
hid->name[127] = 0;
|
||||
strncpy(hid->phys, ev->u.create2.phys, 63);
|
||||
hid->phys[63] = 0;
|
||||
strncpy(hid->uniq, ev->u.create2.uniq, 63);
|
||||
hid->uniq[63] = 0;
|
||||
len = min(sizeof(hid->name), sizeof(ev->u.create2.name)) - 1;
|
||||
strncpy(hid->name, ev->u.create2.name, len);
|
||||
len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)) - 1;
|
||||
strncpy(hid->phys, ev->u.create2.phys, len);
|
||||
len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)) - 1;
|
||||
strncpy(hid->uniq, ev->u.create2.uniq, len);
|
||||
|
||||
hid->ll_driver = &uhid_hid_driver;
|
||||
hid->bus = ev->u.create2.bus;
|
||||
|
@ -485,18 +512,42 @@ static int uhid_dev_create2(struct uhid_device *uhid,
|
|||
uhid->running = false;
|
||||
err_free:
|
||||
kfree(uhid->rd_data);
|
||||
uhid->rd_data = NULL;
|
||||
uhid->rd_size = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uhid_dev_create(struct uhid_device *uhid,
|
||||
struct uhid_event *ev)
|
||||
{
|
||||
struct uhid_create_req orig;
|
||||
|
||||
orig = ev->u.create;
|
||||
|
||||
if (orig.rd_size <= 0 || orig.rd_size > HID_MAX_DESCRIPTOR_SIZE)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&ev->u.create2.rd_data, orig.rd_data, orig.rd_size))
|
||||
return -EFAULT;
|
||||
|
||||
memcpy(ev->u.create2.name, orig.name, sizeof(orig.name));
|
||||
memcpy(ev->u.create2.phys, orig.phys, sizeof(orig.phys));
|
||||
memcpy(ev->u.create2.uniq, orig.uniq, sizeof(orig.uniq));
|
||||
ev->u.create2.rd_size = orig.rd_size;
|
||||
ev->u.create2.bus = orig.bus;
|
||||
ev->u.create2.vendor = orig.vendor;
|
||||
ev->u.create2.product = orig.product;
|
||||
ev->u.create2.version = orig.version;
|
||||
ev->u.create2.country = orig.country;
|
||||
|
||||
return uhid_dev_create2(uhid, ev);
|
||||
}
|
||||
|
||||
static int uhid_dev_destroy(struct uhid_device *uhid)
|
||||
{
|
||||
if (!uhid->running)
|
||||
return -EINVAL;
|
||||
|
||||
/* clear "running" before setting "report_done" */
|
||||
uhid->running = false;
|
||||
smp_wmb();
|
||||
atomic_set(&uhid->report_done, 1);
|
||||
wake_up_interruptible(&uhid->report_wait);
|
||||
|
||||
hid_destroy_device(uhid->hid);
|
||||
|
@ -527,28 +578,23 @@ static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int uhid_dev_feature_answer(struct uhid_device *uhid,
|
||||
struct uhid_event *ev)
|
||||
static int uhid_dev_get_report_reply(struct uhid_device *uhid,
|
||||
struct uhid_event *ev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!uhid->running)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&uhid->qlock, flags);
|
||||
uhid_report_wake_up(uhid, ev->u.get_report_reply.id, ev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* id for old report; drop it silently */
|
||||
if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id)
|
||||
goto unlock;
|
||||
if (atomic_read(&uhid->report_done))
|
||||
goto unlock;
|
||||
static int uhid_dev_set_report_reply(struct uhid_device *uhid,
|
||||
struct uhid_event *ev)
|
||||
{
|
||||
if (!uhid->running)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&uhid->report_buf, ev, sizeof(*ev));
|
||||
atomic_set(&uhid->report_done, 1);
|
||||
wake_up_interruptible(&uhid->report_wait);
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&uhid->qlock, flags);
|
||||
uhid_report_wake_up(uhid, ev->u.set_report_reply.id, ev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -566,7 +612,6 @@ static int uhid_char_open(struct inode *inode, struct file *file)
|
|||
init_waitqueue_head(&uhid->waitq);
|
||||
init_waitqueue_head(&uhid->report_wait);
|
||||
uhid->running = false;
|
||||
atomic_set(&uhid->report_done, 1);
|
||||
|
||||
file->private_data = uhid;
|
||||
nonseekable_open(inode, file);
|
||||
|
@ -675,8 +720,11 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
|
|||
case UHID_INPUT2:
|
||||
ret = uhid_dev_input2(uhid, &uhid->input_buf);
|
||||
break;
|
||||
case UHID_FEATURE_ANSWER:
|
||||
ret = uhid_dev_feature_answer(uhid, &uhid->input_buf);
|
||||
case UHID_GET_REPORT_REPLY:
|
||||
ret = uhid_dev_get_report_reply(uhid, &uhid->input_buf);
|
||||
break;
|
||||
case UHID_SET_REPORT_REPLY:
|
||||
ret = uhid_dev_set_report_reply(uhid, &uhid->input_buf);
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
|
|
|
@ -82,7 +82,7 @@ static int hid_start_in(struct hid_device *hid)
|
|||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
|
||||
spin_lock_irqsave(&usbhid->lock, flags);
|
||||
if (hid->open > 0 &&
|
||||
if ((hid->open > 0 || hid->quirks & HID_QUIRK_ALWAYS_POLL) &&
|
||||
!test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
|
||||
!test_bit(HID_SUSPENDED, &usbhid->iofl) &&
|
||||
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
|
||||
|
@ -116,40 +116,24 @@ static void hid_reset(struct work_struct *work)
|
|||
struct usbhid_device *usbhid =
|
||||
container_of(work, struct usbhid_device, reset_work);
|
||||
struct hid_device *hid = usbhid->hid;
|
||||
int rc = 0;
|
||||
int rc;
|
||||
|
||||
if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
|
||||
dev_dbg(&usbhid->intf->dev, "clear halt\n");
|
||||
rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe);
|
||||
clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
|
||||
hid_start_in(hid);
|
||||
}
|
||||
|
||||
else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
|
||||
dev_dbg(&usbhid->intf->dev, "resetting device\n");
|
||||
rc = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf);
|
||||
if (rc == 0) {
|
||||
rc = usb_reset_device(hid_to_usb_dev(hid));
|
||||
usb_unlock_device(hid_to_usb_dev(hid));
|
||||
hid_start_in(hid);
|
||||
} else {
|
||||
dev_dbg(&usbhid->intf->dev,
|
||||
"clear-halt failed: %d\n", rc);
|
||||
set_bit(HID_RESET_PENDING, &usbhid->iofl);
|
||||
}
|
||||
clear_bit(HID_RESET_PENDING, &usbhid->iofl);
|
||||
}
|
||||
|
||||
switch (rc) {
|
||||
case 0:
|
||||
if (!test_bit(HID_IN_RUNNING, &usbhid->iofl))
|
||||
hid_io_error(hid);
|
||||
break;
|
||||
default:
|
||||
hid_err(hid, "can't reset device, %s-%s/input%d, status %d\n",
|
||||
hid_to_usb_dev(hid)->bus->bus_name,
|
||||
hid_to_usb_dev(hid)->devpath,
|
||||
usbhid->ifnum, rc);
|
||||
/* FALLTHROUGH */
|
||||
case -EHOSTUNREACH:
|
||||
case -ENODEV:
|
||||
case -EINTR:
|
||||
break;
|
||||
if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
|
||||
dev_dbg(&usbhid->intf->dev, "resetting device\n");
|
||||
usb_queue_reset_device(usbhid->intf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,6 +276,8 @@ static void hid_irq_in(struct urb *urb)
|
|||
case 0: /* success */
|
||||
usbhid_mark_busy(usbhid);
|
||||
usbhid->retry_delay = 0;
|
||||
if ((hid->quirks & HID_QUIRK_ALWAYS_POLL) && !hid->open)
|
||||
break;
|
||||
hid_input_report(urb->context, HID_INPUT_REPORT,
|
||||
urb->transfer_buffer,
|
||||
urb->actual_length, 1);
|
||||
|
@ -735,8 +721,10 @@ void usbhid_close(struct hid_device *hid)
|
|||
if (!--hid->open) {
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
hid_cancel_delayed_stuff(usbhid);
|
||||
usb_kill_urb(usbhid->urbin);
|
||||
usbhid->intf->needs_remote_wakeup = 0;
|
||||
if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) {
|
||||
usb_kill_urb(usbhid->urbin);
|
||||
usbhid->intf->needs_remote_wakeup = 0;
|
||||
}
|
||||
} else {
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
}
|
||||
|
@ -1134,6 +1122,19 @@ static int usbhid_start(struct hid_device *hid)
|
|||
|
||||
set_bit(HID_STARTED, &usbhid->iofl);
|
||||
|
||||
if (hid->quirks & HID_QUIRK_ALWAYS_POLL) {
|
||||
ret = usb_autopm_get_interface(usbhid->intf);
|
||||
if (ret)
|
||||
goto fail;
|
||||
usbhid->intf->needs_remote_wakeup = 1;
|
||||
ret = hid_start_in(hid);
|
||||
if (ret) {
|
||||
dev_err(&hid->dev,
|
||||
"failed to start in urb: %d\n", ret);
|
||||
}
|
||||
usb_autopm_put_interface(usbhid->intf);
|
||||
}
|
||||
|
||||
/* Some keyboards don't work until their LEDs have been set.
|
||||
* Since BIOSes do set the LEDs, it must be safe for any device
|
||||
* that supports the keyboard boot protocol.
|
||||
|
@ -1166,6 +1167,9 @@ static void usbhid_stop(struct hid_device *hid)
|
|||
if (WARN_ON(!usbhid))
|
||||
return;
|
||||
|
||||
if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
|
||||
usbhid->intf->needs_remote_wakeup = 0;
|
||||
|
||||
clear_bit(HID_STARTED, &usbhid->iofl);
|
||||
spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */
|
||||
set_bit(HID_DISCONNECTED, &usbhid->iofl);
|
||||
|
|
|
@ -70,6 +70,7 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
|
||||
{ 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_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
|
||||
|
@ -79,6 +80,8 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
|
||||
{ USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1610, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1640, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
|
||||
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2, HID_QUIRK_ALWAYS_POLL },
|
||||
{ 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 },
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
@ -143,4 +144,9 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
struct wacom_wac *wacom_wac);
|
||||
int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac);
|
||||
void wacom_wac_usage_mapping(struct hid_device *hdev,
|
||||
struct hid_field *field, struct hid_usage *usage);
|
||||
int wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value);
|
||||
void wacom_wac_report(struct hid_device *hdev, struct hid_report *report);
|
||||
#endif
|
||||
|
|
|
@ -13,23 +13,26 @@
|
|||
|
||||
#include "wacom_wac.h"
|
||||
#include "wacom.h"
|
||||
#include <linux/hid.h>
|
||||
|
||||
#define WAC_MSG_RETRIES 5
|
||||
|
||||
#define WAC_CMD_WL_LED_CONTROL 0x03
|
||||
#define WAC_CMD_LED_CONTROL 0x20
|
||||
#define WAC_CMD_ICON_START 0x21
|
||||
#define WAC_CMD_ICON_XFER 0x23
|
||||
#define WAC_CMD_ICON_BT_XFER 0x26
|
||||
#define WAC_CMD_RETRIES 10
|
||||
|
||||
static int wacom_get_report(struct hid_device *hdev, u8 type, u8 id,
|
||||
void *buf, size_t size, unsigned int retries)
|
||||
#define DEV_ATTR_RW_PERM (S_IRUGO | S_IWUSR | S_IWGRP)
|
||||
#define DEV_ATTR_WO_PERM (S_IWUSR | S_IWGRP)
|
||||
|
||||
static int wacom_get_report(struct hid_device *hdev, u8 type, u8 *buf,
|
||||
size_t size, unsigned int retries)
|
||||
{
|
||||
int retval;
|
||||
|
||||
do {
|
||||
retval = hid_hw_raw_request(hdev, id, buf, size, type,
|
||||
retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
|
||||
HID_REQ_GET_REPORT);
|
||||
} while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
|
||||
|
||||
|
@ -106,12 +109,35 @@ static void wacom_feature_mapping(struct hid_device *hdev,
|
|||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
struct wacom_features *features = &wacom->wacom_wac.features;
|
||||
struct hid_data *hid_data = &wacom->wacom_wac.hid_data;
|
||||
u8 *data;
|
||||
int ret;
|
||||
|
||||
switch (usage->hid) {
|
||||
case HID_DG_CONTACTMAX:
|
||||
/* leave touch_max as is if predefined */
|
||||
if (!features->touch_max)
|
||||
features->touch_max = field->value[0];
|
||||
if (!features->touch_max) {
|
||||
/* read manually */
|
||||
data = kzalloc(2, GFP_KERNEL);
|
||||
if (!data)
|
||||
break;
|
||||
data[0] = field->report->id;
|
||||
ret = wacom_get_report(hdev, HID_FEATURE_REPORT,
|
||||
data, 2, 0);
|
||||
if (ret == 2)
|
||||
features->touch_max = data[1];
|
||||
kfree(data);
|
||||
}
|
||||
break;
|
||||
case HID_DG_INPUTMODE:
|
||||
/* Ignore if value index is out of bounds. */
|
||||
if (usage->usage_index >= field->report_count) {
|
||||
dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n");
|
||||
break;
|
||||
}
|
||||
|
||||
hid_data->inputmode = field->report->id;
|
||||
hid_data->inputmode_index = usage->usage_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -199,6 +225,9 @@ static void wacom_usage_mapping(struct hid_device *hdev,
|
|||
features->pressure_max = field->logical_maximum;
|
||||
break;
|
||||
}
|
||||
|
||||
if (features->type == HID_GENERIC)
|
||||
wacom_wac_usage_mapping(hdev, field, usage);
|
||||
}
|
||||
|
||||
static void wacom_parse_hid(struct hid_device *hdev,
|
||||
|
@ -237,6 +266,25 @@ static void wacom_parse_hid(struct hid_device *hdev,
|
|||
}
|
||||
}
|
||||
|
||||
static int wacom_hid_set_device_mode(struct hid_device *hdev)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
struct hid_data *hid_data = &wacom->wacom_wac.hid_data;
|
||||
struct hid_report *r;
|
||||
struct hid_report_enum *re;
|
||||
|
||||
if (hid_data->inputmode < 0)
|
||||
return 0;
|
||||
|
||||
re = &(hdev->report_enum[HID_FEATURE_REPORT]);
|
||||
r = re->report_id_hash[hid_data->inputmode];
|
||||
if (r) {
|
||||
r->field[0]->value[hid_data->inputmode_index] = 2;
|
||||
hid_hw_request(hdev, r, HID_REQ_SET_REPORT);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
|
||||
int length, int mode)
|
||||
{
|
||||
|
@ -255,7 +303,7 @@ static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
|
|||
length, 1);
|
||||
if (error >= 0)
|
||||
error = wacom_get_report(hdev, HID_FEATURE_REPORT,
|
||||
report_id, rep_data, length, 1);
|
||||
rep_data, length, 1);
|
||||
} while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES);
|
||||
|
||||
kfree(rep_data);
|
||||
|
@ -329,6 +377,9 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
|
|||
if (hdev->bus == BUS_BLUETOOTH)
|
||||
return wacom_bt_query_tablet_data(hdev, 1, features);
|
||||
|
||||
if (features->type == HID_GENERIC)
|
||||
return wacom_hid_set_device_mode(hdev);
|
||||
|
||||
if (features->device_type == BTN_TOOL_FINGER) {
|
||||
if (features->type > TABLETPC) {
|
||||
/* MT Tablet PC touch */
|
||||
|
@ -487,8 +538,14 @@ static int wacom_led_control(struct wacom *wacom)
|
|||
{
|
||||
unsigned char *buf;
|
||||
int retval;
|
||||
unsigned char report_id = WAC_CMD_LED_CONTROL;
|
||||
int buf_size = 9;
|
||||
|
||||
buf = kzalloc(9, GFP_KERNEL);
|
||||
if (wacom->wacom_wac.pid) { /* wireless connected */
|
||||
report_id = WAC_CMD_WL_LED_CONTROL;
|
||||
buf_size = 13;
|
||||
}
|
||||
buf = kzalloc(buf_size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -502,9 +559,16 @@ static int wacom_led_control(struct wacom *wacom)
|
|||
int ring_led = wacom->led.select[0] & 0x03;
|
||||
int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03;
|
||||
int crop_lum = 0;
|
||||
unsigned char led_bits = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
|
||||
|
||||
buf[0] = WAC_CMD_LED_CONTROL;
|
||||
buf[1] = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
|
||||
buf[0] = report_id;
|
||||
if (wacom->wacom_wac.pid) {
|
||||
wacom_get_report(wacom->hdev, HID_FEATURE_REPORT,
|
||||
buf, buf_size, WAC_CMD_RETRIES);
|
||||
buf[0] = report_id;
|
||||
buf[4] = led_bits;
|
||||
} else
|
||||
buf[1] = led_bits;
|
||||
}
|
||||
else {
|
||||
int led = wacom->led.select[0] | 0x4;
|
||||
|
@ -513,14 +577,14 @@ static int wacom_led_control(struct wacom *wacom)
|
|||
wacom->wacom_wac.features.type == WACOM_24HD)
|
||||
led |= (wacom->led.select[1] << 4) | 0x40;
|
||||
|
||||
buf[0] = WAC_CMD_LED_CONTROL;
|
||||
buf[0] = report_id;
|
||||
buf[1] = led;
|
||||
buf[2] = wacom->led.llv;
|
||||
buf[3] = wacom->led.hlv;
|
||||
buf[4] = wacom->led.img_lum;
|
||||
}
|
||||
|
||||
retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 9,
|
||||
retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, buf_size,
|
||||
WAC_CMD_RETRIES);
|
||||
kfree(buf);
|
||||
|
||||
|
@ -602,9 +666,10 @@ static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \
|
|||
{ \
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);\
|
||||
struct wacom *wacom = hid_get_drvdata(hdev); \
|
||||
return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]); \
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", \
|
||||
wacom->led.select[SET_ID]); \
|
||||
} \
|
||||
static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR, \
|
||||
static DEVICE_ATTR(status_led##SET_ID##_select, DEV_ATTR_RW_PERM, \
|
||||
wacom_led##SET_ID##_select_show, \
|
||||
wacom_led##SET_ID##_select_store)
|
||||
|
||||
|
@ -641,8 +706,15 @@ static ssize_t wacom_##name##_luminance_store(struct device *dev, \
|
|||
return wacom_luminance_store(wacom, &wacom->led.field, \
|
||||
buf, count); \
|
||||
} \
|
||||
static DEVICE_ATTR(name##_luminance, S_IWUSR, \
|
||||
NULL, wacom_##name##_luminance_store)
|
||||
static ssize_t wacom_##name##_luminance_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct wacom *wacom = dev_get_drvdata(dev); \
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", wacom->led.field); \
|
||||
} \
|
||||
static DEVICE_ATTR(name##_luminance, DEV_ATTR_RW_PERM, \
|
||||
wacom_##name##_luminance_show, \
|
||||
wacom_##name##_luminance_store)
|
||||
|
||||
DEVICE_LUMINANCE_ATTR(status0, llv);
|
||||
DEVICE_LUMINANCE_ATTR(status1, hlv);
|
||||
|
@ -683,7 +755,7 @@ static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev, \
|
|||
{ \
|
||||
return wacom_button_image_store(dev, BUTTON_ID, buf, count); \
|
||||
} \
|
||||
static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR, \
|
||||
static DEVICE_ATTR(button##BUTTON_ID##_rawimg, DEV_ATTR_WO_PERM, \
|
||||
NULL, wacom_btnimg##BUTTON_ID##_store)
|
||||
|
||||
DEVICE_BTNIMG_ATTR(0);
|
||||
|
@ -989,7 +1061,7 @@ static ssize_t wacom_store_speed(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
static DEVICE_ATTR(speed, DEV_ATTR_RW_PERM,
|
||||
wacom_show_speed, wacom_store_speed);
|
||||
|
||||
static struct input_dev *wacom_allocate_input(struct wacom *wacom)
|
||||
|
@ -1010,21 +1082,61 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom)
|
|||
input_dev->uniq = hdev->uniq;
|
||||
input_dev->id.bustype = hdev->bus;
|
||||
input_dev->id.vendor = hdev->vendor;
|
||||
input_dev->id.product = hdev->product;
|
||||
input_dev->id.product = wacom_wac->pid ? wacom_wac->pid : hdev->product;
|
||||
input_dev->id.version = hdev->version;
|
||||
input_set_drvdata(input_dev, wacom);
|
||||
|
||||
return input_dev;
|
||||
}
|
||||
|
||||
static void wacom_unregister_inputs(struct wacom *wacom)
|
||||
static void wacom_free_inputs(struct wacom *wacom)
|
||||
{
|
||||
if (wacom->wacom_wac.input)
|
||||
input_unregister_device(wacom->wacom_wac.input);
|
||||
if (wacom->wacom_wac.pad_input)
|
||||
input_unregister_device(wacom->wacom_wac.pad_input);
|
||||
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
|
||||
|
||||
if (wacom_wac->input)
|
||||
input_free_device(wacom_wac->input);
|
||||
if (wacom_wac->pad_input)
|
||||
input_free_device(wacom_wac->pad_input);
|
||||
wacom_wac->input = NULL;
|
||||
wacom_wac->pad_input = NULL;
|
||||
}
|
||||
|
||||
static int wacom_allocate_inputs(struct wacom *wacom)
|
||||
{
|
||||
struct input_dev *input_dev, *pad_input_dev;
|
||||
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
|
||||
|
||||
input_dev = wacom_allocate_input(wacom);
|
||||
pad_input_dev = wacom_allocate_input(wacom);
|
||||
if (!input_dev || !pad_input_dev) {
|
||||
wacom_free_inputs(wacom);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wacom_wac->input = input_dev;
|
||||
wacom_wac->pad_input = pad_input_dev;
|
||||
wacom_wac->pad_input->name = wacom_wac->pad_name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_clean_inputs(struct wacom *wacom)
|
||||
{
|
||||
if (wacom->wacom_wac.input) {
|
||||
if (wacom->wacom_wac.input_registered)
|
||||
input_unregister_device(wacom->wacom_wac.input);
|
||||
else
|
||||
input_free_device(wacom->wacom_wac.input);
|
||||
}
|
||||
if (wacom->wacom_wac.pad_input) {
|
||||
if (wacom->wacom_wac.input_registered)
|
||||
input_unregister_device(wacom->wacom_wac.pad_input);
|
||||
else
|
||||
input_free_device(wacom->wacom_wac.pad_input);
|
||||
}
|
||||
wacom->wacom_wac.input = NULL;
|
||||
wacom->wacom_wac.pad_input = NULL;
|
||||
wacom_destroy_leds(wacom);
|
||||
}
|
||||
|
||||
static int wacom_register_inputs(struct wacom *wacom)
|
||||
|
@ -1033,24 +1145,19 @@ static int wacom_register_inputs(struct wacom *wacom)
|
|||
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
|
||||
int error;
|
||||
|
||||
input_dev = wacom_allocate_input(wacom);
|
||||
pad_input_dev = wacom_allocate_input(wacom);
|
||||
if (!input_dev || !pad_input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
input_dev = wacom_wac->input;
|
||||
pad_input_dev = wacom_wac->pad_input;
|
||||
|
||||
wacom_wac->input = input_dev;
|
||||
wacom_wac->pad_input = pad_input_dev;
|
||||
wacom_wac->pad_input->name = wacom_wac->pad_name;
|
||||
if (!input_dev || !pad_input_dev)
|
||||
return -EINVAL;
|
||||
|
||||
error = wacom_setup_input_capabilities(input_dev, wacom_wac);
|
||||
if (error)
|
||||
goto fail2;
|
||||
return error;
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error)
|
||||
goto fail2;
|
||||
return error;
|
||||
|
||||
error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac);
|
||||
if (error) {
|
||||
|
@ -1061,22 +1168,23 @@ static int wacom_register_inputs(struct wacom *wacom)
|
|||
} else {
|
||||
error = input_register_device(pad_input_dev);
|
||||
if (error)
|
||||
goto fail3;
|
||||
goto fail_register_pad_input;
|
||||
|
||||
error = wacom_initialize_leds(wacom);
|
||||
if (error)
|
||||
goto fail_leds;
|
||||
}
|
||||
|
||||
wacom_wac->input_registered = true;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
fail_leds:
|
||||
input_unregister_device(pad_input_dev);
|
||||
pad_input_dev = NULL;
|
||||
fail_register_pad_input:
|
||||
input_unregister_device(input_dev);
|
||||
input_dev = NULL;
|
||||
fail2:
|
||||
wacom_wac->input = NULL;
|
||||
wacom_wac->pad_input = NULL;
|
||||
fail1:
|
||||
if (input_dev)
|
||||
input_free_device(input_dev);
|
||||
if (pad_input_dev)
|
||||
input_free_device(pad_input_dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -1101,13 +1209,13 @@ static void wacom_wireless_work(struct work_struct *work)
|
|||
hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
|
||||
wacom1 = hid_get_drvdata(hdev1);
|
||||
wacom_wac1 = &(wacom1->wacom_wac);
|
||||
wacom_unregister_inputs(wacom1);
|
||||
wacom_clean_inputs(wacom1);
|
||||
|
||||
/* Touch interface */
|
||||
hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
|
||||
wacom2 = hid_get_drvdata(hdev2);
|
||||
wacom_wac2 = &(wacom2->wacom_wac);
|
||||
wacom_unregister_inputs(wacom2);
|
||||
wacom_clean_inputs(wacom2);
|
||||
|
||||
if (wacom_wac->pid == 0) {
|
||||
hid_info(wacom->hdev, "wireless tablet disconnected\n");
|
||||
|
@ -1140,7 +1248,9 @@ static void wacom_wireless_work(struct work_struct *work)
|
|||
wacom_wac1->features.name);
|
||||
wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max;
|
||||
wacom_wac1->shared->type = wacom_wac1->features.type;
|
||||
error = wacom_register_inputs(wacom1);
|
||||
wacom_wac1->pid = wacom_wac->pid;
|
||||
error = wacom_allocate_inputs(wacom1) ||
|
||||
wacom_register_inputs(wacom1);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
|
@ -1160,7 +1270,9 @@ static void wacom_wireless_work(struct work_struct *work)
|
|||
"%s (WL) Pad",wacom_wac2->features.name);
|
||||
snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
|
||||
"%s (WL) Pad", wacom_wac2->features.name);
|
||||
error = wacom_register_inputs(wacom2);
|
||||
wacom_wac2->pid = wacom_wac->pid;
|
||||
error = wacom_allocate_inputs(wacom2) ||
|
||||
wacom_register_inputs(wacom2);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
|
@ -1177,8 +1289,8 @@ static void wacom_wireless_work(struct work_struct *work)
|
|||
return;
|
||||
|
||||
fail:
|
||||
wacom_unregister_inputs(wacom1);
|
||||
wacom_unregister_inputs(wacom2);
|
||||
wacom_clean_inputs(wacom1);
|
||||
wacom_clean_inputs(wacom2);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1241,10 +1353,13 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
struct wacom_wac *wacom_wac;
|
||||
struct wacom_features *features;
|
||||
int error;
|
||||
unsigned int connect_mask = HID_CONNECT_HIDRAW;
|
||||
|
||||
if (!id->driver_data)
|
||||
return -EINVAL;
|
||||
|
||||
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
|
||||
|
||||
wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
|
||||
if (!wacom)
|
||||
return -ENOMEM;
|
||||
|
@ -1256,7 +1371,7 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
error = hid_parse(hdev);
|
||||
if (error) {
|
||||
hid_err(hdev, "parse failed\n");
|
||||
goto fail1;
|
||||
goto fail_parse;
|
||||
}
|
||||
|
||||
wacom_wac = &wacom->wacom_wac;
|
||||
|
@ -1265,12 +1380,12 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
features->pktlen = wacom_compute_pktlen(hdev);
|
||||
if (features->pktlen > WACOM_PKGLEN_MAX) {
|
||||
error = -EINVAL;
|
||||
goto fail1;
|
||||
goto fail_pktlen;
|
||||
}
|
||||
|
||||
if (features->check_for_hid_type && features->hid_type != hdev->type) {
|
||||
error = -ENODEV;
|
||||
goto fail1;
|
||||
goto fail_type;
|
||||
}
|
||||
|
||||
wacom->usbdev = dev;
|
||||
|
@ -1278,6 +1393,12 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
mutex_init(&wacom->lock);
|
||||
INIT_WORK(&wacom->work, wacom_wireless_work);
|
||||
|
||||
if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
|
||||
error = wacom_allocate_inputs(wacom);
|
||||
if (error)
|
||||
goto fail_allocate_inputs;
|
||||
}
|
||||
|
||||
/* set the default size in case we do not get them from hid */
|
||||
wacom_set_default_phy(features);
|
||||
|
||||
|
@ -1339,24 +1460,20 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
|
||||
error = wacom_add_shared_data(hdev);
|
||||
if (error)
|
||||
goto fail1;
|
||||
goto fail_shared_data;
|
||||
}
|
||||
|
||||
error = wacom_initialize_leds(wacom);
|
||||
if (error)
|
||||
goto fail2;
|
||||
|
||||
if (!(features->quirks & WACOM_QUIRK_MONITOR) &&
|
||||
(features->quirks & WACOM_QUIRK_BATTERY)) {
|
||||
error = wacom_initialize_battery(wacom);
|
||||
if (error)
|
||||
goto fail3;
|
||||
goto fail_battery;
|
||||
}
|
||||
|
||||
if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
|
||||
error = wacom_register_inputs(wacom);
|
||||
if (error)
|
||||
goto fail4;
|
||||
goto fail_register_inputs;
|
||||
}
|
||||
|
||||
if (hdev->bus == BUS_BLUETOOTH) {
|
||||
|
@ -1367,16 +1484,19 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
error);
|
||||
}
|
||||
|
||||
/* Note that if query fails it is not a hard failure */
|
||||
wacom_query_tablet_data(hdev, features);
|
||||
if (features->type == HID_GENERIC)
|
||||
connect_mask |= HID_CONNECT_DRIVER;
|
||||
|
||||
/* Regular HID work starts now */
|
||||
error = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
|
||||
error = hid_hw_start(hdev, connect_mask);
|
||||
if (error) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
goto fail5;
|
||||
goto fail_hw_start;
|
||||
}
|
||||
|
||||
/* Note that if query fails it is not a hard failure */
|
||||
wacom_query_tablet_data(hdev, features);
|
||||
|
||||
if (features->quirks & WACOM_QUIRK_MONITOR)
|
||||
error = hid_hw_open(hdev);
|
||||
|
||||
|
@ -1387,13 +1507,21 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
|
||||
return 0;
|
||||
|
||||
fail5: if (hdev->bus == BUS_BLUETOOTH)
|
||||
fail_hw_start:
|
||||
if (hdev->bus == BUS_BLUETOOTH)
|
||||
device_remove_file(&hdev->dev, &dev_attr_speed);
|
||||
wacom_unregister_inputs(wacom);
|
||||
fail4: wacom_destroy_battery(wacom);
|
||||
fail3: wacom_destroy_leds(wacom);
|
||||
fail2: wacom_remove_shared_data(wacom_wac);
|
||||
fail1: kfree(wacom);
|
||||
fail_register_inputs:
|
||||
wacom_clean_inputs(wacom);
|
||||
wacom_destroy_battery(wacom);
|
||||
fail_battery:
|
||||
wacom_remove_shared_data(wacom_wac);
|
||||
fail_shared_data:
|
||||
wacom_clean_inputs(wacom);
|
||||
fail_allocate_inputs:
|
||||
fail_type:
|
||||
fail_pktlen:
|
||||
fail_parse:
|
||||
kfree(wacom);
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
return error;
|
||||
}
|
||||
|
@ -1405,11 +1533,10 @@ static void wacom_remove(struct hid_device *hdev)
|
|||
hid_hw_stop(hdev);
|
||||
|
||||
cancel_work_sync(&wacom->work);
|
||||
wacom_unregister_inputs(wacom);
|
||||
wacom_clean_inputs(wacom);
|
||||
if (hdev->bus == BUS_BLUETOOTH)
|
||||
device_remove_file(&hdev->dev, &dev_attr_speed);
|
||||
wacom_destroy_battery(wacom);
|
||||
wacom_destroy_leds(wacom);
|
||||
wacom_remove_shared_data(&wacom->wacom_wac);
|
||||
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
|
@ -1444,6 +1571,8 @@ static struct hid_driver wacom_driver = {
|
|||
.id_table = wacom_ids,
|
||||
.probe = wacom_probe,
|
||||
.remove = wacom_remove,
|
||||
.event = wacom_wac_event,
|
||||
.report = wacom_wac_report,
|
||||
#ifdef CONFIG_PM
|
||||
.resume = wacom_resume,
|
||||
.reset_resume = wacom_reset_resume,
|
||||
|
|
|
@ -1248,6 +1248,296 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_map_usage(struct wacom *wacom, struct hid_usage *usage,
|
||||
struct hid_field *field, __u8 type, __u16 code, int fuzz)
|
||||
{
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct input_dev *input = wacom_wac->input;
|
||||
int fmin = field->logical_minimum;
|
||||
int fmax = field->logical_maximum;
|
||||
|
||||
usage->type = type;
|
||||
usage->code = code;
|
||||
|
||||
set_bit(type, input->evbit);
|
||||
|
||||
switch (type) {
|
||||
case EV_ABS:
|
||||
input_set_abs_params(input, code, fmin, fmax, fuzz, 0);
|
||||
input_abs_set_res(input, code,
|
||||
hidinput_calc_abs_res(field, code));
|
||||
break;
|
||||
case EV_KEY:
|
||||
input_set_capability(input, EV_KEY, code);
|
||||
break;
|
||||
case EV_MSC:
|
||||
input_set_capability(input, EV_MSC, code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
|
||||
struct hid_field *field, struct hid_usage *usage)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
|
||||
switch (usage->hid) {
|
||||
case HID_GD_X:
|
||||
wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4);
|
||||
break;
|
||||
case HID_GD_Y:
|
||||
wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4);
|
||||
break;
|
||||
case HID_DG_TIPPRESSURE:
|
||||
wacom_map_usage(wacom, usage, field, EV_ABS, ABS_PRESSURE, 0);
|
||||
break;
|
||||
case HID_DG_INRANGE:
|
||||
wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOOL_PEN, 0);
|
||||
break;
|
||||
case HID_DG_INVERT:
|
||||
wacom_map_usage(wacom, usage, field, EV_KEY,
|
||||
BTN_TOOL_RUBBER, 0);
|
||||
break;
|
||||
case HID_DG_ERASER:
|
||||
case HID_DG_TIPSWITCH:
|
||||
wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0);
|
||||
break;
|
||||
case HID_DG_BARRELSWITCH:
|
||||
wacom_map_usage(wacom, usage, field, EV_KEY, BTN_STYLUS, 0);
|
||||
break;
|
||||
case HID_DG_BARRELSWITCH2:
|
||||
wacom_map_usage(wacom, usage, field, EV_KEY, BTN_STYLUS2, 0);
|
||||
break;
|
||||
case HID_DG_TOOLSERIALNUMBER:
|
||||
wacom_map_usage(wacom, usage, field, EV_MSC, MSC_SERIAL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct input_dev *input = wacom_wac->input;
|
||||
|
||||
/* checking which Tool / tip switch to send */
|
||||
switch (usage->hid) {
|
||||
case HID_DG_INRANGE:
|
||||
wacom_wac->hid_data.inrange_state = value;
|
||||
return 0;
|
||||
case HID_DG_INVERT:
|
||||
wacom_wac->hid_data.invert_state = value;
|
||||
return 0;
|
||||
case HID_DG_ERASER:
|
||||
case HID_DG_TIPSWITCH:
|
||||
wacom_wac->hid_data.tipswitch |= value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send pen events only when touch is up or forced out */
|
||||
if (!usage->type || wacom_wac->shared->touch_down)
|
||||
return 0;
|
||||
|
||||
input_event(input, usage->type, usage->code, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_wac_pen_report(struct hid_device *hdev,
|
||||
struct hid_report *report)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct input_dev *input = wacom_wac->input;
|
||||
bool prox = wacom_wac->hid_data.inrange_state;
|
||||
|
||||
if (!wacom_wac->shared->stylus_in_proximity) /* first in prox */
|
||||
/* Going into proximity select tool */
|
||||
wacom_wac->tool[0] = wacom_wac->hid_data.invert_state ?
|
||||
BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
||||
|
||||
/* keep pen state for touch events */
|
||||
wacom_wac->shared->stylus_in_proximity = prox;
|
||||
|
||||
/* send pen events only when touch is up or forced out */
|
||||
if (!wacom_wac->shared->touch_down) {
|
||||
input_report_key(input, BTN_TOUCH,
|
||||
wacom_wac->hid_data.tipswitch);
|
||||
input_report_key(input, wacom_wac->tool[0], prox);
|
||||
|
||||
wacom_wac->hid_data.tipswitch = false;
|
||||
|
||||
input_sync(input);
|
||||
}
|
||||
}
|
||||
|
||||
static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
|
||||
struct hid_field *field, struct hid_usage *usage)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct input_dev *input = wacom_wac->input;
|
||||
unsigned touch_max = wacom_wac->features.touch_max;
|
||||
|
||||
switch (usage->hid) {
|
||||
case HID_GD_X:
|
||||
if (touch_max == 1)
|
||||
wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4);
|
||||
else
|
||||
wacom_map_usage(wacom, usage, field, EV_ABS,
|
||||
ABS_MT_POSITION_X, 4);
|
||||
break;
|
||||
case HID_GD_Y:
|
||||
if (touch_max == 1)
|
||||
wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4);
|
||||
else
|
||||
wacom_map_usage(wacom, usage, field, EV_ABS,
|
||||
ABS_MT_POSITION_Y, 4);
|
||||
break;
|
||||
case HID_DG_CONTACTID:
|
||||
input_mt_init_slots(input, wacom_wac->features.touch_max,
|
||||
INPUT_MT_DIRECT);
|
||||
break;
|
||||
case HID_DG_INRANGE:
|
||||
break;
|
||||
case HID_DG_INVERT:
|
||||
break;
|
||||
case HID_DG_TIPSWITCH:
|
||||
wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int wacom_wac_finger_event(struct hid_device *hdev,
|
||||
struct hid_field *field, struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
|
||||
switch (usage->hid) {
|
||||
case HID_GD_X:
|
||||
wacom_wac->hid_data.x = value;
|
||||
break;
|
||||
case HID_GD_Y:
|
||||
wacom_wac->hid_data.y = value;
|
||||
break;
|
||||
case HID_DG_CONTACTID:
|
||||
wacom_wac->hid_data.id = value;
|
||||
break;
|
||||
case HID_DG_TIPSWITCH:
|
||||
wacom_wac->hid_data.tipswitch = value;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_wac_finger_mt_report(struct wacom_wac *wacom_wac,
|
||||
struct input_dev *input, bool touch)
|
||||
{
|
||||
int slot;
|
||||
struct hid_data *hid_data = &wacom_wac->hid_data;
|
||||
|
||||
slot = input_mt_get_slot_by_key(input, hid_data->id);
|
||||
|
||||
input_mt_slot(input, slot);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
|
||||
if (touch) {
|
||||
input_report_abs(input, ABS_MT_POSITION_X, hid_data->x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, hid_data->y);
|
||||
}
|
||||
input_mt_sync_frame(input);
|
||||
}
|
||||
|
||||
static void wacom_wac_finger_single_touch_report(struct wacom_wac *wacom_wac,
|
||||
struct input_dev *input, bool touch)
|
||||
{
|
||||
struct hid_data *hid_data = &wacom_wac->hid_data;
|
||||
|
||||
if (touch) {
|
||||
input_report_abs(input, ABS_X, hid_data->x);
|
||||
input_report_abs(input, ABS_Y, hid_data->y);
|
||||
}
|
||||
input_report_key(input, BTN_TOUCH, touch);
|
||||
}
|
||||
|
||||
static void wacom_wac_finger_report(struct hid_device *hdev,
|
||||
struct hid_report *report)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct input_dev *input = wacom_wac->input;
|
||||
bool touch = wacom_wac->hid_data.tipswitch &&
|
||||
!wacom_wac->shared->stylus_in_proximity;
|
||||
unsigned touch_max = wacom_wac->features.touch_max;
|
||||
|
||||
if (touch_max > 1)
|
||||
wacom_wac_finger_mt_report(wacom_wac, input, touch);
|
||||
else
|
||||
wacom_wac_finger_single_touch_report(wacom_wac, input, touch);
|
||||
input_sync(input);
|
||||
|
||||
/* keep touch state for pen event */
|
||||
wacom_wac->shared->touch_down = touch;
|
||||
}
|
||||
|
||||
#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \
|
||||
((f)->physical == HID_DG_STYLUS))
|
||||
#define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \
|
||||
((f)->physical == HID_DG_FINGER))
|
||||
|
||||
void wacom_wac_usage_mapping(struct hid_device *hdev,
|
||||
struct hid_field *field, struct hid_usage *usage)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct input_dev *input = wacom_wac->input;
|
||||
|
||||
/* currently, only direct devices have proper hid report descriptors */
|
||||
__set_bit(INPUT_PROP_DIRECT, input->propbit);
|
||||
|
||||
if (WACOM_PEN_FIELD(field))
|
||||
return wacom_wac_pen_usage_mapping(hdev, field, usage);
|
||||
|
||||
if (WACOM_FINGER_FIELD(field))
|
||||
return wacom_wac_finger_usage_mapping(hdev, field, usage);
|
||||
}
|
||||
|
||||
int wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
|
||||
if (wacom->wacom_wac.features.type != HID_GENERIC)
|
||||
return 0;
|
||||
|
||||
if (WACOM_PEN_FIELD(field))
|
||||
return wacom_wac_pen_event(hdev, field, usage, value);
|
||||
|
||||
if (WACOM_FINGER_FIELD(field))
|
||||
return wacom_wac_finger_event(hdev, field, usage, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
|
||||
{
|
||||
struct wacom *wacom = hid_get_drvdata(hdev);
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct hid_field *field = report->field[0];
|
||||
|
||||
if (wacom_wac->features.type != HID_GENERIC)
|
||||
return;
|
||||
|
||||
if (WACOM_PEN_FIELD(field))
|
||||
return wacom_wac_pen_report(hdev, report);
|
||||
|
||||
if (WACOM_FINGER_FIELD(field))
|
||||
return wacom_wac_finger_report(hdev, report);
|
||||
}
|
||||
|
||||
static int wacom_bpt_touch(struct wacom_wac *wacom)
|
||||
{
|
||||
struct wacom_features *features = &wacom->features;
|
||||
|
@ -1746,6 +2036,10 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
|
||||
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
|
||||
if (features->type == HID_GENERIC)
|
||||
/* setup has already been done */
|
||||
return 0;
|
||||
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
__set_bit(ABS_MISC, input_dev->absbit);
|
||||
|
||||
|
@ -1990,6 +2284,9 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
|
|||
input_set_abs_params(input_dev, ABS_X, 0, 1, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, 1, 0, 0);
|
||||
|
||||
/* kept for making udev and libwacom accepting the pad */
|
||||
__set_bit(BTN_STYLUS, input_dev->keybit);
|
||||
|
||||
switch (features->type) {
|
||||
case GRAPHIRE_BT:
|
||||
__set_bit(BTN_0, input_dev->keybit);
|
||||
|
@ -2573,6 +2870,17 @@ static const struct wacom_features wacom_features_0x309 =
|
|||
{ "Wacom ISDv5 309", .type = WACOM_24HDT, /* Touch */
|
||||
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10,
|
||||
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
|
||||
static const struct wacom_features wacom_features_0x30A =
|
||||
{ "Wacom ISDv5 30A", 59352, 33648, 2047, 63,
|
||||
CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
|
||||
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30C };
|
||||
static const struct wacom_features wacom_features_0x30C =
|
||||
{ "Wacom ISDv5 30C", .type = WACOM_24HDT, /* Touch */
|
||||
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30A, .touch_max = 10,
|
||||
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
|
||||
|
||||
static const struct wacom_features wacom_features_HID_ANY_ID =
|
||||
{ "Wacom HID", .type = HID_GENERIC };
|
||||
|
||||
#define USB_DEVICE_WACOM(prod) \
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
|
||||
|
@ -2708,6 +3016,8 @@ const struct hid_device_id wacom_ids[] = {
|
|||
{ USB_DEVICE_WACOM(0x304) },
|
||||
{ USB_DEVICE_WACOM(0x307) },
|
||||
{ USB_DEVICE_WACOM(0x309) },
|
||||
{ USB_DEVICE_WACOM(0x30A) },
|
||||
{ USB_DEVICE_WACOM(0x30C) },
|
||||
{ USB_DEVICE_WACOM(0x30E) },
|
||||
{ USB_DEVICE_WACOM(0x314) },
|
||||
{ USB_DEVICE_WACOM(0x315) },
|
||||
|
@ -2716,6 +3026,8 @@ const struct hid_device_id wacom_ids[] = {
|
|||
{ USB_DEVICE_WACOM(0x4004) },
|
||||
{ USB_DEVICE_WACOM(0x5000) },
|
||||
{ USB_DEVICE_WACOM(0x5002) },
|
||||
|
||||
{ USB_DEVICE_WACOM(HID_ANY_ID) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, wacom_ids);
|
||||
|
|
|
@ -113,6 +113,7 @@ enum {
|
|||
MTSCREEN,
|
||||
MTTPC,
|
||||
MTTPC_B,
|
||||
HID_GENERIC,
|
||||
MAX_TYPE
|
||||
};
|
||||
|
||||
|
@ -154,6 +155,20 @@ struct wacom_shared {
|
|||
struct input_dev *touch_input;
|
||||
};
|
||||
|
||||
struct hid_data {
|
||||
__s16 inputmode; /* InputMode HID feature, -1 if non-existent */
|
||||
__s16 inputmode_index; /* InputMode HID feature index in the report */
|
||||
bool inrange_state;
|
||||
bool invert_state;
|
||||
bool tipswitch;
|
||||
int x;
|
||||
int y;
|
||||
int pressure;
|
||||
int width;
|
||||
int height;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct wacom_wac {
|
||||
char name[WACOM_NAME_MAX];
|
||||
char pad_name[WACOM_NAME_MAX];
|
||||
|
@ -167,6 +182,7 @@ struct wacom_wac {
|
|||
struct wacom_shared *shared;
|
||||
struct input_dev *input;
|
||||
struct input_dev *pad_input;
|
||||
bool input_registered;
|
||||
int pid;
|
||||
int battery_capacity;
|
||||
int num_contacts_left;
|
||||
|
@ -174,6 +190,7 @@ struct wacom_wac {
|
|||
int ps_connected;
|
||||
u8 bt_features;
|
||||
u8 bt_high_speed;
|
||||
struct hid_data hid_data;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -265,6 +265,7 @@ struct hid_item {
|
|||
#define HID_CONNECT_HIDDEV 0x08
|
||||
#define HID_CONNECT_HIDDEV_FORCE 0x10
|
||||
#define HID_CONNECT_FF 0x20
|
||||
#define HID_CONNECT_DRIVER 0x40
|
||||
#define HID_CONNECT_DEFAULT (HID_CONNECT_HIDINPUT|HID_CONNECT_HIDRAW| \
|
||||
HID_CONNECT_HIDDEV|HID_CONNECT_FF)
|
||||
|
||||
|
@ -287,6 +288,7 @@ struct hid_item {
|
|||
#define HID_QUIRK_HIDINPUT_FORCE 0x00000080
|
||||
#define HID_QUIRK_NO_EMPTY_INPUT 0x00000100
|
||||
#define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200
|
||||
#define HID_QUIRK_ALWAYS_POLL 0x00000400
|
||||
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
|
||||
#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID 0x00020000
|
||||
#define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP 0x00040000
|
||||
|
@ -440,6 +442,7 @@ struct hid_output_fifo {
|
|||
#define HID_CLAIMED_INPUT 1
|
||||
#define HID_CLAIMED_HIDDEV 2
|
||||
#define HID_CLAIMED_HIDRAW 4
|
||||
#define HID_CLAIMED_DRIVER 8
|
||||
|
||||
#define HID_STAT_ADDED 1
|
||||
#define HID_STAT_PARSED 2
|
||||
|
|
|
@ -24,35 +24,23 @@
|
|||
#include <linux/hid.h>
|
||||
|
||||
enum uhid_event_type {
|
||||
UHID_CREATE,
|
||||
__UHID_LEGACY_CREATE,
|
||||
UHID_DESTROY,
|
||||
UHID_START,
|
||||
UHID_STOP,
|
||||
UHID_OPEN,
|
||||
UHID_CLOSE,
|
||||
UHID_OUTPUT,
|
||||
UHID_OUTPUT_EV, /* obsolete! */
|
||||
UHID_INPUT,
|
||||
UHID_FEATURE,
|
||||
UHID_FEATURE_ANSWER,
|
||||
__UHID_LEGACY_OUTPUT_EV,
|
||||
__UHID_LEGACY_INPUT,
|
||||
UHID_GET_REPORT,
|
||||
UHID_GET_REPORT_REPLY,
|
||||
UHID_CREATE2,
|
||||
UHID_INPUT2,
|
||||
UHID_SET_REPORT,
|
||||
UHID_SET_REPORT_REPLY,
|
||||
};
|
||||
|
||||
struct uhid_create_req {
|
||||
__u8 name[128];
|
||||
__u8 phys[64];
|
||||
__u8 uniq[64];
|
||||
__u8 __user *rd_data;
|
||||
__u16 rd_size;
|
||||
|
||||
__u16 bus;
|
||||
__u32 vendor;
|
||||
__u32 product;
|
||||
__u32 version;
|
||||
__u32 country;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct uhid_create2_req {
|
||||
__u8 name[128];
|
||||
__u8 phys[64];
|
||||
|
@ -66,6 +54,16 @@ struct uhid_create2_req {
|
|||
__u8 rd_data[HID_MAX_DESCRIPTOR_SIZE];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
enum uhid_dev_flag {
|
||||
UHID_DEV_NUMBERED_FEATURE_REPORTS = (1ULL << 0),
|
||||
UHID_DEV_NUMBERED_OUTPUT_REPORTS = (1ULL << 1),
|
||||
UHID_DEV_NUMBERED_INPUT_REPORTS = (1ULL << 2),
|
||||
};
|
||||
|
||||
struct uhid_start_req {
|
||||
__u64 dev_flags;
|
||||
};
|
||||
|
||||
#define UHID_DATA_MAX 4096
|
||||
|
||||
enum uhid_report_type {
|
||||
|
@ -74,11 +72,6 @@ enum uhid_report_type {
|
|||
UHID_INPUT_REPORT,
|
||||
};
|
||||
|
||||
struct uhid_input_req {
|
||||
__u8 data[UHID_DATA_MAX];
|
||||
__u16 size;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct uhid_input2_req {
|
||||
__u16 size;
|
||||
__u8 data[UHID_DATA_MAX];
|
||||
|
@ -90,20 +83,83 @@ struct uhid_output_req {
|
|||
__u8 rtype;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* Obsolete! Newer kernels will no longer send these events but instead convert
|
||||
* it into raw output reports via UHID_OUTPUT. */
|
||||
struct uhid_get_report_req {
|
||||
__u32 id;
|
||||
__u8 rnum;
|
||||
__u8 rtype;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct uhid_get_report_reply_req {
|
||||
__u32 id;
|
||||
__u16 err;
|
||||
__u16 size;
|
||||
__u8 data[UHID_DATA_MAX];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct uhid_set_report_req {
|
||||
__u32 id;
|
||||
__u8 rnum;
|
||||
__u8 rtype;
|
||||
__u16 size;
|
||||
__u8 data[UHID_DATA_MAX];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct uhid_set_report_reply_req {
|
||||
__u32 id;
|
||||
__u16 err;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/*
|
||||
* Compat Layer
|
||||
* All these commands and requests are obsolete. You should avoid using them in
|
||||
* new code. We support them for backwards-compatibility, but you might not get
|
||||
* access to new feature in case you use them.
|
||||
*/
|
||||
|
||||
enum uhid_legacy_event_type {
|
||||
UHID_CREATE = __UHID_LEGACY_CREATE,
|
||||
UHID_OUTPUT_EV = __UHID_LEGACY_OUTPUT_EV,
|
||||
UHID_INPUT = __UHID_LEGACY_INPUT,
|
||||
UHID_FEATURE = UHID_GET_REPORT,
|
||||
UHID_FEATURE_ANSWER = UHID_GET_REPORT_REPLY,
|
||||
};
|
||||
|
||||
/* Obsolete! Use UHID_CREATE2. */
|
||||
struct uhid_create_req {
|
||||
__u8 name[128];
|
||||
__u8 phys[64];
|
||||
__u8 uniq[64];
|
||||
__u8 __user *rd_data;
|
||||
__u16 rd_size;
|
||||
|
||||
__u16 bus;
|
||||
__u32 vendor;
|
||||
__u32 product;
|
||||
__u32 version;
|
||||
__u32 country;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* Obsolete! Use UHID_INPUT2. */
|
||||
struct uhid_input_req {
|
||||
__u8 data[UHID_DATA_MAX];
|
||||
__u16 size;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* Obsolete! Kernel uses UHID_OUTPUT exclusively now. */
|
||||
struct uhid_output_ev_req {
|
||||
__u16 type;
|
||||
__u16 code;
|
||||
__s32 value;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* Obsolete! Kernel uses ABI compatible UHID_GET_REPORT. */
|
||||
struct uhid_feature_req {
|
||||
__u32 id;
|
||||
__u8 rnum;
|
||||
__u8 rtype;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* Obsolete! Use ABI compatible UHID_GET_REPORT_REPLY. */
|
||||
struct uhid_feature_answer_req {
|
||||
__u32 id;
|
||||
__u16 err;
|
||||
|
@ -111,6 +167,15 @@ struct uhid_feature_answer_req {
|
|||
__u8 data[UHID_DATA_MAX];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/*
|
||||
* UHID Events
|
||||
* All UHID events from and to the kernel are encoded as "struct uhid_event".
|
||||
* The "type" field contains a UHID_* type identifier. All payload depends on
|
||||
* that type and can be accessed via ev->u.XYZ accordingly.
|
||||
* If user-space writes short events, they're extended with 0s by the kernel. If
|
||||
* the kernel writes short events, user-space shall extend them with 0s.
|
||||
*/
|
||||
|
||||
struct uhid_event {
|
||||
__u32 type;
|
||||
|
||||
|
@ -120,9 +185,14 @@ struct uhid_event {
|
|||
struct uhid_output_req output;
|
||||
struct uhid_output_ev_req output_ev;
|
||||
struct uhid_feature_req feature;
|
||||
struct uhid_get_report_req get_report;
|
||||
struct uhid_feature_answer_req feature_answer;
|
||||
struct uhid_get_report_reply_req get_report_reply;
|
||||
struct uhid_create2_req create2;
|
||||
struct uhid_input2_req input2;
|
||||
struct uhid_set_report_req set_report;
|
||||
struct uhid_set_report_reply_req set_report_reply;
|
||||
struct uhid_start_req start;
|
||||
} u;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
|
|
Loading…
Reference in a new issue