mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-18 01:34:14 +08:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (47 commits) HID: fix mismerge in hid-lg HID: hidraw: fix window in hidraw_release HID: hid-sony: override usbhid_output_raw_report for Sixaxis HID: add absolute axis resolution calculation HID: force feedback support for Logitech RumblePad gamepad HID: support STmicroelectronics and Sitronix with hid-stantuml driver HID: magicmouse: Adjust major / minor axes to scale HID: Fix for problems with eGalax/DWAV multi-touch-screen HID: waltop: add support for Waltop Slim Tablet 12.1 inch HID: add NOGET quirk for AXIS 295 Video Surveillance Joystick HID: usbhid: remove unused hiddev_driver HID: magicmouse: Use hid-input parsing rather than bypassing it HID: trivial formatting fix HID: Add support for Logitech Speed Force Wireless gaming wheel HID: don't Send Feature Reports on Interrupt Endpoint HID: 3m: Adjust major / minor axes to scale HID: 3m: Correct touchscreen emulation HID: 3m: Convert to MT slots HID: 3m: Output proper orientation range HID: 3m: Adjust to sequential MT HID protocol ...
This commit is contained in:
commit
7c024e9534
98
Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra
Normal file
98
Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra
Normal file
@ -0,0 +1,98 @@
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_cpi
|
||||
Date: August 2010
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: It is possible to switch the cpi setting of the mouse with the
|
||||
press of a button.
|
||||
When read, this file returns the raw number of the actual cpi
|
||||
setting reported by the mouse. This number has to be further
|
||||
processed to receive the real dpi value.
|
||||
|
||||
VALUE DPI
|
||||
1 400
|
||||
2 800
|
||||
4 1600
|
||||
|
||||
This file is readonly.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_profile
|
||||
Date: August 2010
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When read, this file returns the number of the actual profile in
|
||||
range 0-4.
|
||||
This file is readonly.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/firmware_version
|
||||
Date: August 2010
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When read, this file returns the raw integer version number of the
|
||||
firmware reported by the mouse. Using the integer value eases
|
||||
further usage in other programs. To receive the real version
|
||||
number the decimal point has to be shifted 2 positions to the
|
||||
left. E.g. a returned value of 138 means 1.38
|
||||
This file is readonly.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile_settings
|
||||
Date: August 2010
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The mouse can store 5 profiles which can be switched by the
|
||||
press of a button. A profile is split in settings and buttons.
|
||||
profile_settings holds informations like resolution, sensitivity
|
||||
and light effects.
|
||||
When written, this file lets one write the respective profile
|
||||
settings back to the mouse. The data has to be 13 bytes long.
|
||||
The mouse will reject invalid data.
|
||||
Which profile to write is determined by the profile number
|
||||
contained in the data.
|
||||
This file is writeonly.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5]_settings
|
||||
Date: August 2010
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The mouse can store 5 profiles which can be switched by the
|
||||
press of a button. A profile is split in settings and buttons.
|
||||
profile_settings holds informations like resolution, sensitivity
|
||||
and light effects.
|
||||
When read, these files return the respective profile settings.
|
||||
The returned data is 13 bytes in size.
|
||||
This file is readonly.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile_buttons
|
||||
Date: August 2010
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The mouse can store 5 profiles which can be switched by the
|
||||
press of a button. A profile is split in settings and buttons.
|
||||
profile_buttons holds informations about button layout.
|
||||
When written, this file lets one write the respective profile
|
||||
buttons back to the mouse. The data has to be 19 bytes long.
|
||||
The mouse will reject invalid data.
|
||||
Which profile to write is determined by the profile number
|
||||
contained in the data.
|
||||
This file is writeonly.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5]_buttons
|
||||
Date: August 2010
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The mouse can store 5 profiles which can be switched by the
|
||||
press of a button. A profile is split in settings and buttons.
|
||||
profile_buttons holds informations about button layout.
|
||||
When read, these files return the respective profile buttons.
|
||||
The returned data is 19 bytes in size.
|
||||
This file is readonly.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/startup_profile
|
||||
Date: August 2010
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: The integer value of this attribute ranges from 0-4.
|
||||
When read, this attribute returns the number of the profile
|
||||
that's active when the mouse is powered on.
|
||||
This file is readonly.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/settings
|
||||
Date: August 2010
|
||||
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
Description: When read, this file returns the settings stored in the mouse.
|
||||
The size of the data is 3 bytes and holds information on the
|
||||
startup_profile.
|
||||
When written, this file lets write settings back to the mouse.
|
||||
The data has to be 3 bytes long. The mouse will reject invalid
|
||||
data.
|
126
Documentation/input/ntrig.txt
Normal file
126
Documentation/input/ntrig.txt
Normal file
@ -0,0 +1,126 @@
|
||||
N-Trig touchscreen Driver
|
||||
-------------------------
|
||||
Copyright (c) 2008-2010 Rafi Rubin <rafi@seas.upenn.edu>
|
||||
Copyright (c) 2009-2010 Stephane Chatty
|
||||
|
||||
This driver provides support for N-Trig pen and multi-touch sensors. Single
|
||||
and multi-touch events are translated to the appropriate protocols for
|
||||
the hid and input systems. Pen events are sufficiently hid compliant and
|
||||
are left to the hid core. The driver also provides additional filtering
|
||||
and utility functions accessible with sysfs and module parameters.
|
||||
|
||||
This driver has been reported to work properly with multiple N-Trig devices
|
||||
attached.
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
Note: values set at load time are global and will apply to all applicable
|
||||
devices. Adjusting parameters with sysfs will override the load time values,
|
||||
but only for that one device.
|
||||
|
||||
The following parameters are used to configure filters to reduce noise:
|
||||
|
||||
activate_slack number of fingers to ignore before processing events
|
||||
|
||||
activation_height size threshold to activate immediately
|
||||
activation_width
|
||||
|
||||
min_height size threshold bellow which fingers are ignored
|
||||
min_width both to decide activation and during activity
|
||||
|
||||
deactivate_slack the number of "no contact" frames to ignore before
|
||||
propagating the end of activity events
|
||||
|
||||
When the last finger is removed from the device, it sends a number of empty
|
||||
frames. By holding off on deactivation for a few frames we can tolerate false
|
||||
erroneous disconnects, where the sensor may mistakenly not detect a finger that
|
||||
is still present. Thus deactivate_slack addresses problems where a users might
|
||||
see breaks in lines during drawing, or drop an object during a long drag.
|
||||
|
||||
|
||||
Additional sysfs items
|
||||
----------------------
|
||||
|
||||
These nodes just provide easy access to the ranges reported by the device.
|
||||
sensor_logical_height the range for positions reported during activity
|
||||
sensor_logical_width
|
||||
|
||||
sensor_physical_height internal ranges not used for normal events but
|
||||
sensor_physical_width useful for tuning
|
||||
|
||||
All N-Trig devices with product id of 1 report events in the ranges of
|
||||
X: 0-9600
|
||||
Y: 0-7200
|
||||
However not all of these devices have the same physical dimensions. Most
|
||||
seem to be 12" sensors (Dell Latitude XT and XT2 and the HP TX2), and
|
||||
at least one model (Dell Studio 17) has a 17" sensor. The ratio of physical
|
||||
to logical sizes is used to adjust the size based filter parameters.
|
||||
|
||||
|
||||
Filtering
|
||||
---------
|
||||
|
||||
With the release of the early multi-touch firmwares it became increasingly
|
||||
obvious that these sensors were prone to erroneous events. Users reported
|
||||
seeing both inappropriately dropped contact and ghosts, contacts reported
|
||||
where no finger was actually touching the screen.
|
||||
|
||||
Deactivation slack helps prevent dropped contact for single touch use, but does
|
||||
not address the problem of dropping one of more contacts while other contacts
|
||||
are still active. Drops in the multi-touch context require additional
|
||||
processing and should be handled in tandem with tacking.
|
||||
|
||||
As observed ghost contacts are similar to actual use of the sensor, but they
|
||||
seem to have different profiles. Ghost activity typically shows up as small
|
||||
short lived touches. As such, I assume that the longer the continuous stream
|
||||
of events the more likely those events are from a real contact, and that the
|
||||
larger the size of each contact the more likely it is real. Balancing the
|
||||
goals of preventing ghosts and accepting real events quickly (to minimize
|
||||
user observable latency), the filter accumulates confidence for incoming
|
||||
events until it hits thresholds and begins propagating. In the interest in
|
||||
minimizing stored state as well as the cost of operations to make a decision,
|
||||
I've kept that decision simple.
|
||||
|
||||
Time is measured in terms of the number of fingers reported, not frames since
|
||||
the probability of multiple simultaneous ghosts is expected to drop off
|
||||
dramatically with increasing numbers. Rather than accumulate weight as a
|
||||
function of size, I just use it as a binary threshold. A sufficiently large
|
||||
contact immediately overrides the waiting period and leads to activation.
|
||||
|
||||
Setting the activation size thresholds to large values will result in deciding
|
||||
primarily on activation slack. If you see longer lived ghosts, turning up the
|
||||
activation slack while reducing the size thresholds may suffice to eliminate
|
||||
the ghosts while keeping the screen quite responsive to firm taps.
|
||||
|
||||
Contacts continue to be filtered with min_height and min_width even after
|
||||
the initial activation filter is satisfied. The intent is to provide
|
||||
a mechanism for filtering out ghosts in the form of an extra finger while
|
||||
you actually are using the screen. In practice this sort of ghost has
|
||||
been far less problematic or relatively rare and I've left the defaults
|
||||
set to 0 for both parameters, effectively turning off that filter.
|
||||
|
||||
I don't know what the optimal values are for these filters. If the defaults
|
||||
don't work for you, please play with the parameters. If you do find other
|
||||
values more comfortable, I would appreciate feedback.
|
||||
|
||||
The calibration of these devices does drift over time. If ghosts or contact
|
||||
dropping worsen and interfere with the normal usage of your device, try
|
||||
recalibrating it.
|
||||
|
||||
|
||||
Calibration
|
||||
-----------
|
||||
|
||||
The N-Trig windows tools provide calibration and testing routines. Also an
|
||||
unofficial unsupported set of user space tools including a calibrator is
|
||||
available at:
|
||||
http://code.launchpad.net/~rafi-seas/+junk/ntrig_calib
|
||||
|
||||
|
||||
Tracking
|
||||
--------
|
||||
|
||||
As of yet, all tested N-Trig firmwares do not track fingers. When multiple
|
||||
contacts are active they seem to be sorted primarily by Y position.
|
@ -56,20 +56,20 @@ menu "Special HID drivers"
|
||||
depends on HID
|
||||
|
||||
config HID_3M_PCT
|
||||
tristate "3M PCT"
|
||||
tristate "3M PCT touchscreen"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for 3M PCT touch screens.
|
||||
|
||||
config HID_A4TECH
|
||||
tristate "A4 tech" if EMBEDDED
|
||||
tristate "A4 tech mice" if EMBEDDED
|
||||
depends on USB_HID
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
Support for A4 tech X5 and WOP-35 / Trust 450L mice.
|
||||
|
||||
config HID_ACRUX_FF
|
||||
tristate "ACRUX force feedback support"
|
||||
tristate "ACRUX force feedback"
|
||||
depends on USB_HID
|
||||
select INPUT_FF_MEMLESS
|
||||
---help---
|
||||
@ -77,7 +77,7 @@ config HID_ACRUX_FF
|
||||
game controllers.
|
||||
|
||||
config HID_APPLE
|
||||
tristate "Apple" if EMBEDDED
|
||||
tristate "Apple {i,Power,Mac}Books" if EMBEDDED
|
||||
depends on (USB_HID || BT_HIDP)
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
@ -88,7 +88,7 @@ config HID_APPLE
|
||||
MacBooks, MacBook Pros and Apple Aluminum.
|
||||
|
||||
config HID_BELKIN
|
||||
tristate "Belkin" if EMBEDDED
|
||||
tristate "Belkin Flip KVM and Wireless keyboard" if EMBEDDED
|
||||
depends on USB_HID
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
@ -101,14 +101,14 @@ config HID_CANDO
|
||||
Support for Cando dual touch panel.
|
||||
|
||||
config HID_CHERRY
|
||||
tristate "Cherry" if EMBEDDED
|
||||
tristate "Cherry Cymotion keyboard" if EMBEDDED
|
||||
depends on USB_HID
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
Support for Cherry Cymotion keyboard.
|
||||
|
||||
config HID_CHICONY
|
||||
tristate "Chicony" if EMBEDDED
|
||||
tristate "Chicony Tactical pad" if EMBEDDED
|
||||
depends on USB_HID
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
@ -130,20 +130,20 @@ config HID_PRODIKEYS
|
||||
and some additional multimedia keys.
|
||||
|
||||
config HID_CYPRESS
|
||||
tristate "Cypress" if EMBEDDED
|
||||
tristate "Cypress mouse and barcode readers" if EMBEDDED
|
||||
depends on USB_HID
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
Support for cypress mouse and barcode readers.
|
||||
|
||||
config HID_DRAGONRISE
|
||||
tristate "DragonRise Inc. support"
|
||||
tristate "DragonRise Inc. game controller"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Say Y here if you have DragonRise Inc.game controllers.
|
||||
|
||||
config DRAGONRISE_FF
|
||||
bool "DragonRise Inc. force feedback support"
|
||||
bool "DragonRise Inc. force feedback"
|
||||
depends on HID_DRAGONRISE
|
||||
select INPUT_FF_MEMLESS
|
||||
---help---
|
||||
@ -157,46 +157,58 @@ config HID_EGALAX
|
||||
Support for the eGalax dual-touch panel.
|
||||
|
||||
config HID_ELECOM
|
||||
tristate "ELECOM"
|
||||
tristate "ELECOM BM084 bluetooth mouse"
|
||||
depends on BT_HIDP
|
||||
---help---
|
||||
Support for the ELECOM BM084 (bluetooth mouse).
|
||||
|
||||
config HID_EZKEY
|
||||
tristate "Ezkey" if EMBEDDED
|
||||
tristate "Ezkey BTC 8193 keyboard" if EMBEDDED
|
||||
depends on USB_HID
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
Support for Ezkey BTC 8193 keyboard.
|
||||
|
||||
config HID_KYE
|
||||
tristate "Kye" if EMBEDDED
|
||||
tristate "Kye/Genius Ergo Mouse" if EMBEDDED
|
||||
depends on USB_HID
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
Support for Kye/Genius Ergo Mouse.
|
||||
|
||||
config HID_UCLOGIC
|
||||
tristate "UC-Logic"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for UC-Logic tablets.
|
||||
|
||||
config HID_WALTOP
|
||||
tristate "Waltop"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Waltop tablets.
|
||||
|
||||
config HID_GYRATION
|
||||
tristate "Gyration"
|
||||
tristate "Gyration remote control"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Gyration remote control.
|
||||
|
||||
config HID_TWINHAN
|
||||
tristate "Twinhan"
|
||||
tristate "Twinhan IR remote control"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Twinhan IR remote control.
|
||||
|
||||
config HID_KENSINGTON
|
||||
tristate "Kensington" if EMBEDDED
|
||||
tristate "Kensington Slimblade Trackball" if EMBEDDED
|
||||
depends on USB_HID
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
Support for Kensington Slimblade Trackball.
|
||||
|
||||
config HID_LOGITECH
|
||||
tristate "Logitech" if EMBEDDED
|
||||
tristate "Logitech devices" if EMBEDDED
|
||||
depends on USB_HID
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
@ -220,12 +232,12 @@ config LOGITECH_FF
|
||||
force feedback.
|
||||
|
||||
config LOGIRUMBLEPAD2_FF
|
||||
bool "Logitech Rumblepad 2 force feedback support"
|
||||
bool "Logitech RumblePad/Rumblepad 2 force feedback support"
|
||||
depends on HID_LOGITECH
|
||||
select INPUT_FF_MEMLESS
|
||||
help
|
||||
Say Y here if you want to enable force feedback support for Logitech
|
||||
Rumblepad 2 devices.
|
||||
RumblePad and Rumblepad 2 devices.
|
||||
|
||||
config LOGIG940_FF
|
||||
bool "Logitech Flight System G940 force feedback support"
|
||||
@ -235,6 +247,14 @@ config LOGIG940_FF
|
||||
Say Y here if you want to enable force feedback support for Logitech
|
||||
Flight System G940 devices.
|
||||
|
||||
config LOGIWII_FF
|
||||
bool "Logitech Speed Force Wireless force feedback support"
|
||||
depends on HID_LOGITECH
|
||||
select INPUT_FF_MEMLESS
|
||||
help
|
||||
Say Y here if you want to enable force feedback support for Logitech
|
||||
Speed Force Wireless (Wii) devices.
|
||||
|
||||
config HID_MAGICMOUSE
|
||||
tristate "Apple MagicMouse multi-touch support"
|
||||
depends on BT_HIDP
|
||||
@ -245,39 +265,39 @@ config HID_MAGICMOUSE
|
||||
Apple Wireless "Magic" Mouse.
|
||||
|
||||
config HID_MICROSOFT
|
||||
tristate "Microsoft" if EMBEDDED
|
||||
tristate "Microsoft non-fully HID-compliant devices" if EMBEDDED
|
||||
depends on USB_HID
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
Support for Microsoft devices that are not fully compliant with HID standard.
|
||||
|
||||
config HID_MOSART
|
||||
tristate "MosArt"
|
||||
tristate "MosArt dual-touch panels"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for MosArt dual-touch panels.
|
||||
|
||||
config HID_MONTEREY
|
||||
tristate "Monterey" if EMBEDDED
|
||||
tristate "Monterey Genius KB29E keyboard" if EMBEDDED
|
||||
depends on USB_HID
|
||||
default !EMBEDDED
|
||||
---help---
|
||||
Support for Monterey Genius KB29E.
|
||||
|
||||
config HID_NTRIG
|
||||
tristate "NTrig"
|
||||
tristate "N-Trig touch screen"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for N-Trig touch screen.
|
||||
|
||||
config HID_ORTEK
|
||||
tristate "Ortek"
|
||||
tristate "Ortek WKB-2000 wireless keyboard and mouse trackpad"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Ortek WKB-2000 wireless keyboard + mouse trackpad.
|
||||
|
||||
config HID_PANTHERLORD
|
||||
tristate "Pantherlord support"
|
||||
tristate "Pantherlord/GreenAsia game controller"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Say Y here if you have a PantherLord/GreenAsia based game controller
|
||||
@ -292,7 +312,7 @@ config PANTHERLORD_FF
|
||||
or adapter and want to enable force feedback support for it.
|
||||
|
||||
config HID_PETALYNX
|
||||
tristate "Petalynx"
|
||||
tristate "Petalynx Maxter remote control"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Petalynx Maxter remote control.
|
||||
@ -356,7 +376,7 @@ config HID_PICOLCD_LEDS
|
||||
Provide access to PicoLCD's GPO pins via leds class.
|
||||
|
||||
config HID_QUANTA
|
||||
tristate "Quanta Optical Touch"
|
||||
tristate "Quanta Optical Touch panels"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Quanta Optical Touch dual-touch panels.
|
||||
@ -376,32 +396,39 @@ config HID_ROCCAT_KONE
|
||||
---help---
|
||||
Support for Roccat Kone mouse.
|
||||
|
||||
config HID_ROCCAT_PYRA
|
||||
tristate "Roccat Pyra mouse support"
|
||||
depends on USB_HID
|
||||
select HID_ROCCAT
|
||||
---help---
|
||||
Support for Roccat Pyra mouse.
|
||||
|
||||
config HID_SAMSUNG
|
||||
tristate "Samsung"
|
||||
tristate "Samsung InfraRed remote control or keyboards"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Samsung InfraRed remote control or keyboards.
|
||||
|
||||
config HID_SONY
|
||||
tristate "Sony"
|
||||
tristate "Sony PS3 controller"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Sony PS3 controller.
|
||||
|
||||
config HID_STANTUM
|
||||
tristate "Stantum"
|
||||
tristate "Stantum multitouch panel"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Stantum multitouch panel.
|
||||
|
||||
config HID_SUNPLUS
|
||||
tristate "Sunplus"
|
||||
tristate "Sunplus wireless desktop"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Sunplus wireless desktop.
|
||||
|
||||
config HID_GREENASIA
|
||||
tristate "GreenAsia (Product ID 0x12) support"
|
||||
tristate "GreenAsia (Product ID 0x12) game controller support"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Say Y here if you have a GreenAsia (Product ID 0x12) based game
|
||||
|
@ -21,6 +21,9 @@ endif
|
||||
ifdef CONFIG_LOGIG940_FF
|
||||
hid-logitech-objs += hid-lg3ff.o
|
||||
endif
|
||||
ifdef CONFIG_LOGIWII_FF
|
||||
hid-logitech-objs += hid-lg4ff.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o
|
||||
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
|
||||
@ -52,6 +55,7 @@ obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
|
||||
obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o
|
||||
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o
|
||||
obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o
|
||||
obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o
|
||||
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
|
||||
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
|
||||
obj-$(CONFIG_HID_SONY) += hid-sony.o
|
||||
@ -61,9 +65,11 @@ obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
|
||||
obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
|
||||
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
|
||||
obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
|
||||
obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
|
||||
obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
|
||||
obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
|
||||
obj-$(CONFIG_HID_WACOM) += hid-wacom.o
|
||||
obj-$(CONFIG_HID_WALTOP) += hid-waltop.o
|
||||
|
||||
obj-$(CONFIG_USB_HID) += usbhid/
|
||||
obj-$(CONFIG_USB_MOUSE) += usbhid/
|
||||
|
@ -2,6 +2,8 @@
|
||||
* HID driver for 3M PCT multitouch panels
|
||||
*
|
||||
* Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr>
|
||||
* Copyright (c) 2010 Henrik Rydberg <rydberg@euromail.se>
|
||||
* Copyright (c) 2010 Canonical, Ltd.
|
||||
*
|
||||
*/
|
||||
|
||||
@ -24,15 +26,26 @@ MODULE_LICENSE("GPL");
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define MAX_SLOTS 60
|
||||
#define MAX_TRKID USHRT_MAX
|
||||
#define MAX_EVENTS 360
|
||||
|
||||
/* estimated signal-to-noise ratios */
|
||||
#define SN_MOVE 2048
|
||||
#define SN_WIDTH 128
|
||||
|
||||
struct mmm_finger {
|
||||
__s32 x, y, w, h;
|
||||
__u8 rank;
|
||||
__u16 id;
|
||||
bool prev_touch;
|
||||
bool touch, valid;
|
||||
};
|
||||
|
||||
struct mmm_data {
|
||||
struct mmm_finger f[10];
|
||||
__u8 curid, num;
|
||||
struct mmm_finger f[MAX_SLOTS];
|
||||
__u16 id;
|
||||
__u8 curid;
|
||||
__u8 nexp, nreal;
|
||||
bool touch, valid;
|
||||
};
|
||||
|
||||
@ -40,6 +53,10 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
int f1 = field->logical_minimum;
|
||||
int f2 = field->logical_maximum;
|
||||
int df = f2 - f1;
|
||||
|
||||
switch (usage->hid & HID_USAGE_PAGE) {
|
||||
|
||||
case HID_UP_BUTTON:
|
||||
@ -50,18 +67,20 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
case HID_GD_X:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_X);
|
||||
input_set_abs_params(hi->input, ABS_MT_POSITION_X,
|
||||
f1, f2, df / SN_MOVE, 0);
|
||||
/* touchscreen emulation */
|
||||
input_set_abs_params(hi->input, ABS_X,
|
||||
field->logical_minimum,
|
||||
field->logical_maximum, 0, 0);
|
||||
f1, f2, df / SN_MOVE, 0);
|
||||
return 1;
|
||||
case HID_GD_Y:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_POSITION_Y);
|
||||
input_set_abs_params(hi->input, ABS_MT_POSITION_Y,
|
||||
f1, f2, df / SN_MOVE, 0);
|
||||
/* touchscreen emulation */
|
||||
input_set_abs_params(hi->input, ABS_Y,
|
||||
field->logical_minimum,
|
||||
field->logical_maximum, 0, 0);
|
||||
f1, f2, df / SN_MOVE, 0);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -81,21 +100,31 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
case HID_DG_TIPSWITCH:
|
||||
/* touchscreen emulation */
|
||||
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
|
||||
input_set_capability(hi->input, EV_KEY, BTN_TOUCH);
|
||||
return 1;
|
||||
case HID_DG_WIDTH:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_TOUCH_MAJOR);
|
||||
input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR,
|
||||
f1, f2, df / SN_WIDTH, 0);
|
||||
return 1;
|
||||
case HID_DG_HEIGHT:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_TOUCH_MINOR);
|
||||
input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR,
|
||||
f1, f2, df / SN_WIDTH, 0);
|
||||
input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
|
||||
1, 1, 0, 0);
|
||||
0, 1, 0, 0);
|
||||
return 1;
|
||||
case HID_DG_CONTACTID:
|
||||
field->logical_maximum = 59;
|
||||
field->logical_maximum = MAX_TRKID;
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_TRACKING_ID);
|
||||
input_set_abs_params(hi->input, ABS_MT_TRACKING_ID,
|
||||
0, MAX_TRKID, 0, 0);
|
||||
if (!hi->input->mt)
|
||||
input_mt_create_slots(hi->input, MAX_SLOTS);
|
||||
input_set_events_per_packet(hi->input, MAX_EVENTS);
|
||||
return 1;
|
||||
}
|
||||
/* let hid-input decide for the others */
|
||||
@ -113,10 +142,10 @@ static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
/* tell hid-input to skip setup of these event types */
|
||||
if (usage->type == EV_KEY || usage->type == EV_ABS)
|
||||
clear_bit(usage->code, *bit);
|
||||
|
||||
return 0;
|
||||
set_bit(usage->type, hi->input->evbit);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -126,70 +155,49 @@ static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
|
||||
{
|
||||
struct mmm_finger *oldest = 0;
|
||||
bool pressed = false, released = false;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* we need to iterate on all fingers to decide if we have a press
|
||||
* or a release event in our touchscreen emulation.
|
||||
*/
|
||||
for (i = 0; i < 10; ++i) {
|
||||
for (i = 0; i < MAX_SLOTS; ++i) {
|
||||
struct mmm_finger *f = &md->f[i];
|
||||
if (!f->valid) {
|
||||
/* this finger is just placeholder data, ignore */
|
||||
} else if (f->touch) {
|
||||
continue;
|
||||
}
|
||||
input_mt_slot(input, i);
|
||||
if (f->touch) {
|
||||
/* this finger is on the screen */
|
||||
int wide = (f->w > f->h);
|
||||
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i);
|
||||
/* divided by two to match visual scale of touch */
|
||||
int major = max(f->w, f->h) >> 1;
|
||||
int minor = min(f->w, f->h) >> 1;
|
||||
|
||||
if (!f->prev_touch)
|
||||
f->id = md->id++;
|
||||
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, f->id);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
|
||||
input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
|
||||
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR,
|
||||
wide ? f->w : f->h);
|
||||
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR,
|
||||
wide ? f->h : f->w);
|
||||
input_mt_sync(input);
|
||||
/*
|
||||
* touchscreen emulation: maintain the age rank
|
||||
* of this finger, decide if we have a press
|
||||
*/
|
||||
if (f->rank == 0) {
|
||||
f->rank = ++(md->num);
|
||||
if (f->rank == 1)
|
||||
pressed = true;
|
||||
}
|
||||
if (f->rank == 1)
|
||||
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
|
||||
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
|
||||
/* touchscreen emulation: pick the oldest contact */
|
||||
if (!oldest || ((f->id - oldest->id) & (SHRT_MAX + 1)))
|
||||
oldest = f;
|
||||
} else {
|
||||
/* this finger took off the screen */
|
||||
/* touchscreen emulation: maintain age rank of others */
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 10; ++j) {
|
||||
struct mmm_finger *g = &md->f[j];
|
||||
if (g->rank > f->rank) {
|
||||
g->rank--;
|
||||
if (g->rank == 1)
|
||||
oldest = g;
|
||||
}
|
||||
}
|
||||
f->rank = 0;
|
||||
--(md->num);
|
||||
if (md->num == 0)
|
||||
released = true;
|
||||
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1);
|
||||
}
|
||||
f->prev_touch = f->touch;
|
||||
f->valid = 0;
|
||||
}
|
||||
|
||||
/* touchscreen emulation */
|
||||
if (oldest) {
|
||||
if (pressed)
|
||||
input_event(input, EV_KEY, BTN_TOUCH, 1);
|
||||
input_event(input, EV_KEY, BTN_TOUCH, 1);
|
||||
input_event(input, EV_ABS, ABS_X, oldest->x);
|
||||
input_event(input, EV_ABS, ABS_Y, oldest->y);
|
||||
} else if (released) {
|
||||
} else {
|
||||
input_event(input, EV_KEY, BTN_TOUCH, 0);
|
||||
}
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -223,10 +231,12 @@ static int mmm_event(struct hid_device *hid, struct hid_field *field,
|
||||
md->f[md->curid].h = value;
|
||||
break;
|
||||
case HID_DG_CONTACTID:
|
||||
value = clamp_val(value, 0, MAX_SLOTS - 1);
|
||||
if (md->valid) {
|
||||
md->curid = value;
|
||||
md->f[value].touch = md->touch;
|
||||
md->f[value].valid = 1;
|
||||
md->nreal++;
|
||||
}
|
||||
break;
|
||||
case HID_GD_X:
|
||||
@ -238,7 +248,12 @@ static int mmm_event(struct hid_device *hid, struct hid_field *field,
|
||||
md->f[md->curid].y = value;
|
||||
break;
|
||||
case HID_DG_CONTACTCOUNT:
|
||||
mmm_filter_event(md, input);
|
||||
if (value)
|
||||
md->nexp = value;
|
||||
if (md->nreal >= md->nexp) {
|
||||
mmm_filter_event(md, input);
|
||||
md->nreal = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -255,6 +270,8 @@ static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
int ret;
|
||||
struct mmm_data *md;
|
||||
|
||||
hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
|
||||
|
||||
md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL);
|
||||
if (!md) {
|
||||
dev_err(&hdev->dev, "cannot allocate 3M data\n");
|
||||
|
@ -133,6 +133,8 @@ static const struct hid_device_id a4_devices[] = {
|
||||
.driver_data = A4_2WHEEL_MOUSE_HACK_7 },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D),
|
||||
.driver_data = A4_2WHEEL_MOUSE_HACK_B8 },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649),
|
||||
.driver_data = A4_2WHEEL_MOUSE_HACK_B8 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, a4_devices);
|
||||
|
@ -246,17 +246,18 @@ static int apple_event(struct hid_device *hdev, struct hid_field *field,
|
||||
/*
|
||||
* MacBook JIS keyboard has wrong logical maximum
|
||||
*/
|
||||
static void apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct apple_sc *asc = hid_get_drvdata(hdev);
|
||||
|
||||
if ((asc->quirks & APPLE_RDESC_JIS) && rsize >= 60 &&
|
||||
if ((asc->quirks & APPLE_RDESC_JIS) && *rsize >= 60 &&
|
||||
rdesc[53] == 0x65 && rdesc[59] == 0x65) {
|
||||
dev_info(&hdev->dev, "fixing up MacBook JIS keyboard report "
|
||||
"descriptor\n");
|
||||
rdesc[53] = rdesc[59] = 0xe7;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static void apple_setup_input(struct input_dev *input)
|
||||
|
@ -26,15 +26,16 @@
|
||||
* Cherry Cymotion keyboard have an invalid HID report descriptor,
|
||||
* that needs fixing before we can parse it.
|
||||
*/
|
||||
static void ch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *ch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
|
||||
if (*rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
|
||||
dev_info(&hdev->dev, "fixing up Cherry Cymotion report "
|
||||
"descriptor\n");
|
||||
rdesc[11] = rdesc[16] = 0xff;
|
||||
rdesc[12] = rdesc[17] = 0x03;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
#define ch_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
|
||||
|
@ -388,12 +388,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
|
||||
__u32 data;
|
||||
unsigned n;
|
||||
|
||||
/* Local delimiter could have value 0, which allows size to be 0 */
|
||||
if (item->size == 0 && item->tag != HID_LOCAL_ITEM_TAG_DELIMITER) {
|
||||
dbg_hid("item data expected for local item\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = item_udata(item);
|
||||
|
||||
switch (item->tag) {
|
||||
@ -651,7 +645,7 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
|
||||
};
|
||||
|
||||
if (device->driver->report_fixup)
|
||||
device->driver->report_fixup(device, start, size);
|
||||
start = device->driver->report_fixup(device, start, &size);
|
||||
|
||||
device->rdesc = kmemdup(start, size, GFP_KERNEL);
|
||||
if (device->rdesc == NULL)
|
||||
@ -1241,6 +1235,7 @@ static const struct hid_device_id hid_blacklist[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) },
|
||||
#if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE)
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) },
|
||||
#endif
|
||||
@ -1248,6 +1243,7 @@ static const struct hid_device_id hid_blacklist[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) },
|
||||
@ -1327,6 +1323,7 @@ static const struct hid_device_id hid_blacklist[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
|
||||
@ -1336,6 +1333,7 @@ static const struct hid_device_id hid_blacklist[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
|
||||
@ -1371,12 +1369,15 @@ static const struct hid_device_id hid_blacklist[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_MTP_STM) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, USB_DEVICE_ID_MTP_SITRONIX) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
|
||||
@ -1388,8 +1389,16 @@ static const struct hid_device_id hid_blacklist[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
|
||||
|
@ -31,16 +31,16 @@
|
||||
* Some USB barcode readers from cypress have usage min and usage max in
|
||||
* the wrong order
|
||||
*/
|
||||
static void cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
||||
unsigned int i;
|
||||
|
||||
if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX))
|
||||
return;
|
||||
return rdesc;
|
||||
|
||||
for (i = 0; i < rsize - 4; i++)
|
||||
for (i = 0; i < *rsize - 4; i++)
|
||||
if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
|
||||
__u8 tmp;
|
||||
|
||||
@ -50,6 +50,7 @@ static void cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
rdesc[i + 3] = rdesc[i + 1];
|
||||
rdesc[i + 1] = tmp;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
|
@ -570,6 +570,8 @@ void hid_debug_event(struct hid_device *hdev, char *buf)
|
||||
buf[i];
|
||||
list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
|
||||
}
|
||||
|
||||
wake_up_interruptible(&hdev->debug_wait);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_debug_event);
|
||||
|
||||
|
@ -31,7 +31,7 @@ struct egalax_data {
|
||||
bool first; /* is this the first finger in the frame? */
|
||||
bool valid; /* valid finger data, or just placeholder? */
|
||||
bool activity; /* at least one active finger previously? */
|
||||
__u16 lastx, lasty; /* latest valid (x, y) in the frame */
|
||||
__u16 lastx, lasty, lastz; /* latest valid (x, y, z) in the frame */
|
||||
};
|
||||
|
||||
static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
@ -79,6 +79,10 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
case HID_DG_TIPPRESSURE:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_PRESSURE);
|
||||
/* touchscreen emulation */
|
||||
input_set_abs_params(hi->input, ABS_PRESSURE,
|
||||
field->logical_minimum,
|
||||
field->logical_maximum, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -109,8 +113,8 @@ static void egalax_filter_event(struct egalax_data *td, struct input_dev *input)
|
||||
if (td->valid) {
|
||||
/* emit multitouch events */
|
||||
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x >> 3);
|
||||
input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y >> 3);
|
||||
input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z);
|
||||
|
||||
input_mt_sync(input);
|
||||
@ -121,6 +125,7 @@ static void egalax_filter_event(struct egalax_data *td, struct input_dev *input)
|
||||
*/
|
||||
td->lastx = td->x;
|
||||
td->lasty = td->y;
|
||||
td->lastz = td->z;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -129,8 +134,9 @@ static void egalax_filter_event(struct egalax_data *td, struct input_dev *input)
|
||||
* the oldest on the panel, the one we want for single touch
|
||||
*/
|
||||
if (!td->first && td->activity) {
|
||||
input_event(input, EV_ABS, ABS_X, td->lastx);
|
||||
input_event(input, EV_ABS, ABS_Y, td->lasty);
|
||||
input_event(input, EV_ABS, ABS_X, td->lastx >> 3);
|
||||
input_event(input, EV_ABS, ABS_Y, td->lasty >> 3);
|
||||
input_event(input, EV_ABS, ABS_PRESSURE, td->lastz);
|
||||
}
|
||||
|
||||
if (!td->valid) {
|
||||
|
@ -20,14 +20,15 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static void elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) {
|
||||
if (*rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) {
|
||||
dev_info(&hdev->dev, "Fixing up Elecom BM084 "
|
||||
"report descriptor.\n");
|
||||
rdesc[47] = 0x00;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static const struct hid_device_id elecom_devices[] = {
|
||||
|
@ -25,6 +25,7 @@
|
||||
#define USB_VENDOR_ID_A4TECH 0x09da
|
||||
#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006
|
||||
#define USB_DEVICE_ID_A4TECH_X5_005D 0x000a
|
||||
#define USB_DEVICE_ID_A4TECH_RP_649 0x001a
|
||||
|
||||
#define USB_VENDOR_ID_AASHIMA 0x06d6
|
||||
#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025
|
||||
@ -63,6 +64,7 @@
|
||||
#define USB_VENDOR_ID_APPLE 0x05ac
|
||||
#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
|
||||
#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d
|
||||
#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e
|
||||
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e
|
||||
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214
|
||||
@ -142,6 +144,7 @@
|
||||
#define USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE 0x0051
|
||||
#define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE 0x00ff
|
||||
#define USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK 0x00d3
|
||||
#define USB_DEVICE_ID_CH_AXIS_295 0x001c
|
||||
|
||||
#define USB_VENDOR_ID_CHERRY 0x046a
|
||||
#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
|
||||
@ -343,6 +346,7 @@
|
||||
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
|
||||
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
|
||||
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
|
||||
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a
|
||||
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211
|
||||
#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215
|
||||
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218
|
||||
@ -354,6 +358,7 @@
|
||||
#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293
|
||||
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
|
||||
#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
|
||||
#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c
|
||||
#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
|
||||
#define USB_DEVICE_ID_S510_RECEIVER 0xc50c
|
||||
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
|
||||
@ -466,6 +471,8 @@
|
||||
|
||||
#define USB_VENDOR_ID_ROCCAT 0x1e7d
|
||||
#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced
|
||||
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24
|
||||
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6
|
||||
|
||||
#define USB_VENDOR_ID_SAITEK 0x06a3
|
||||
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
|
||||
@ -485,6 +492,12 @@
|
||||
#define USB_VENDOR_ID_STANTUM 0x1f87
|
||||
#define USB_DEVICE_ID_MTP 0x0002
|
||||
|
||||
#define USB_VENDOR_ID_STANTUM_STM 0x0483
|
||||
#define USB_DEVICE_ID_MTP_STM 0x3261
|
||||
|
||||
#define USB_VENDOR_ID_STANTUM_SITRONIX 0x1403
|
||||
#define USB_DEVICE_ID_MTP_SITRONIX 0x5001
|
||||
|
||||
#define USB_VENDOR_ID_SUN 0x0430
|
||||
#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
|
||||
|
||||
@ -514,8 +527,10 @@
|
||||
|
||||
#define USB_VENDOR_ID_UCLOGIC 0x5543
|
||||
#define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042
|
||||
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U 0x0003
|
||||
#define USB_DEVICE_ID_UCLOGIC_TABLET_KNA5 0x6001
|
||||
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U 0x0003
|
||||
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U 0x0004
|
||||
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005
|
||||
|
||||
#define USB_VENDOR_ID_VERNIER 0x08f7
|
||||
#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
|
||||
@ -527,6 +542,12 @@
|
||||
#define USB_VENDOR_ID_WACOM 0x056a
|
||||
#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81
|
||||
|
||||
#define USB_VENDOR_ID_WALTOP 0x172f
|
||||
#define USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH 0x0032
|
||||
#define USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH 0x0034
|
||||
#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH 0x0501
|
||||
#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH 0x0500
|
||||
|
||||
#define USB_VENDOR_ID_WISEGROUP 0x0925
|
||||
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
|
||||
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
|
||||
|
@ -149,6 +149,83 @@ static int hidinput_setkeycode(struct input_dev *dev,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hidinput_calc_abs_res - calculate an absolute axis resolution
|
||||
* @field: the HID report field to calculate resolution for
|
||||
* @code: axis code
|
||||
*
|
||||
* The formula is:
|
||||
* (logical_maximum - logical_minimum)
|
||||
* resolution = ----------------------------------------------------------
|
||||
* (physical_maximum - physical_minimum) * 10 ^ unit_exponent
|
||||
*
|
||||
* as seen in the HID specification v1.11 6.2.2.7 Global Items.
|
||||
*
|
||||
* Only exponent 1 length units are processed. Centimeters are converted to
|
||||
* inches. Degrees are converted to radians.
|
||||
*/
|
||||
static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
|
||||
{
|
||||
__s32 unit_exponent = field->unit_exponent;
|
||||
__s32 logical_extents = field->logical_maximum -
|
||||
field->logical_minimum;
|
||||
__s32 physical_extents = field->physical_maximum -
|
||||
field->physical_minimum;
|
||||
__s32 prev;
|
||||
|
||||
/* Check if the extents are sane */
|
||||
if (logical_extents <= 0 || physical_extents <= 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Verify and convert units.
|
||||
* See HID specification v1.11 6.2.2.7 Global Items for unit decoding
|
||||
*/
|
||||
if (code == ABS_X || code == ABS_Y || code == ABS_Z) {
|
||||
if (field->unit == 0x11) { /* If centimeters */
|
||||
/* Convert to inches */
|
||||
prev = logical_extents;
|
||||
logical_extents *= 254;
|
||||
if (logical_extents < prev)
|
||||
return 0;
|
||||
unit_exponent += 2;
|
||||
} else if (field->unit != 0x13) { /* If not inches */
|
||||
return 0;
|
||||
}
|
||||
} else if (code == ABS_RX || code == ABS_RY || code == ABS_RZ) {
|
||||
if (field->unit == 0x14) { /* If degrees */
|
||||
/* Convert to radians */
|
||||
prev = logical_extents;
|
||||
logical_extents *= 573;
|
||||
if (logical_extents < prev)
|
||||
return 0;
|
||||
unit_exponent += 1;
|
||||
} else if (field->unit != 0x12) { /* If not radians */
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Apply negative unit exponent */
|
||||
for (; unit_exponent < 0; unit_exponent++) {
|
||||
prev = logical_extents;
|
||||
logical_extents *= 10;
|
||||
if (logical_extents < prev)
|
||||
return 0;
|
||||
}
|
||||
/* Apply positive unit exponent */
|
||||
for (; unit_exponent > 0; unit_exponent--) {
|
||||
prev = physical_extents;
|
||||
physical_extents *= 10;
|
||||
if (physical_extents < prev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate resolution */
|
||||
return logical_extents / physical_extents;
|
||||
}
|
||||
|
||||
static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
|
||||
struct hid_usage *usage)
|
||||
{
|
||||
@ -336,6 +413,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
||||
map_key_clear(BTN_STYLUS);
|
||||
break;
|
||||
|
||||
case 0x46: /* TabletPick */
|
||||
map_key_clear(BTN_STYLUS2);
|
||||
break;
|
||||
|
||||
default: goto unknown;
|
||||
}
|
||||
break;
|
||||
@ -537,6 +618,9 @@ mapped:
|
||||
input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4);
|
||||
else input_set_abs_params(input, usage->code, a, b, 0, 0);
|
||||
|
||||
input_abs_set_res(input, usage->code,
|
||||
hidinput_calc_abs_res(field, usage->code));
|
||||
|
||||
/* use a larger default input buffer for MT devices */
|
||||
if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0)
|
||||
input_set_events_per_packet(input, 60);
|
||||
@ -659,6 +743,9 @@ void hidinput_report_event(struct hid_device *hid, struct hid_report *report)
|
||||
{
|
||||
struct hid_input *hidinput;
|
||||
|
||||
if (hid->quirks & HID_QUIRK_NO_INPUT_SYNC)
|
||||
return;
|
||||
|
||||
list_for_each_entry(hidinput, &hid->inputs, list)
|
||||
input_sync(hidinput->input);
|
||||
}
|
||||
|
@ -23,10 +23,10 @@
|
||||
* - report size 8 count 1 must be size 1 count 8 for button bitfield
|
||||
* - change the button usage range to 4-7 for the extra buttons
|
||||
*/
|
||||
static void kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (rsize >= 74 &&
|
||||
if (*rsize >= 74 &&
|
||||
rdesc[61] == 0x05 && rdesc[62] == 0x08 &&
|
||||
rdesc[63] == 0x19 && rdesc[64] == 0x08 &&
|
||||
rdesc[65] == 0x29 && rdesc[66] == 0x0f &&
|
||||
@ -40,6 +40,7 @@ static void kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
rdesc[72] = 0x01;
|
||||
rdesc[74] = 0x08;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static const struct hid_device_id kye_devices[] = {
|
||||
|
@ -7,6 +7,7 @@
|
||||
* Copyright (c) 2006-2007 Jiri Kosina
|
||||
* Copyright (c) 2007 Paul Walmsley
|
||||
* Copyright (c) 2008 Jiri Slaby
|
||||
* Copyright (c) 2010 Hendrik Iben
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -19,6 +20,9 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
#include "hid-lg.h"
|
||||
@ -35,31 +39,43 @@
|
||||
#define LG_FF2 0x400
|
||||
#define LG_RDESC_REL_ABS 0x800
|
||||
#define LG_FF3 0x1000
|
||||
#define LG_FF4 0x2000
|
||||
|
||||
/*
|
||||
* Certain Logitech keyboards send in report #3 keys which are far
|
||||
* above the logical maximum described in descriptor. This extends
|
||||
* the original value of 0x28c of logical maximum to 0x104d
|
||||
*/
|
||||
static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
||||
|
||||
if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 &&
|
||||
if ((quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
|
||||
rdesc[84] == 0x8c && rdesc[85] == 0x02) {
|
||||
dev_info(&hdev->dev, "fixing up Logitech keyboard report "
|
||||
"descriptor\n");
|
||||
rdesc[84] = rdesc[89] = 0x4d;
|
||||
rdesc[85] = rdesc[90] = 0x10;
|
||||
}
|
||||
if ((quirks & LG_RDESC_REL_ABS) && rsize >= 50 &&
|
||||
if ((quirks & LG_RDESC_REL_ABS) && *rsize >= 50 &&
|
||||
rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
|
||||
rdesc[49] == 0x81 && rdesc[50] == 0x06) {
|
||||
dev_info(&hdev->dev, "fixing up rel/abs in Logitech "
|
||||
"report descriptor\n");
|
||||
rdesc[33] = rdesc[50] = 0x02;
|
||||
}
|
||||
if ((quirks & LG_FF4) && *rsize >= 101 &&
|
||||
rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
|
||||
rdesc[47] == 0x05 && rdesc[48] == 0x09) {
|
||||
dev_info(&hdev->dev, "fixing up Logitech Speed Force Wireless "
|
||||
"button descriptor\n");
|
||||
rdesc[41] = 0x05;
|
||||
rdesc[42] = 0x09;
|
||||
rdesc[47] = 0x95;
|
||||
rdesc[48] = 0x0B;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
|
||||
@ -285,12 +301,33 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (quirks & LG_FF4) {
|
||||
unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
|
||||
|
||||
if (ret >= 0) {
|
||||
/* insert a little delay of 10 jiffies ~ 40ms */
|
||||
wait_queue_head_t wait;
|
||||
init_waitqueue_head (&wait);
|
||||
wait_event_interruptible_timeout(wait, 0, 10);
|
||||
|
||||
/* Select random Address */
|
||||
buf[1] = 0xB2;
|
||||
get_random_bytes(&buf[2], 2);
|
||||
|
||||
ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
|
||||
}
|
||||
}
|
||||
|
||||
if (quirks & LG_FF)
|
||||
lgff_init(hdev);
|
||||
if (quirks & LG_FF2)
|
||||
lg2ff_init(hdev);
|
||||
if (quirks & LG_FF3)
|
||||
lg3ff_init(hdev);
|
||||
if (quirks & LG_FF4)
|
||||
lg4ff_init(hdev);
|
||||
|
||||
return 0;
|
||||
err_free:
|
||||
@ -325,6 +362,8 @@ static const struct hid_device_id lg_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
|
||||
.driver_data = LG_NOGET | LG_FF },
|
||||
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
|
||||
.driver_data = LG_FF2 },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
|
||||
.driver_data = LG_FF },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
|
||||
@ -339,6 +378,8 @@ static const struct hid_device_id lg_devices[] = {
|
||||
.driver_data = LG_FF },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
|
||||
.driver_data = LG_FF },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
|
||||
.driver_data = LG_FF4 },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
|
||||
.driver_data = LG_FF },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
|
||||
|
@ -19,4 +19,10 @@ int lg3ff_init(struct hid_device *hdev);
|
||||
static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LOGIWII_FF
|
||||
int lg4ff_init(struct hid_device *hdev);
|
||||
#else
|
||||
static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Force feedback support for Logitech Rumblepad 2
|
||||
* Force feedback support for Logitech RumblePad and Rumblepad 2
|
||||
*
|
||||
* Copyright (c) 2008 Anssi Hannula <anssi.hannula@gmail.com>
|
||||
*/
|
||||
@ -110,7 +110,7 @@ int lg2ff_init(struct hid_device *hid)
|
||||
|
||||
usbhid_submit_report(hid, report, USB_DIR_OUT);
|
||||
|
||||
dev_info(&hid->dev, "Force feedback for Logitech Rumblepad 2 by "
|
||||
dev_info(&hid->dev, "Force feedback for Logitech RumblePad/Rumblepad 2 by "
|
||||
"Anssi Hannula <anssi.hannula@gmail.com>\n");
|
||||
|
||||
return 0;
|
||||
|
136
drivers/hid/hid-lg4ff.c
Normal file
136
drivers/hid/hid-lg4ff.c
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Force feedback support for Logitech Speed Force Wireless
|
||||
*
|
||||
* http://wiibrew.org/wiki/Logitech_USB_steering_wheel
|
||||
*
|
||||
* Copyright (c) 2010 Simon Wood <simon@mungewell.org>
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
|
||||
#include "usbhid/usbhid.h"
|
||||
#include "hid-lg.h"
|
||||
|
||||
struct lg4ff_device {
|
||||
struct hid_report *report;
|
||||
};
|
||||
|
||||
static const signed short ff4_wheel_ac[] = {
|
||||
FF_CONSTANT,
|
||||
FF_AUTOCENTER,
|
||||
-1
|
||||
};
|
||||
|
||||
static int hid_lg4ff_play(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
|
||||
int x;
|
||||
|
||||
#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
|
||||
|
||||
switch (effect->type) {
|
||||
case FF_CONSTANT:
|
||||
x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
|
||||
CLAMP(x);
|
||||
report->field[0]->value[0] = 0x11; /* Slot 1 */
|
||||
report->field[0]->value[1] = 0x10;
|
||||
report->field[0]->value[2] = x;
|
||||
report->field[0]->value[3] = 0x00;
|
||||
report->field[0]->value[4] = 0x00;
|
||||
report->field[0]->value[5] = 0x08;
|
||||
report->field[0]->value[6] = 0x00;
|
||||
dbg_hid("Autocenter, x=0x%02X\n", x);
|
||||
|
||||
usbhid_submit_report(hid, report, USB_DIR_OUT);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hid_lg4ff_set_autocenter(struct input_dev *dev, u16 magnitude)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
|
||||
__s32 *value = report->field[0]->value;
|
||||
|
||||
*value++ = 0xfe;
|
||||
*value++ = 0x0d;
|
||||
*value++ = 0x07;
|
||||
*value++ = 0x07;
|
||||
*value++ = (magnitude >> 8) & 0xff;
|
||||
*value++ = 0x00;
|
||||
*value = 0x00;
|
||||
|
||||
usbhid_submit_report(hid, report, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
|
||||
int lg4ff_init(struct hid_device *hid)
|
||||
{
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct input_dev *dev = hidinput->input;
|
||||
struct hid_report *report;
|
||||
struct hid_field *field;
|
||||
const signed short *ff_bits = ff4_wheel_ac;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
/* Find the report to use */
|
||||
if (list_empty(report_list)) {
|
||||
err_hid("No output report found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check that the report looks ok */
|
||||
report = list_entry(report_list->next, struct hid_report, list);
|
||||
if (!report) {
|
||||
err_hid("NULL output report");
|
||||
return -1;
|
||||
}
|
||||
|
||||
field = report->field[0];
|
||||
if (!field) {
|
||||
err_hid("NULL field");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; ff_bits[i] >= 0; i++)
|
||||
set_bit(ff_bits[i], dev->ffbit);
|
||||
|
||||
error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (test_bit(FF_AUTOCENTER, dev->ffbit))
|
||||
dev->ff->set_autocenter = hid_lg4ff_set_autocenter;
|
||||
|
||||
dev_info(&hid->dev, "Force feedback for Logitech Speed Force Wireless by "
|
||||
"Simon Wood <simon@mungewell.org>\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Apple "Magic" Wireless Mouse driver
|
||||
*
|
||||
* Copyright (c) 2010 Michael Poole <mdpoole@troilus.org>
|
||||
* Copyright (c) 2010 Chase Douglas <chase.douglas@canonical.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -53,7 +54,9 @@ static bool report_undeciphered;
|
||||
module_param(report_undeciphered, bool, 0644);
|
||||
MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
|
||||
|
||||
#define TOUCH_REPORT_ID 0x29
|
||||
#define TRACKPAD_REPORT_ID 0x28
|
||||
#define MOUSE_REPORT_ID 0x29
|
||||
#define DOUBLE_REPORT_ID 0xf7
|
||||
/* These definitions are not precise, but they're close enough. (Bits
|
||||
* 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem
|
||||
* to be some kind of bit mask -- 0x20 may be a near-field reading,
|
||||
@ -67,15 +70,19 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
|
||||
|
||||
#define SCROLL_ACCEL_DEFAULT 7
|
||||
|
||||
/* Single touch emulation should only begin when no touches are currently down.
|
||||
* This is true when single_touch_id is equal to NO_TOUCHES. If multiple touches
|
||||
* are down and the touch providing for single touch emulation is lifted,
|
||||
* single_touch_id is equal to SINGLE_TOUCH_UP. While single touch emulation is
|
||||
* occuring, single_touch_id corresponds with the tracking id of the touch used.
|
||||
*/
|
||||
#define NO_TOUCHES -1
|
||||
#define SINGLE_TOUCH_UP -2
|
||||
|
||||
/**
|
||||
* struct magicmouse_sc - Tracks Magic Mouse-specific data.
|
||||
* @input: Input device through which we report events.
|
||||
* @quirks: Currently unused.
|
||||
* @last_timestamp: Timestamp from most recent (18-bit) touch report
|
||||
* (units of milliseconds over short windows, but seems to
|
||||
* increase faster when there are no touches).
|
||||
* @delta_time: 18-bit difference between the two most recent touch
|
||||
* reports from the mouse.
|
||||
* @ntouches: Number of touches in most recent touch report.
|
||||
* @scroll_accel: Number of consecutive scroll motions.
|
||||
* @scroll_jiffies: Time of last scroll motion.
|
||||
@ -86,8 +93,6 @@ struct magicmouse_sc {
|
||||
struct input_dev *input;
|
||||
unsigned long quirks;
|
||||
|
||||
int last_timestamp;
|
||||
int delta_time;
|
||||
int ntouches;
|
||||
int scroll_accel;
|
||||
unsigned long scroll_jiffies;
|
||||
@ -98,9 +103,9 @@ struct magicmouse_sc {
|
||||
short scroll_x;
|
||||
short scroll_y;
|
||||
u8 size;
|
||||
u8 down;
|
||||
} touches[16];
|
||||
int tracking_ids[16];
|
||||
int single_touch_id;
|
||||
};
|
||||
|
||||
static int magicmouse_firm_touch(struct magicmouse_sc *msc)
|
||||
@ -166,18 +171,35 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state)
|
||||
static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata)
|
||||
{
|
||||
struct input_dev *input = msc->input;
|
||||
__s32 x_y = tdata[0] << 8 | tdata[1] << 16 | tdata[2] << 24;
|
||||
int misc = tdata[5] | tdata[6] << 8;
|
||||
int id = (misc >> 6) & 15;
|
||||
int x = x_y << 12 >> 20;
|
||||
int y = -(x_y >> 20);
|
||||
int down = (tdata[7] & TOUCH_STATE_MASK) != TOUCH_STATE_NONE;
|
||||
int id, x, y, size, orientation, touch_major, touch_minor, state, down;
|
||||
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
||||
id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
|
||||
x = (tdata[1] << 28 | tdata[0] << 20) >> 20;
|
||||
y = -((tdata[2] << 24 | tdata[1] << 16) >> 20);
|
||||
size = tdata[5] & 0x3f;
|
||||
orientation = (tdata[6] >> 2) - 32;
|
||||
touch_major = tdata[3];
|
||||
touch_minor = tdata[4];
|
||||
state = tdata[7] & TOUCH_STATE_MASK;
|
||||
down = state != TOUCH_STATE_NONE;
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf;
|
||||
x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
|
||||
y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19);
|
||||
size = tdata[6] & 0x3f;
|
||||
orientation = (tdata[7] >> 2) - 32;
|
||||
touch_major = tdata[4];
|
||||
touch_minor = tdata[5];
|
||||
state = tdata[8] & TOUCH_STATE_MASK;
|
||||
down = state != TOUCH_STATE_NONE;
|
||||
}
|
||||
|
||||
/* Store tracking ID and other fields. */
|
||||
msc->tracking_ids[raw_id] = id;
|
||||
msc->touches[id].x = x;
|
||||
msc->touches[id].y = y;
|
||||
msc->touches[id].size = misc & 63;
|
||||
msc->touches[id].size = size;
|
||||
|
||||
/* If requested, emulate a scroll wheel by detecting small
|
||||
* vertical touch motions.
|
||||
@ -188,7 +210,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
|
||||
int step_y = msc->touches[id].scroll_y - y;
|
||||
|
||||
/* Calculate and apply the scroll motion. */
|
||||
switch (tdata[7] & TOUCH_STATE_MASK) {
|
||||
switch (state) {
|
||||
case TOUCH_STATE_START:
|
||||
msc->touches[id].scroll_x = x;
|
||||
msc->touches[id].scroll_y = y;
|
||||
@ -222,21 +244,28 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
|
||||
}
|
||||
}
|
||||
|
||||
if (down) {
|
||||
msc->ntouches++;
|
||||
if (msc->single_touch_id == NO_TOUCHES)
|
||||
msc->single_touch_id = id;
|
||||
} else if (msc->single_touch_id == id)
|
||||
msc->single_touch_id = SINGLE_TOUCH_UP;
|
||||
|
||||
/* Generate the input events for this touch. */
|
||||
if (report_touches && down) {
|
||||
int orientation = (misc >> 10) - 32;
|
||||
|
||||
msc->touches[id].down = 1;
|
||||
|
||||
input_report_abs(input, ABS_MT_TRACKING_ID, id);
|
||||
input_report_abs(input, ABS_MT_TOUCH_MAJOR, tdata[3]);
|
||||
input_report_abs(input, ABS_MT_TOUCH_MINOR, tdata[4]);
|
||||
input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2);
|
||||
input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2);
|
||||
input_report_abs(input, ABS_MT_ORIENTATION, orientation);
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
|
||||
if (report_undeciphered)
|
||||
input_event(input, EV_MSC, MSC_RAW, tdata[7]);
|
||||
if (report_undeciphered) {
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
|
||||
input_event(input, EV_MSC, MSC_RAW, tdata[7]);
|
||||
else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
input_event(input, EV_MSC, MSC_RAW, tdata[8]);
|
||||
}
|
||||
|
||||
input_mt_sync(input);
|
||||
}
|
||||
@ -247,39 +276,43 @@ static int magicmouse_raw_event(struct hid_device *hdev,
|
||||
{
|
||||
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
|
||||
struct input_dev *input = msc->input;
|
||||
int x, y, ts, ii, clicks, last_up;
|
||||
int x = 0, y = 0, ii, clicks = 0, npoints;
|
||||
|
||||
switch (data[0]) {
|
||||
case 0x10:
|
||||
if (size != 6)
|
||||
case TRACKPAD_REPORT_ID:
|
||||
/* Expect four bytes of prefix, and N*9 bytes of touch data. */
|
||||
if (size < 4 || ((size - 4) % 9) != 0)
|
||||
return 0;
|
||||
x = (__s16)(data[2] | data[3] << 8);
|
||||
y = (__s16)(data[4] | data[5] << 8);
|
||||
npoints = (size - 4) / 9;
|
||||
msc->ntouches = 0;
|
||||
for (ii = 0; ii < npoints; ii++)
|
||||
magicmouse_emit_touch(msc, ii, data + ii * 9 + 4);
|
||||
|
||||
/* We don't need an MT sync here because trackpad emits a
|
||||
* BTN_TOUCH event in a new frame when all touches are released.
|
||||
*/
|
||||
if (msc->ntouches == 0)
|
||||
msc->single_touch_id = NO_TOUCHES;
|
||||
|
||||
clicks = data[1];
|
||||
|
||||
/* The following bits provide a device specific timestamp. They
|
||||
* are unused here.
|
||||
*
|
||||
* ts = data[1] >> 6 | data[2] << 2 | data[3] << 10;
|
||||
*/
|
||||
break;
|
||||
case TOUCH_REPORT_ID:
|
||||
case MOUSE_REPORT_ID:
|
||||
/* Expect six bytes of prefix, and N*8 bytes of touch data. */
|
||||
if (size < 6 || ((size - 6) % 8) != 0)
|
||||
return 0;
|
||||
ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
|
||||
msc->delta_time = (ts - msc->last_timestamp) & 0x3ffff;
|
||||
msc->last_timestamp = ts;
|
||||
msc->ntouches = (size - 6) / 8;
|
||||
for (ii = 0; ii < msc->ntouches; ii++)
|
||||
npoints = (size - 6) / 8;
|
||||
msc->ntouches = 0;
|
||||
for (ii = 0; ii < npoints; ii++)
|
||||
magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);
|
||||
|
||||
if (report_touches) {
|
||||
last_up = 1;
|
||||
for (ii = 0; ii < ARRAY_SIZE(msc->touches); ii++) {
|
||||
if (msc->touches[ii].down) {
|
||||
last_up = 0;
|
||||
msc->touches[ii].down = 0;
|
||||
}
|
||||
}
|
||||
if (last_up) {
|
||||
input_mt_sync(input);
|
||||
}
|
||||
}
|
||||
if (report_touches && msc->ntouches == 0)
|
||||
input_mt_sync(input);
|
||||
|
||||
/* When emulating three-button mode, it is important
|
||||
* to have the current touch information before
|
||||
@ -288,68 +321,72 @@ static int magicmouse_raw_event(struct hid_device *hdev,
|
||||
x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22;
|
||||
y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22;
|
||||
clicks = data[3];
|
||||
|
||||
/* The following bits provide a device specific timestamp. They
|
||||
* are unused here.
|
||||
*
|
||||
* ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
|
||||
*/
|
||||
break;
|
||||
case DOUBLE_REPORT_ID:
|
||||
/* Sometimes the trackpad sends two touch reports in one
|
||||
* packet.
|
||||
*/
|
||||
magicmouse_raw_event(hdev, report, data + 2, data[1]);
|
||||
magicmouse_raw_event(hdev, report, data + 2 + data[1],
|
||||
size - 2 - data[1]);
|
||||
break;
|
||||
case 0x20: /* Theoretically battery status (0-100), but I have
|
||||
* never seen it -- maybe it is only upon request.
|
||||
*/
|
||||
case 0x60: /* Unknown, maybe laser on/off. */
|
||||
case 0x61: /* Laser reflection status change.
|
||||
* data[1]: 0 = spotted, 1 = lost
|
||||
*/
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
magicmouse_emit_buttons(msc, clicks & 3);
|
||||
input_report_rel(input, REL_X, x);
|
||||
input_report_rel(input, REL_Y, y);
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
||||
magicmouse_emit_buttons(msc, clicks & 3);
|
||||
input_report_rel(input, REL_X, x);
|
||||
input_report_rel(input, REL_Y, y);
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
input_report_key(input, BTN_MOUSE, clicks & 1);
|
||||
input_report_key(input, BTN_TOUCH, msc->ntouches > 0);
|
||||
input_report_key(input, BTN_TOOL_FINGER, msc->ntouches == 1);
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP, msc->ntouches == 2);
|
||||
input_report_key(input, BTN_TOOL_TRIPLETAP, msc->ntouches == 3);
|
||||
input_report_key(input, BTN_TOOL_QUADTAP, msc->ntouches == 4);
|
||||
if (msc->single_touch_id >= 0) {
|
||||
input_report_abs(input, ABS_X,
|
||||
msc->touches[msc->single_touch_id].x);
|
||||
input_report_abs(input, ABS_Y,
|
||||
msc->touches[msc->single_touch_id].y);
|
||||
}
|
||||
}
|
||||
|
||||
input_sync(input);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int magicmouse_input_open(struct input_dev *dev)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
|
||||
return hid->ll_driver->open(hid);
|
||||
}
|
||||
|
||||
static void magicmouse_input_close(struct input_dev *dev)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
|
||||
hid->ll_driver->close(hid);
|
||||
}
|
||||
|
||||
static void magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev)
|
||||
{
|
||||
input_set_drvdata(input, hdev);
|
||||
input->event = hdev->ll_driver->hidinput_input_event;
|
||||
input->open = magicmouse_input_open;
|
||||
input->close = magicmouse_input_close;
|
||||
|
||||
input->name = hdev->name;
|
||||
input->phys = hdev->phys;
|
||||
input->uniq = hdev->uniq;
|
||||
input->id.bustype = hdev->bus;
|
||||
input->id.vendor = hdev->vendor;
|
||||
input->id.product = hdev->product;
|
||||
input->id.version = hdev->version;
|
||||
input->dev.parent = hdev->dev.parent;
|
||||
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(BTN_LEFT, input->keybit);
|
||||
__set_bit(BTN_RIGHT, input->keybit);
|
||||
if (emulate_3button)
|
||||
__set_bit(BTN_MIDDLE, input->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, input->keybit);
|
||||
|
||||
__set_bit(EV_REL, input->evbit);
|
||||
__set_bit(REL_X, input->relbit);
|
||||
__set_bit(REL_Y, input->relbit);
|
||||
if (emulate_scroll_wheel) {
|
||||
__set_bit(REL_WHEEL, input->relbit);
|
||||
__set_bit(REL_HWHEEL, input->relbit);
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
||||
__set_bit(BTN_LEFT, input->keybit);
|
||||
__set_bit(BTN_RIGHT, input->keybit);
|
||||
if (emulate_3button)
|
||||
__set_bit(BTN_MIDDLE, input->keybit);
|
||||
|
||||
__set_bit(EV_REL, input->evbit);
|
||||
__set_bit(REL_X, input->relbit);
|
||||
__set_bit(REL_Y, input->relbit);
|
||||
if (emulate_scroll_wheel) {
|
||||
__set_bit(REL_WHEEL, input->relbit);
|
||||
__set_bit(REL_HWHEEL, input->relbit);
|
||||
}
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
__set_bit(BTN_MOUSE, input->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, input->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
|
||||
__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
|
||||
__set_bit(BTN_TOOL_QUADTAP, input->keybit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
}
|
||||
|
||||
if (report_touches) {
|
||||
@ -359,16 +396,26 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, -1100, 1358,
|
||||
4, 0);
|
||||
|
||||
/* Note: Touch Y position from the device is inverted relative
|
||||
* to how pointer motion is reported (and relative to how USB
|
||||
* HID recommends the coordinates work). This driver keeps
|
||||
* the origin at the same position, and just uses the additive
|
||||
* inverse of the reported Y.
|
||||
*/
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, 2047,
|
||||
4, 0);
|
||||
if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, -1100,
|
||||
1358, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, -1589,
|
||||
2047, 4, 0);
|
||||
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
input_set_abs_params(input, ABS_X, -2909, 3167, 4, 0);
|
||||
input_set_abs_params(input, ABS_Y, -2456, 2565, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, -2909,
|
||||
3167, 4, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, -2456,
|
||||
2565, 4, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (report_undeciphered) {
|
||||
@ -377,12 +424,22 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
|
||||
}
|
||||
}
|
||||
|
||||
static int magicmouse_input_mapping(struct hid_device *hdev,
|
||||
struct hid_input *hi, struct hid_field *field,
|
||||
struct hid_usage *usage, unsigned long **bit, int *max)
|
||||
{
|
||||
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
|
||||
|
||||
if (!msc->input)
|
||||
msc->input = hi->input;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int magicmouse_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
__u8 feature_1[] = { 0xd7, 0x01 };
|
||||
__u8 feature_2[] = { 0xf8, 0x01, 0x32 };
|
||||
struct input_dev *input;
|
||||
__u8 feature[] = { 0xd7, 0x01 };
|
||||
struct magicmouse_sc *msc;
|
||||
struct hid_report *report;
|
||||
int ret;
|
||||
@ -398,6 +455,8 @@ static int magicmouse_probe(struct hid_device *hdev,
|
||||
msc->quirks = id->driver_data;
|
||||
hid_set_drvdata(hdev, msc);
|
||||
|
||||
msc->single_touch_id = NO_TOUCHES;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
dev_err(&hdev->dev, "magicmouse hid parse failed\n");
|
||||
@ -410,10 +469,22 @@ static int magicmouse_probe(struct hid_device *hdev,
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/* we are handling the input ourselves */
|
||||
hidinput_disconnect(hdev);
|
||||
/* We do this after hid-input is done parsing reports so that
|
||||
* hid-input uses the most natural button and axis IDs.
|
||||
*/
|
||||
if (msc->input)
|
||||
magicmouse_setup_input(msc->input, hdev);
|
||||
|
||||
if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
|
||||
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
||||
MOUSE_REPORT_ID);
|
||||
else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
||||
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
||||
TRACKPAD_REPORT_ID);
|
||||
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
||||
DOUBLE_REPORT_ID);
|
||||
}
|
||||
|
||||
report = hid_register_report(hdev, HID_INPUT_REPORT, TOUCH_REPORT_ID);
|
||||
if (!report) {
|
||||
dev_err(&hdev->dev, "unable to register touch report\n");
|
||||
ret = -ENOMEM;
|
||||
@ -421,39 +492,15 @@ static int magicmouse_probe(struct hid_device *hdev,
|
||||
}
|
||||
report->size = 6;
|
||||
|
||||
ret = hdev->hid_output_raw_report(hdev, feature_1, sizeof(feature_1),
|
||||
ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature),
|
||||
HID_FEATURE_REPORT);
|
||||
if (ret != sizeof(feature_1)) {
|
||||
dev_err(&hdev->dev, "unable to request touch data (1:%d)\n",
|
||||
if (ret != sizeof(feature)) {
|
||||
dev_err(&hdev->dev, "unable to request touch data (%d)\n",
|
||||
ret);
|
||||
goto err_stop_hw;
|
||||
}
|
||||
ret = hdev->hid_output_raw_report(hdev, feature_2,
|
||||
sizeof(feature_2), HID_FEATURE_REPORT);
|
||||
if (ret != sizeof(feature_2)) {
|
||||
dev_err(&hdev->dev, "unable to request touch data (2:%d)\n",
|
||||
ret);
|
||||
goto err_stop_hw;
|
||||
}
|
||||
|
||||
input = input_allocate_device();
|
||||
if (!input) {
|
||||
dev_err(&hdev->dev, "can't alloc input device\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_stop_hw;
|
||||
}
|
||||
magicmouse_setup_input(input, hdev);
|
||||
|
||||
ret = input_register_device(input);
|
||||
if (ret) {
|
||||
dev_err(&hdev->dev, "input device registration failed\n");
|
||||
goto err_input;
|
||||
}
|
||||
msc->input = input;
|
||||
|
||||
return 0;
|
||||
err_input:
|
||||
input_free_device(input);
|
||||
err_stop_hw:
|
||||
hid_hw_stop(hdev);
|
||||
err_free:
|
||||
@ -466,13 +513,14 @@ static void magicmouse_remove(struct hid_device *hdev)
|
||||
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
|
||||
|
||||
hid_hw_stop(hdev);
|
||||
input_unregister_device(msc->input);
|
||||
kfree(msc);
|
||||
}
|
||||
|
||||
static const struct hid_device_id magic_mice[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE),
|
||||
.driver_data = 0 },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
|
||||
USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
|
||||
USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, magic_mice);
|
||||
@ -483,6 +531,7 @@ static struct hid_driver magicmouse_driver = {
|
||||
.probe = magicmouse_probe,
|
||||
.remove = magicmouse_remove,
|
||||
.raw_event = magicmouse_raw_event,
|
||||
.input_mapping = magicmouse_input_mapping,
|
||||
};
|
||||
|
||||
static int __init magicmouse_init(void)
|
||||
|
@ -33,18 +33,19 @@
|
||||
* Microsoft Wireless Desktop Receiver (Model 1028) has
|
||||
* 'Usage Min/Max' where it ought to have 'Physical Min/Max'
|
||||
*/
|
||||
static void ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
||||
|
||||
if ((quirks & MS_RDESC) && rsize == 571 && rdesc[557] == 0x19 &&
|
||||
if ((quirks & MS_RDESC) && *rsize == 571 && rdesc[557] == 0x19 &&
|
||||
rdesc[559] == 0x29) {
|
||||
dev_info(&hdev->dev, "fixing up Microsoft Wireless Receiver "
|
||||
"Model 1028 report descriptor\n");
|
||||
rdesc[557] = 0x35;
|
||||
rdesc[559] = 0x45;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
#define ms_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
|
||||
|
@ -22,14 +22,15 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static void mr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *mr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (rsize >= 30 && rdesc[29] == 0x05 && rdesc[30] == 0x09) {
|
||||
if (*rsize >= 30 && rdesc[29] == 0x05 && rdesc[30] == 0x09) {
|
||||
dev_info(&hdev->dev, "fixing up button/consumer in HID report "
|
||||
"descriptor\n");
|
||||
rdesc[30] = 0x0c;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
#define mr_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
|
||||
|
@ -90,6 +90,55 @@ struct ntrig_data {
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* This function converts the 4 byte raw firmware code into
|
||||
* a string containing 5 comma separated numbers.
|
||||
*/
|
||||
static int ntrig_version_string(unsigned char *raw, char *buf)
|
||||
{
|
||||
__u8 a = (raw[1] & 0x0e) >> 1;
|
||||
__u8 b = (raw[0] & 0x3c) >> 2;
|
||||
__u8 c = ((raw[0] & 0x03) << 3) | ((raw[3] & 0xe0) >> 5);
|
||||
__u8 d = ((raw[3] & 0x07) << 3) | ((raw[2] & 0xe0) >> 5);
|
||||
__u8 e = raw[2] & 0x07;
|
||||
|
||||
/*
|
||||
* As yet unmapped bits:
|
||||
* 0b11000000 0b11110001 0b00011000 0b00011000
|
||||
*/
|
||||
|
||||
return sprintf(buf, "%u.%u.%u.%u.%u", a, b, c, d, e);
|
||||
}
|
||||
|
||||
static void ntrig_report_version(struct hid_device *hdev)
|
||||
{
|
||||
int ret;
|
||||
char buf[20];
|
||||
struct usb_device *usb_dev = hid_to_usb_dev(hdev);
|
||||
unsigned char *data = kmalloc(8, GFP_KERNEL);
|
||||
|
||||
if (!data)
|
||||
goto err_free;
|
||||
|
||||
ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE |
|
||||
USB_DIR_IN,
|
||||
0x30c, 1, data, 8,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (ret == 8) {
|
||||
ret = ntrig_version_string(&data[2], buf);
|
||||
|
||||
dev_info(&hdev->dev,
|
||||
"Firmware version: %s (%02x%02x %02x%02x)\n",
|
||||
buf, data[2], data[3], data[4], data[5]);
|
||||
}
|
||||
|
||||
err_free:
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
static ssize_t show_phys_width(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
@ -377,8 +426,8 @@ static struct attribute_group ntrig_attribute_group = {
|
||||
*/
|
||||
|
||||
static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
struct ntrig_data *nd = hid_get_drvdata(hdev);
|
||||
|
||||
@ -448,13 +497,13 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
/* width/height mapped on TouchMajor/TouchMinor/Orientation */
|
||||
case HID_DG_WIDTH:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_TOUCH_MAJOR);
|
||||
EV_ABS, ABS_MT_TOUCH_MAJOR);
|
||||
return 1;
|
||||
case HID_DG_HEIGHT:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_TOUCH_MINOR);
|
||||
EV_ABS, ABS_MT_TOUCH_MINOR);
|
||||
input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
|
||||
0, 1, 0, 0);
|
||||
0, 1, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -468,8 +517,8 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
}
|
||||
|
||||
static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
/* No special mappings needed for the pen and single touch */
|
||||
if (field->physical)
|
||||
@ -489,7 +538,7 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
* and call input_mt_sync after each point if necessary
|
||||
*/
|
||||
static int ntrig_event (struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct input_dev *input = field->hidinput->input;
|
||||
struct ntrig_data *nd = hid_get_drvdata(hid);
|
||||
@ -848,6 +897,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
if (report)
|
||||
usbhid_submit_report(hdev, report, USB_DIR_OUT);
|
||||
|
||||
ntrig_report_version(hdev);
|
||||
|
||||
ret = sysfs_create_group(&hdev->dev.kobj,
|
||||
&ntrig_attribute_group);
|
||||
|
||||
@ -860,7 +911,7 @@ err_free:
|
||||
static void ntrig_remove(struct hid_device *hdev)
|
||||
{
|
||||
sysfs_remove_group(&hdev->dev.kobj,
|
||||
&ntrig_attribute_group);
|
||||
&ntrig_attribute_group);
|
||||
hid_hw_stop(hdev);
|
||||
kfree(hid_get_drvdata(hdev));
|
||||
}
|
||||
|
@ -19,14 +19,15 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static void ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x01) {
|
||||
if (*rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x01) {
|
||||
dev_info(&hdev->dev, "Fixing up Ortek WKB-2000 "
|
||||
"report descriptor.\n");
|
||||
rdesc[55] = 0x92;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static const struct hid_device_id ortek_devices[] = {
|
||||
|
@ -23,10 +23,10 @@
|
||||
#include "hid-ids.h"
|
||||
|
||||
/* Petalynx Maxter Remote has maximum for consumer page set too low */
|
||||
static void pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (rsize >= 60 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 &&
|
||||
if (*rsize >= 60 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 &&
|
||||
rdesc[41] == 0x00 && rdesc[59] == 0x26 &&
|
||||
rdesc[60] == 0xf9 && rdesc[61] == 0x00) {
|
||||
dev_info(&hdev->dev, "fixing up Petalynx Maxter Remote report "
|
||||
@ -34,6 +34,7 @@ static void pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
rdesc[60] = 0xfa;
|
||||
rdesc[40] = 0xfa;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
#define pl_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
|
||||
|
@ -740,10 +740,10 @@ int pcmidi_snd_terminate(struct pcmidi_snd *pm)
|
||||
/*
|
||||
* PC-MIDI report descriptor for report id is wrong.
|
||||
*/
|
||||
static void pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (rsize == 178 &&
|
||||
if (*rsize == 178 &&
|
||||
rdesc[111] == 0x06 && rdesc[112] == 0x00 &&
|
||||
rdesc[113] == 0xff) {
|
||||
dev_info(&hdev->dev, "fixing up pc-midi keyboard report "
|
||||
@ -751,6 +751,7 @@ static void pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
|
||||
rdesc[144] = 0x18; /* report 4: was 0x10 report count */
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
|
968
drivers/hid/hid-roccat-pyra.c
Normal file
968
drivers/hid/hid-roccat-pyra.c
Normal file
@ -0,0 +1,968 @@
|
||||
/*
|
||||
* Roccat Pyra driver for Linux
|
||||
*
|
||||
* Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Roccat Pyra is a mobile gamer mouse which comes in wired and wireless
|
||||
* variant. Wireless variant is not tested.
|
||||
* Userland tools can be found at http://sourceforge.net/projects/roccat
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include "hid-ids.h"
|
||||
#include "hid-roccat.h"
|
||||
#include "hid-roccat-pyra.h"
|
||||
|
||||
static void profile_activated(struct pyra_device *pyra,
|
||||
unsigned int new_profile)
|
||||
{
|
||||
pyra->actual_profile = new_profile;
|
||||
pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi;
|
||||
}
|
||||
|
||||
static int pyra_send_control(struct usb_device *usb_dev, int value,
|
||||
enum pyra_control_requests request)
|
||||
{
|
||||
int len;
|
||||
struct pyra_control control;
|
||||
|
||||
if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
|
||||
request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
|
||||
(value < 0 || value > 4))
|
||||
return -EINVAL;
|
||||
|
||||
control.command = PYRA_COMMAND_CONTROL;
|
||||
control.value = value;
|
||||
control.request = request;
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
PYRA_USB_COMMAND_CONTROL, 0, (char *)&control,
|
||||
sizeof(struct pyra_control),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (len != sizeof(struct pyra_control))
|
||||
return len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pyra_receive_control_status(struct usb_device *usb_dev)
|
||||
{
|
||||
int len;
|
||||
struct pyra_control control;
|
||||
|
||||
do {
|
||||
msleep(10);
|
||||
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE |
|
||||
USB_DIR_IN,
|
||||
PYRA_USB_COMMAND_CONTROL, 0, (char *)&control,
|
||||
sizeof(struct pyra_control),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
/* requested too early, try again */
|
||||
} while (len == -EPROTO);
|
||||
|
||||
if (len == sizeof(struct pyra_control) &&
|
||||
control.command == PYRA_COMMAND_CONTROL &&
|
||||
control.request == PYRA_CONTROL_REQUEST_STATUS &&
|
||||
control.value == 1)
|
||||
return 0;
|
||||
else {
|
||||
dev_err(&usb_dev->dev, "receive control status: "
|
||||
"unknown response 0x%x 0x%x\n",
|
||||
control.request, control.value);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int pyra_get_profile_settings(struct usb_device *usb_dev,
|
||||
struct pyra_profile_settings *buf, int number)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = pyra_send_control(usb_dev, number,
|
||||
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)buf,
|
||||
sizeof(struct pyra_profile_settings),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (retval != sizeof(struct pyra_profile_settings))
|
||||
return retval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pyra_get_profile_buttons(struct usb_device *usb_dev,
|
||||
struct pyra_profile_buttons *buf, int number)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = pyra_send_control(usb_dev, number,
|
||||
PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buf,
|
||||
sizeof(struct pyra_profile_buttons),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
if (retval != sizeof(struct pyra_profile_buttons))
|
||||
return retval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pyra_get_settings(struct usb_device *usb_dev,
|
||||
struct pyra_settings *buf)
|
||||
{
|
||||
int len;
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
PYRA_USB_COMMAND_SETTINGS, 0, buf,
|
||||
sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT);
|
||||
if (len != sizeof(struct pyra_settings))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf)
|
||||
{
|
||||
int len;
|
||||
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
PYRA_USB_COMMAND_INFO, 0, buf,
|
||||
sizeof(struct pyra_info), USB_CTRL_SET_TIMEOUT);
|
||||
if (len != sizeof(struct pyra_info))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pyra_set_profile_settings(struct usb_device *usb_dev,
|
||||
struct pyra_profile_settings const *settings)
|
||||
{
|
||||
int len;
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)settings,
|
||||
sizeof(struct pyra_profile_settings),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (len != sizeof(struct pyra_profile_settings))
|
||||
return -EIO;
|
||||
if (pyra_receive_control_status(usb_dev))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pyra_set_profile_buttons(struct usb_device *usb_dev,
|
||||
struct pyra_profile_buttons const *buttons)
|
||||
{
|
||||
int len;
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buttons,
|
||||
sizeof(struct pyra_profile_buttons),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (len != sizeof(struct pyra_profile_buttons))
|
||||
return -EIO;
|
||||
if (pyra_receive_control_status(usb_dev))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pyra_set_settings(struct usb_device *usb_dev,
|
||||
struct pyra_settings const *settings)
|
||||
{
|
||||
int len;
|
||||
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
USB_REQ_SET_CONFIGURATION,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
PYRA_USB_COMMAND_SETTINGS, 0, (char *)settings,
|
||||
sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT);
|
||||
if (len != sizeof(struct pyra_settings))
|
||||
return -EIO;
|
||||
if (pyra_receive_control_status(usb_dev))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count, int number)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
|
||||
if (off >= sizeof(struct pyra_profile_settings))
|
||||
return 0;
|
||||
|
||||
if (off + count > sizeof(struct pyra_profile_settings))
|
||||
count = sizeof(struct pyra_profile_settings) - off;
|
||||
|
||||
mutex_lock(&pyra->pyra_lock);
|
||||
memcpy(buf, ((char const *)&pyra->profile_settings[number]) + off,
|
||||
count);
|
||||
mutex_unlock(&pyra->pyra_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profile1_settings(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pyra_sysfs_read_profilex_settings(fp, kobj,
|
||||
attr, buf, off, count, 0);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profile2_settings(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pyra_sysfs_read_profilex_settings(fp, kobj,
|
||||
attr, buf, off, count, 1);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profile3_settings(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pyra_sysfs_read_profilex_settings(fp, kobj,
|
||||
attr, buf, off, count, 2);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profile4_settings(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pyra_sysfs_read_profilex_settings(fp, kobj,
|
||||
attr, buf, off, count, 3);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profile5_settings(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pyra_sysfs_read_profilex_settings(fp, kobj,
|
||||
attr, buf, off, count, 4);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count, int number)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
|
||||
if (off >= sizeof(struct pyra_profile_buttons))
|
||||
return 0;
|
||||
|
||||
if (off + count > sizeof(struct pyra_profile_buttons))
|
||||
count = sizeof(struct pyra_profile_buttons) - off;
|
||||
|
||||
mutex_lock(&pyra->pyra_lock);
|
||||
memcpy(buf, ((char const *)&pyra->profile_buttons[number]) + off,
|
||||
count);
|
||||
mutex_unlock(&pyra->pyra_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profile1_buttons(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pyra_sysfs_read_profilex_buttons(fp, kobj,
|
||||
attr, buf, off, count, 0);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profile2_buttons(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pyra_sysfs_read_profilex_buttons(fp, kobj,
|
||||
attr, buf, off, count, 1);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profile3_buttons(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pyra_sysfs_read_profilex_buttons(fp, kobj,
|
||||
attr, buf, off, count, 2);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profile4_buttons(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pyra_sysfs_read_profilex_buttons(fp, kobj,
|
||||
attr, buf, off, count, 3);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_profile5_buttons(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
return pyra_sysfs_read_profilex_buttons(fp, kobj,
|
||||
attr, buf, off, count, 4);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_write_profile_settings(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
|
||||
int retval = 0;
|
||||
int difference;
|
||||
int profile_number;
|
||||
struct pyra_profile_settings *profile_settings;
|
||||
|
||||
if (off != 0 || count != sizeof(struct pyra_profile_settings))
|
||||
return -EINVAL;
|
||||
|
||||
profile_number = ((struct pyra_profile_settings const *)buf)->number;
|
||||
profile_settings = &pyra->profile_settings[profile_number];
|
||||
|
||||
mutex_lock(&pyra->pyra_lock);
|
||||
difference = memcmp(buf, profile_settings,
|
||||
sizeof(struct pyra_profile_settings));
|
||||
if (difference) {
|
||||
retval = pyra_set_profile_settings(usb_dev,
|
||||
(struct pyra_profile_settings const *)buf);
|
||||
if (!retval)
|
||||
memcpy(profile_settings, buf,
|
||||
sizeof(struct pyra_profile_settings));
|
||||
}
|
||||
mutex_unlock(&pyra->pyra_lock);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return sizeof(struct pyra_profile_settings);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
|
||||
int retval = 0;
|
||||
int difference;
|
||||
int profile_number;
|
||||
struct pyra_profile_buttons *profile_buttons;
|
||||
|
||||
if (off != 0 || count != sizeof(struct pyra_profile_buttons))
|
||||
return -EINVAL;
|
||||
|
||||
profile_number = ((struct pyra_profile_buttons const *)buf)->number;
|
||||
profile_buttons = &pyra->profile_buttons[profile_number];
|
||||
|
||||
mutex_lock(&pyra->pyra_lock);
|
||||
difference = memcmp(buf, profile_buttons,
|
||||
sizeof(struct pyra_profile_buttons));
|
||||
if (difference) {
|
||||
retval = pyra_set_profile_buttons(usb_dev,
|
||||
(struct pyra_profile_buttons const *)buf);
|
||||
if (!retval)
|
||||
memcpy(profile_buttons, buf,
|
||||
sizeof(struct pyra_profile_buttons));
|
||||
}
|
||||
mutex_unlock(&pyra->pyra_lock);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return sizeof(struct pyra_profile_buttons);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_read_settings(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
|
||||
if (off >= sizeof(struct pyra_settings))
|
||||
return 0;
|
||||
|
||||
if (off + count > sizeof(struct pyra_settings))
|
||||
count = sizeof(struct pyra_settings) - off;
|
||||
|
||||
mutex_lock(&pyra->pyra_lock);
|
||||
memcpy(buf, ((char const *)&pyra->settings) + off, count);
|
||||
mutex_unlock(&pyra->pyra_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_write_settings(struct file *fp,
|
||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
|
||||
int retval = 0;
|
||||
int difference;
|
||||
|
||||
if (off != 0 || count != sizeof(struct pyra_settings))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pyra->pyra_lock);
|
||||
difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings));
|
||||
if (difference) {
|
||||
retval = pyra_set_settings(usb_dev,
|
||||
(struct pyra_settings const *)buf);
|
||||
if (!retval)
|
||||
memcpy(&pyra->settings, buf,
|
||||
sizeof(struct pyra_settings));
|
||||
}
|
||||
mutex_unlock(&pyra->pyra_lock);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
profile_activated(pyra, pyra->settings.startup_profile);
|
||||
|
||||
return sizeof(struct pyra_settings);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version);
|
||||
}
|
||||
|
||||
static ssize_t pyra_sysfs_show_startup_profile(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL);
|
||||
|
||||
static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL);
|
||||
|
||||
static DEVICE_ATTR(firmware_version, 0440,
|
||||
pyra_sysfs_show_firmware_version, NULL);
|
||||
|
||||
static DEVICE_ATTR(startup_profile, 0440,
|
||||
pyra_sysfs_show_startup_profile, NULL);
|
||||
|
||||
static struct attribute *pyra_attributes[] = {
|
||||
&dev_attr_actual_cpi.attr,
|
||||
&dev_attr_actual_profile.attr,
|
||||
&dev_attr_firmware_version.attr,
|
||||
&dev_attr_startup_profile.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group pyra_attribute_group = {
|
||||
.attrs = pyra_attributes
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile_settings_attr = {
|
||||
.attr = { .name = "profile_settings", .mode = 0220 },
|
||||
.size = sizeof(struct pyra_profile_settings),
|
||||
.write = pyra_sysfs_write_profile_settings
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile1_settings_attr = {
|
||||
.attr = { .name = "profile1_settings", .mode = 0440 },
|
||||
.size = sizeof(struct pyra_profile_settings),
|
||||
.read = pyra_sysfs_read_profile1_settings
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile2_settings_attr = {
|
||||
.attr = { .name = "profile2_settings", .mode = 0440 },
|
||||
.size = sizeof(struct pyra_profile_settings),
|
||||
.read = pyra_sysfs_read_profile2_settings
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile3_settings_attr = {
|
||||
.attr = { .name = "profile3_settings", .mode = 0440 },
|
||||
.size = sizeof(struct pyra_profile_settings),
|
||||
.read = pyra_sysfs_read_profile3_settings
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile4_settings_attr = {
|
||||
.attr = { .name = "profile4_settings", .mode = 0440 },
|
||||
.size = sizeof(struct pyra_profile_settings),
|
||||
.read = pyra_sysfs_read_profile4_settings
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile5_settings_attr = {
|
||||
.attr = { .name = "profile5_settings", .mode = 0440 },
|
||||
.size = sizeof(struct pyra_profile_settings),
|
||||
.read = pyra_sysfs_read_profile5_settings
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile_buttons_attr = {
|
||||
.attr = { .name = "profile_buttons", .mode = 0220 },
|
||||
.size = sizeof(struct pyra_profile_buttons),
|
||||
.write = pyra_sysfs_write_profile_buttons
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile1_buttons_attr = {
|
||||
.attr = { .name = "profile1_buttons", .mode = 0440 },
|
||||
.size = sizeof(struct pyra_profile_buttons),
|
||||
.read = pyra_sysfs_read_profile1_buttons
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile2_buttons_attr = {
|
||||
.attr = { .name = "profile2_buttons", .mode = 0440 },
|
||||
.size = sizeof(struct pyra_profile_buttons),
|
||||
.read = pyra_sysfs_read_profile2_buttons
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile3_buttons_attr = {
|
||||
.attr = { .name = "profile3_buttons", .mode = 0440 },
|
||||
.size = sizeof(struct pyra_profile_buttons),
|
||||
.read = pyra_sysfs_read_profile3_buttons
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile4_buttons_attr = {
|
||||
.attr = { .name = "profile4_buttons", .mode = 0440 },
|
||||
.size = sizeof(struct pyra_profile_buttons),
|
||||
.read = pyra_sysfs_read_profile4_buttons
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_profile5_buttons_attr = {
|
||||
.attr = { .name = "profile5_buttons", .mode = 0440 },
|
||||
.size = sizeof(struct pyra_profile_buttons),
|
||||
.read = pyra_sysfs_read_profile5_buttons
|
||||
};
|
||||
|
||||
static struct bin_attribute pyra_settings_attr = {
|
||||
.attr = { .name = "settings", .mode = 0660 },
|
||||
.size = sizeof(struct pyra_settings),
|
||||
.read = pyra_sysfs_read_settings,
|
||||
.write = pyra_sysfs_write_settings
|
||||
};
|
||||
|
||||
static int pyra_create_sysfs_attributes(struct usb_interface *intf)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = sysfs_create_group(&intf->dev.kobj, &pyra_attribute_group);
|
||||
if (retval)
|
||||
goto exit_1;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile_settings_attr);
|
||||
if (retval)
|
||||
goto exit_2;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile1_settings_attr);
|
||||
if (retval)
|
||||
goto exit_3;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile2_settings_attr);
|
||||
if (retval)
|
||||
goto exit_4;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile3_settings_attr);
|
||||
if (retval)
|
||||
goto exit_5;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile4_settings_attr);
|
||||
if (retval)
|
||||
goto exit_6;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile5_settings_attr);
|
||||
if (retval)
|
||||
goto exit_7;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile_buttons_attr);
|
||||
if (retval)
|
||||
goto exit_8;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile1_buttons_attr);
|
||||
if (retval)
|
||||
goto exit_9;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile2_buttons_attr);
|
||||
if (retval)
|
||||
goto exit_10;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile3_buttons_attr);
|
||||
if (retval)
|
||||
goto exit_11;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile4_buttons_attr);
|
||||
if (retval)
|
||||
goto exit_12;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_profile5_buttons_attr);
|
||||
if (retval)
|
||||
goto exit_13;
|
||||
|
||||
retval = sysfs_create_bin_file(&intf->dev.kobj,
|
||||
&pyra_settings_attr);
|
||||
if (retval)
|
||||
goto exit_14;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_14:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr);
|
||||
exit_13:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr);
|
||||
exit_12:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr);
|
||||
exit_11:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr);
|
||||
exit_10:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr);
|
||||
exit_9:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr);
|
||||
exit_8:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr);
|
||||
exit_7:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr);
|
||||
exit_6:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr);
|
||||
exit_5:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr);
|
||||
exit_4:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr);
|
||||
exit_3:
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr);
|
||||
exit_2:
|
||||
sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group);
|
||||
exit_1:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void pyra_remove_sysfs_attributes(struct usb_interface *intf)
|
||||
{
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_settings_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr);
|
||||
sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr);
|
||||
sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group);
|
||||
}
|
||||
|
||||
static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
|
||||
struct pyra_device *pyra)
|
||||
{
|
||||
struct pyra_info *info;
|
||||
int retval, i;
|
||||
|
||||
mutex_init(&pyra->pyra_lock);
|
||||
|
||||
info = kmalloc(sizeof(struct pyra_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
retval = pyra_get_info(usb_dev, info);
|
||||
if (retval) {
|
||||
kfree(info);
|
||||
return retval;
|
||||
}
|
||||
pyra->firmware_version = info->firmware_version;
|
||||
kfree(info);
|
||||
|
||||
retval = pyra_get_settings(usb_dev, &pyra->settings);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
for (i = 0; i < 5; ++i) {
|
||||
retval = pyra_get_profile_settings(usb_dev,
|
||||
&pyra->profile_settings[i], i);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = pyra_get_profile_buttons(usb_dev,
|
||||
&pyra->profile_buttons[i], i);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
profile_activated(pyra, pyra->settings.startup_profile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pyra_init_specials(struct hid_device *hdev)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
struct pyra_device *pyra;
|
||||
int retval;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
== USB_INTERFACE_PROTOCOL_MOUSE) {
|
||||
|
||||
pyra = kzalloc(sizeof(*pyra), GFP_KERNEL);
|
||||
if (!pyra) {
|
||||
dev_err(&hdev->dev, "can't alloc device descriptor\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
hid_set_drvdata(hdev, pyra);
|
||||
|
||||
retval = pyra_init_pyra_device_struct(usb_dev, pyra);
|
||||
if (retval) {
|
||||
dev_err(&hdev->dev,
|
||||
"couldn't init struct pyra_device\n");
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
retval = roccat_connect(hdev);
|
||||
if (retval < 0) {
|
||||
dev_err(&hdev->dev, "couldn't init char dev\n");
|
||||
} else {
|
||||
pyra->chrdev_minor = retval;
|
||||
pyra->roccat_claimed = 1;
|
||||
}
|
||||
|
||||
retval = pyra_create_sysfs_attributes(intf);
|
||||
if (retval) {
|
||||
dev_err(&hdev->dev, "cannot create sysfs files\n");
|
||||
goto exit_free;
|
||||
}
|
||||
} else {
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit_free:
|
||||
kfree(pyra);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void pyra_remove_specials(struct hid_device *hdev)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
struct pyra_device *pyra;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
== USB_INTERFACE_PROTOCOL_MOUSE) {
|
||||
pyra_remove_sysfs_attributes(intf);
|
||||
pyra = hid_get_drvdata(hdev);
|
||||
if (pyra->roccat_claimed)
|
||||
roccat_disconnect(pyra->chrdev_minor);
|
||||
kfree(hid_get_drvdata(hdev));
|
||||
}
|
||||
}
|
||||
|
||||
static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = hid_parse(hdev);
|
||||
if (retval) {
|
||||
dev_err(&hdev->dev, "parse failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (retval) {
|
||||
dev_err(&hdev->dev, "hw start failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
retval = pyra_init_specials(hdev);
|
||||
if (retval) {
|
||||
dev_err(&hdev->dev, "couldn't install mouse\n");
|
||||
goto exit_stop;
|
||||
}
|
||||
return 0;
|
||||
|
||||
exit_stop:
|
||||
hid_hw_stop(hdev);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void pyra_remove(struct hid_device *hdev)
|
||||
{
|
||||
pyra_remove_specials(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static void pyra_keep_values_up_to_date(struct pyra_device *pyra,
|
||||
u8 const *data)
|
||||
{
|
||||
struct pyra_mouse_event_button const *button_event;
|
||||
|
||||
switch (data[0]) {
|
||||
case PYRA_MOUSE_REPORT_NUMBER_BUTTON:
|
||||
button_event = (struct pyra_mouse_event_button const *)data;
|
||||
switch (button_event->type) {
|
||||
case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
|
||||
profile_activated(pyra, button_event->data1 - 1);
|
||||
break;
|
||||
case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
|
||||
pyra->actual_cpi = button_event->data1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void pyra_report_to_chrdev(struct pyra_device const *pyra,
|
||||
u8 const *data)
|
||||
{
|
||||
struct pyra_roccat_report roccat_report;
|
||||
struct pyra_mouse_event_button const *button_event;
|
||||
|
||||
if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON)
|
||||
return;
|
||||
|
||||
button_event = (struct pyra_mouse_event_button const *)data;
|
||||
|
||||
switch (button_event->type) {
|
||||
case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
|
||||
case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
|
||||
roccat_report.type = button_event->type;
|
||||
roccat_report.value = button_event->data1;
|
||||
roccat_report.key = 0;
|
||||
roccat_report_event(pyra->chrdev_minor,
|
||||
(uint8_t const *)&roccat_report,
|
||||
sizeof(struct pyra_roccat_report));
|
||||
break;
|
||||
case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO:
|
||||
case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT:
|
||||
case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH:
|
||||
if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) {
|
||||
roccat_report.type = button_event->type;
|
||||
roccat_report.key = button_event->data1;
|
||||
/*
|
||||
* pyra reports profile numbers with range 1-5.
|
||||
* Keeping this behaviour.
|
||||
*/
|
||||
roccat_report.value = pyra->actual_profile + 1;
|
||||
roccat_report_event(pyra->chrdev_minor,
|
||||
(uint8_t const *)&roccat_report,
|
||||
sizeof(struct pyra_roccat_report));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
u8 *data, int size)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
struct pyra_device *pyra = hid_get_drvdata(hdev);
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceProtocol
|
||||
!= USB_INTERFACE_PROTOCOL_MOUSE)
|
||||
return 0;
|
||||
|
||||
pyra_keep_values_up_to_date(pyra, data);
|
||||
|
||||
if (pyra->roccat_claimed)
|
||||
pyra_report_to_chrdev(pyra, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hid_device_id pyra_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
|
||||
USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
|
||||
/* TODO add USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS after testing */
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(hid, pyra_devices);
|
||||
|
||||
static struct hid_driver pyra_driver = {
|
||||
.name = "pyra",
|
||||
.id_table = pyra_devices,
|
||||
.probe = pyra_probe,
|
||||
.remove = pyra_remove,
|
||||
.raw_event = pyra_raw_event
|
||||
};
|
||||
|
||||
static int __init pyra_init(void)
|
||||
{
|
||||
return hid_register_driver(&pyra_driver);
|
||||
}
|
||||
|
||||
static void __exit pyra_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&pyra_driver);
|
||||
}
|
||||
|
||||
module_init(pyra_init);
|
||||
module_exit(pyra_exit);
|
||||
|
||||
MODULE_AUTHOR("Stefan Achatz");
|
||||
MODULE_DESCRIPTION("USB Roccat Pyra driver");
|
||||
MODULE_LICENSE("GPL v2");
|
186
drivers/hid/hid-roccat-pyra.h
Normal file
186
drivers/hid/hid-roccat-pyra.h
Normal file
@ -0,0 +1,186 @@
|
||||
#ifndef __HID_ROCCAT_PYRA_H
|
||||
#define __HID_ROCCAT_PYRA_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
struct pyra_b {
|
||||
uint8_t command; /* PYRA_COMMAND_B */
|
||||
uint8_t size; /* always 3 */
|
||||
uint8_t unknown; /* 1 */
|
||||
};
|
||||
|
||||
struct pyra_control {
|
||||
uint8_t command; /* PYRA_COMMAND_CONTROL */
|
||||
/*
|
||||
* value is profile number for request_settings and request_buttons
|
||||
* 1 if status ok for request_status
|
||||
*/
|
||||
uint8_t value; /* Range 0-4 */
|
||||
uint8_t request;
|
||||
};
|
||||
|
||||
enum pyra_control_requests {
|
||||
PYRA_CONTROL_REQUEST_STATUS = 0x00,
|
||||
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
|
||||
PYRA_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20
|
||||
};
|
||||
|
||||
struct pyra_settings {
|
||||
uint8_t command; /* PYRA_COMMAND_SETTINGS */
|
||||
uint8_t size; /* always 3 */
|
||||
uint8_t startup_profile; /* Range 0-4! */
|
||||
};
|
||||
|
||||
struct pyra_profile_settings {
|
||||
uint8_t command; /* PYRA_COMMAND_PROFILE_SETTINGS */
|
||||
uint8_t size; /* always 0xd */
|
||||
uint8_t number; /* Range 0-4 */
|
||||
uint8_t xysync;
|
||||
uint8_t x_sensitivity; /* 0x1-0xa */
|
||||
uint8_t y_sensitivity;
|
||||
uint8_t x_cpi; /* unused */
|
||||
uint8_t y_cpi; /* this value is for x and y */
|
||||
uint8_t lightswitch; /* 0 = off, 1 = on */
|
||||
uint8_t light_effect;
|
||||
uint8_t handedness;
|
||||
uint16_t checksum; /* byte sum */
|
||||
};
|
||||
|
||||
struct pyra_profile_buttons {
|
||||
uint8_t command; /* PYRA_COMMAND_PROFILE_BUTTONS */
|
||||
uint8_t size; /* always 0x13 */
|
||||
uint8_t number; /* Range 0-4 */
|
||||
uint8_t buttons[14];
|
||||
uint16_t checksum; /* byte sum */
|
||||
};
|
||||
|
||||
struct pyra_info {
|
||||
uint8_t command; /* PYRA_COMMAND_INFO */
|
||||
uint8_t size; /* always 6 */
|
||||
uint8_t firmware_version;
|
||||
uint8_t unknown1; /* always 0 */
|
||||
uint8_t unknown2; /* always 1 */
|
||||
uint8_t unknown3; /* always 0 */
|
||||
};
|
||||
|
||||
enum pyra_commands {
|
||||
PYRA_COMMAND_CONTROL = 0x4,
|
||||
PYRA_COMMAND_SETTINGS = 0x5,
|
||||
PYRA_COMMAND_PROFILE_SETTINGS = 0x6,
|
||||
PYRA_COMMAND_PROFILE_BUTTONS = 0x7,
|
||||
PYRA_COMMAND_INFO = 0x9,
|
||||
PYRA_COMMAND_B = 0xb
|
||||
};
|
||||
|
||||
enum pyra_usb_commands {
|
||||
PYRA_USB_COMMAND_CONTROL = 0x304,
|
||||
PYRA_USB_COMMAND_SETTINGS = 0x305,
|
||||
PYRA_USB_COMMAND_PROFILE_SETTINGS = 0x306,
|
||||
PYRA_USB_COMMAND_PROFILE_BUTTONS = 0x307,
|
||||
PYRA_USB_COMMAND_INFO = 0x309,
|
||||
PYRA_USB_COMMAND_B = 0x30b /* writes 3 bytes */
|
||||
};
|
||||
|
||||
enum pyra_mouse_report_numbers {
|
||||
PYRA_MOUSE_REPORT_NUMBER_HID = 1,
|
||||
PYRA_MOUSE_REPORT_NUMBER_AUDIO = 2,
|
||||
PYRA_MOUSE_REPORT_NUMBER_BUTTON = 3,
|
||||
};
|
||||
|
||||
struct pyra_mouse_event_button {
|
||||
uint8_t report_number; /* always 3 */
|
||||
uint8_t unknown; /* always 0 */
|
||||
uint8_t type;
|
||||
uint8_t data1;
|
||||
uint8_t data2;
|
||||
};
|
||||
|
||||
struct pyra_mouse_event_audio {
|
||||
uint8_t report_number; /* always 2 */
|
||||
uint8_t type;
|
||||
uint8_t unused; /* always 0 */
|
||||
};
|
||||
|
||||
/* hid audio controls */
|
||||
enum pyra_mouse_event_audio_types {
|
||||
PYRA_MOUSE_EVENT_AUDIO_TYPE_MUTE = 0xe2,
|
||||
PYRA_MOUSE_EVENT_AUDIO_TYPE_VOLUME_UP = 0xe9,
|
||||
PYRA_MOUSE_EVENT_AUDIO_TYPE_VOLUME_DOWN = 0xea,
|
||||
};
|
||||
|
||||
enum pyra_mouse_event_button_types {
|
||||
/*
|
||||
* Mouse sends tilt events on report_number 1 and 3
|
||||
* Tilt events are sent repeatedly with 0.94s between first and second
|
||||
* event and 0.22s on subsequent
|
||||
*/
|
||||
PYRA_MOUSE_EVENT_BUTTON_TYPE_TILT = 0x10,
|
||||
|
||||
/*
|
||||
* These are sent sequentially
|
||||
* data1 contains new profile number in range 1-5
|
||||
*/
|
||||
PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_1 = 0x20,
|
||||
PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2 = 0x30,
|
||||
|
||||
/*
|
||||
* data1 = button_number (rmp index)
|
||||
* data2 = pressed/released
|
||||
*/
|
||||
PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO = 0x40,
|
||||
PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT = 0x50,
|
||||
|
||||
/*
|
||||
* data1 = button_number (rmp index)
|
||||
*/
|
||||
PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH = 0x60,
|
||||
|
||||
/* data1 = new cpi */
|
||||
PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI = 0xb0,
|
||||
|
||||
/* data1 and data2 = new sensitivity */
|
||||
PYRA_MOUSE_EVENT_BUTTON_TYPE_SENSITIVITY = 0xc0,
|
||||
|
||||
PYRA_MOUSE_EVENT_BUTTON_TYPE_MULTIMEDIA = 0xf0,
|
||||
};
|
||||
|
||||
enum {
|
||||
PYRA_MOUSE_EVENT_BUTTON_PRESS = 0,
|
||||
PYRA_MOUSE_EVENT_BUTTON_RELEASE = 1,
|
||||
};
|
||||
|
||||
struct pyra_roccat_report {
|
||||
uint8_t type;
|
||||
uint8_t value;
|
||||
uint8_t key;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
struct pyra_device {
|
||||
int actual_profile;
|
||||
int actual_cpi;
|
||||
int firmware_version;
|
||||
int roccat_claimed;
|
||||
int chrdev_minor;
|
||||
struct mutex pyra_lock;
|
||||
struct pyra_settings settings;
|
||||
struct pyra_profile_settings profile_settings[5];
|
||||
struct pyra_profile_buttons profile_buttons[5];
|
||||
};
|
||||
|
||||
#endif
|
@ -61,10 +61,10 @@ static inline void samsung_irda_dev_trace(struct hid_device *hdev,
|
||||
"descriptor\n", rsize);
|
||||
}
|
||||
|
||||
static void samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
|
||||
if (*rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
|
||||
rdesc[177] == 0x75 && rdesc[178] == 0x30 &&
|
||||
rdesc[179] == 0x95 && rdesc[180] == 0x01 &&
|
||||
rdesc[182] == 0x40) {
|
||||
@ -74,24 +74,25 @@ static void samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
rdesc[180] = 0x06;
|
||||
rdesc[182] = 0x42;
|
||||
} else
|
||||
if (rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 &&
|
||||
if (*rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 &&
|
||||
rdesc[194] == 0x25 && rdesc[195] == 0x12) {
|
||||
samsung_irda_dev_trace(hdev, 203);
|
||||
rdesc[193] = 0x1;
|
||||
rdesc[195] = 0xf;
|
||||
} else
|
||||
if (rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 &&
|
||||
if (*rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 &&
|
||||
rdesc[126] == 0x25 && rdesc[127] == 0x11) {
|
||||
samsung_irda_dev_trace(hdev, 135);
|
||||
rdesc[125] = 0x1;
|
||||
rdesc[127] = 0xe;
|
||||
} else
|
||||
if (rsize == 171 && rdesc[160] == 0x15 && rdesc[161] == 0x0 &&
|
||||
if (*rsize == 171 && rdesc[160] == 0x15 && rdesc[161] == 0x0 &&
|
||||
rdesc[162] == 0x25 && rdesc[163] == 0x01) {
|
||||
samsung_irda_dev_trace(hdev, 171);
|
||||
rdesc[161] = 0x1;
|
||||
rdesc[163] = 0x3;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
#define samsung_kbd_mouse_map_key_clear(c) \
|
||||
@ -130,11 +131,12 @@ static int samsung_kbd_mouse_input_mapping(struct hid_device *hdev,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (USB_DEVICE_ID_SAMSUNG_IR_REMOTE == hdev->product)
|
||||
samsung_irda_report_fixup(hdev, rdesc, rsize);
|
||||
rdesc = samsung_irda_report_fixup(hdev, rdesc, rsize);
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static int samsung_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
|
@ -24,24 +24,46 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define VAIO_RDESC_CONSTANT 0x0001
|
||||
#define VAIO_RDESC_CONSTANT (1 << 0)
|
||||
#define SIXAXIS_CONTROLLER_USB (1 << 1)
|
||||
#define SIXAXIS_CONTROLLER_BT (1 << 2)
|
||||
|
||||
struct sony_sc {
|
||||
unsigned long quirks;
|
||||
};
|
||||
|
||||
/* Sony Vaio VGX has wrongly mouse pointer declared as constant */
|
||||
static void sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct sony_sc *sc = hid_get_drvdata(hdev);
|
||||
|
||||
if ((sc->quirks & VAIO_RDESC_CONSTANT) &&
|
||||
rsize >= 56 && rdesc[54] == 0x81 && rdesc[55] == 0x07) {
|
||||
*rsize >= 56 && rdesc[54] == 0x81 && rdesc[55] == 0x07) {
|
||||
dev_info(&hdev->dev, "Fixing up Sony Vaio VGX report "
|
||||
"descriptor\n");
|
||||
rdesc[55] = 0x06;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
|
||||
size_t count, unsigned char report_type)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hid->dev.parent);
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||
int report_id = buf[0];
|
||||
int ret;
|
||||
|
||||
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
HID_REQ_SET_REPORT,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
((report_type + 1) << 8) | report_id,
|
||||
interface->desc.bInterfaceNumber, buf, count,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -49,7 +71,7 @@ static void sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
* to "operational". Without this, the ps3 controller will not report any
|
||||
* events.
|
||||
*/
|
||||
static int sony_set_operational_usb(struct hid_device *hdev)
|
||||
static int sixaxis_set_operational_usb(struct hid_device *hdev)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
@ -74,7 +96,7 @@ static int sony_set_operational_usb(struct hid_device *hdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sony_set_operational_bt(struct hid_device *hdev)
|
||||
static int sixaxis_set_operational_bt(struct hid_device *hdev)
|
||||
{
|
||||
unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
|
||||
return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
|
||||
@ -108,16 +130,14 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
switch (hdev->bus) {
|
||||
case BUS_USB:
|
||||
ret = sony_set_operational_usb(hdev);
|
||||
break;
|
||||
case BUS_BLUETOOTH:
|
||||
ret = sony_set_operational_bt(hdev);
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
|
||||
hdev->hid_output_raw_report = sixaxis_usb_output_raw_report;
|
||||
ret = sixaxis_set_operational_usb(hdev);
|
||||
}
|
||||
else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
|
||||
ret = sixaxis_set_operational_bt(hdev);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
if (ret < 0)
|
||||
goto err_stop;
|
||||
@ -137,8 +157,10 @@ static void sony_remove(struct hid_device *hdev)
|
||||
}
|
||||
|
||||
static const struct hid_device_id sony_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
|
||||
.driver_data = SIXAXIS_CONTROLLER_USB },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
|
||||
.driver_data = SIXAXIS_CONTROLLER_BT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE),
|
||||
.driver_data = VAIO_RDESC_CONSTANT },
|
||||
{ }
|
||||
|
@ -249,6 +249,8 @@ static void stantum_remove(struct hid_device *hdev)
|
||||
|
||||
static const struct hid_device_id stantum_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_MTP_STM) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, USB_DEVICE_ID_MTP_SITRONIX) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, stantum_devices);
|
||||
|
@ -22,16 +22,17 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static void sp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *sp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 &&
|
||||
if (*rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 &&
|
||||
rdesc[106] == 0x03) {
|
||||
dev_info(&hdev->dev, "fixing up Sunplus Wireless Desktop "
|
||||
"report descriptor\n");
|
||||
rdesc[105] = rdesc[110] = 0x03;
|
||||
rdesc[106] = rdesc[111] = 0x21;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
#define sp_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
|
||||
|
623
drivers/hid/hid-uclogic.c
Normal file
623
drivers/hid/hid-uclogic.c
Normal file
@ -0,0 +1,623 @@
|
||||
/*
|
||||
* HID driver for UC-Logic devices not fully compliant with HID standard
|
||||
*
|
||||
* Copyright (c) 2010 Nikolai Kondrashov
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
/*
|
||||
* The original descriptors of WPXXXXU tablets have three report IDs, of
|
||||
* which only two are used (8 and 9), and the remaining (7) seems to have
|
||||
* the originally intended pen description which was abandoned for some
|
||||
* reason. From this unused description it is possible to extract the
|
||||
* actual physical extents and resolution. All the models use the same
|
||||
* descriptor with different extents for the unused report ID.
|
||||
*
|
||||
* Here it is:
|
||||
*
|
||||
* Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
* Usage (Pen), ; Pen (02h, application collection)
|
||||
* Collection (Application),
|
||||
* Report ID (7),
|
||||
* Usage (Stylus), ; Stylus (20h, logical collection)
|
||||
* Collection (Physical),
|
||||
* Usage (Tip Switch), ; Tip switch (42h, momentary control)
|
||||
* Usage (Barrel Switch), ; Barrel switch (44h, momentary control)
|
||||
* Usage (Eraser), ; Eraser (45h, momentary control)
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (1),
|
||||
* Report Size (1),
|
||||
* Report Count (3),
|
||||
* Input (Variable),
|
||||
* Report Count (3),
|
||||
* Input (Constant, Variable),
|
||||
* Usage (In Range), ; In range (32h, momentary control)
|
||||
* Report Count (1),
|
||||
* Input (Variable),
|
||||
* Report Count (1),
|
||||
* Input (Constant, Variable),
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (X), ; X (30h, dynamic value)
|
||||
* Report Size (16),
|
||||
* Report Count (1),
|
||||
* Push,
|
||||
* Unit Exponent (13),
|
||||
* Unit (Inch^3),
|
||||
* Physical Minimum (0),
|
||||
* Physical Maximum (Xpm),
|
||||
* Logical Maximum (Xlm),
|
||||
* Input (Variable),
|
||||
* Usage (Y), ; Y (31h, dynamic value)
|
||||
* Physical Maximum (Ypm),
|
||||
* Logical Maximum (Ylm),
|
||||
* Input (Variable),
|
||||
* Pop,
|
||||
* Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
* Usage (Tip Pressure), ; Tip pressure (30h, dynamic value)
|
||||
* Logical Maximum (1023),
|
||||
* Input (Variable),
|
||||
* Report Size (16),
|
||||
* End Collection,
|
||||
* End Collection,
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (Mouse), ; Mouse (02h, application collection)
|
||||
* Collection (Application),
|
||||
* Report ID (8),
|
||||
* Usage (Pointer), ; Pointer (01h, physical collection)
|
||||
* Collection (Physical),
|
||||
* Usage Page (Button), ; Button (09h)
|
||||
* Usage Minimum (01h),
|
||||
* Usage Maximum (03h),
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (1),
|
||||
* Report Count (3),
|
||||
* Report Size (1),
|
||||
* Input (Variable),
|
||||
* Report Count (5),
|
||||
* Input (Constant),
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (X), ; X (30h, dynamic value)
|
||||
* Usage (Y), ; Y (31h, dynamic value)
|
||||
* Usage (Wheel), ; Wheel (38h, dynamic value)
|
||||
* Usage (00h),
|
||||
* Logical Minimum (-127),
|
||||
* Logical Maximum (127),
|
||||
* Report Size (8),
|
||||
* Report Count (4),
|
||||
* Input (Variable, Relative),
|
||||
* End Collection,
|
||||
* End Collection,
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (Mouse), ; Mouse (02h, application collection)
|
||||
* Collection (Application),
|
||||
* Report ID (9),
|
||||
* Usage (Pointer), ; Pointer (01h, physical collection)
|
||||
* Collection (Physical),
|
||||
* Usage Page (Button), ; Button (09h)
|
||||
* Usage Minimum (01h),
|
||||
* Usage Maximum (03h),
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (1),
|
||||
* Report Count (3),
|
||||
* Report Size (1),
|
||||
* Input (Variable),
|
||||
* Report Count (5),
|
||||
* Input (Constant),
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (X), ; X (30h, dynamic value)
|
||||
* Usage (Y), ; Y (31h, dynamic value)
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (32767),
|
||||
* Physical Minimum (0),
|
||||
* Physical Maximum (32767),
|
||||
* Report Count (2),
|
||||
* Report Size (16),
|
||||
* Input (Variable),
|
||||
* Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
* Usage (Tip Pressure), ; Tip pressure (30h, dynamic value)
|
||||
* Logical Maximum (1023),
|
||||
* Report Count (1),
|
||||
* Report Size (16),
|
||||
* Input (Variable),
|
||||
* End Collection,
|
||||
* End Collection
|
||||
*
|
||||
* Here are the extents values for the WPXXXXU models:
|
||||
*
|
||||
* Xpm Xlm Ypm Ylm
|
||||
* WP4030U 4000 8000 3000 6000
|
||||
* WP5540U 5500 11000 4000 8000
|
||||
* WP8060U 8000 16000 6000 12000
|
||||
*
|
||||
* This suggests that all of them have 2000 LPI resolution, as advertised.
|
||||
*/
|
||||
|
||||
/* Size of the original descriptor of WPXXXXU tablets */
|
||||
#define WPXXXXU_RDESC_ORIG_SIZE 212
|
||||
|
||||
/*
|
||||
* Fixed WP4030U report descriptor.
|
||||
* Although the hardware might actually support it, the mouse description
|
||||
* has been removed, since there seems to be no devices having one and it
|
||||
* wouldn't make much sense because of the working area size.
|
||||
*/
|
||||
static __u8 wp4030u_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x09, /* Report ID (9), */
|
||||
0x09, 0x20, /* Usage (Stylus), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x09, 0x44, /* Usage (Barrel Switch), */
|
||||
0x09, 0x46, /* Usage (Tablet Pick), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x05, /* Report Count (5), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0xA4, /* Push, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x55, 0xFD, /* Unit Exponent (-3), */
|
||||
0x65, 0x13, /* Unit (Inch), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x46, 0xA0, 0x0F, /* Physical Maximum (4000), */
|
||||
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x46, 0xB8, 0x0B, /* Physical Maximum (3000), */
|
||||
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xB4, /* Pop, */
|
||||
0x09, 0x30, /* Usage (Tip Pressure), */
|
||||
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/* Fixed WP5540U report descriptor */
|
||||
static __u8 wp5540u_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x09, /* Report ID (9), */
|
||||
0x09, 0x20, /* Usage (Stylus), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x09, 0x44, /* Usage (Barrel Switch), */
|
||||
0x09, 0x46, /* Usage (Tablet Pick), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x05, /* Report Count (5), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0xA4, /* Push, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x55, 0xFD, /* Unit Exponent (-3), */
|
||||
0x65, 0x13, /* Unit (Inch), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x46, 0x7C, 0x15, /* Physical Maximum (5500), */
|
||||
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x46, 0xA0, 0x0F, /* Physical Maximum (4000), */
|
||||
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xB4, /* Pop, */
|
||||
0x09, 0x30, /* Usage (Tip Pressure), */
|
||||
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x02, /* Usage (Mouse), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x08, /* Report ID (8), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x05, 0x09, /* Usage Page (Button), */
|
||||
0x19, 0x01, /* Usage Minimum (01h), */
|
||||
0x29, 0x03, /* Usage Maximum (03h), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x05, /* Report Count (5), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x15, 0x81, /* Logical Minimum (-127), */
|
||||
0x25, 0x7F, /* Logical Maximum (127), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x81, 0x06, /* Input (Variable, Relative), */
|
||||
0x09, 0x38, /* Usage (Wheel), */
|
||||
0x15, 0xFF, /* Logical Minimum (-1), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x06, /* Input (Variable, Relative), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/* Fixed WP8060U report descriptor */
|
||||
static __u8 wp8060u_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x09, /* Report ID (9), */
|
||||
0x09, 0x20, /* Usage (Stylus), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x09, 0x44, /* Usage (Barrel Switch), */
|
||||
0x09, 0x46, /* Usage (Tablet Pick), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x05, /* Report Count (5), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0xA4, /* Push, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x55, 0xFD, /* Unit Exponent (-3), */
|
||||
0x65, 0x13, /* Unit (Inch), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
|
||||
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x46, 0x70, 0x17, /* Physical Maximum (6000), */
|
||||
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xB4, /* Pop, */
|
||||
0x09, 0x30, /* Usage (Tip Pressure), */
|
||||
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x02, /* Usage (Mouse), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x08, /* Report ID (8), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x05, 0x09, /* Usage Page (Button), */
|
||||
0x19, 0x01, /* Usage Minimum (01h), */
|
||||
0x29, 0x03, /* Usage Maximum (03h), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x05, /* Report Count (5), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x15, 0x81, /* Logical Minimum (-127), */
|
||||
0x25, 0x7F, /* Logical Maximum (127), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x81, 0x06, /* Input (Variable, Relative), */
|
||||
0x09, 0x38, /* Usage (Wheel), */
|
||||
0x15, 0xFF, /* Logical Minimum (-1), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x06, /* Input (Variable, Relative), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/*
|
||||
* Original PF1209 report descriptor.
|
||||
*
|
||||
* The descriptor is similar to WPXXXXU descriptors, with an addition of a
|
||||
* feature report (ID 4) of unknown purpose.
|
||||
*
|
||||
* Although the advertised resolution is 4000 LPI the unused report ID
|
||||
* (taken from WPXXXXU, it seems) states 2000 LPI, but it is probably
|
||||
* incorrect and is a result of blind copying without understanding. Anyway
|
||||
* the real logical extents are always scaled to 0..32767, which IMHO spoils
|
||||
* the precision.
|
||||
*
|
||||
* Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
* Usage (Pen), ; Pen (02h, application collection)
|
||||
* Collection (Application),
|
||||
* Report ID (7),
|
||||
* Usage (Stylus), ; Stylus (20h, logical collection)
|
||||
* Collection (Physical),
|
||||
* Usage (Tip Switch), ; Tip switch (42h, momentary control)
|
||||
* Usage (Barrel Switch), ; Barrel switch (44h, momentary control)
|
||||
* Usage (Eraser), ; Eraser (45h, momentary control)
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (1),
|
||||
* Report Size (1),
|
||||
* Report Count (3),
|
||||
* Input (Variable),
|
||||
* Report Count (3),
|
||||
* Input (Constant, Variable),
|
||||
* Usage (In Range), ; In range (32h, momentary control)
|
||||
* Report Count (1),
|
||||
* Input (Variable),
|
||||
* Report Count (1),
|
||||
* Input (Constant, Variable),
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (X), ; X (30h, dynamic value)
|
||||
* Report Size (16),
|
||||
* Report Count (1),
|
||||
* Push,
|
||||
* Unit Exponent (13),
|
||||
* Unit (Inch^3),
|
||||
* Physical Minimum (0),
|
||||
* Physical Maximum (12000),
|
||||
* Logical Maximum (24000),
|
||||
* Input (Variable),
|
||||
* Usage (Y), ; Y (31h, dynamic value)
|
||||
* Physical Maximum (9000),
|
||||
* Logical Maximum (18000),
|
||||
* Input (Variable),
|
||||
* Pop,
|
||||
* Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
* Usage (Tip Pressure), ; Tip pressure (30h, dynamic value)
|
||||
* Logical Maximum (1023),
|
||||
* Input (Variable),
|
||||
* Report Size (16),
|
||||
* End Collection,
|
||||
* End Collection,
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (Mouse), ; Mouse (02h, application collection)
|
||||
* Collection (Application),
|
||||
* Report ID (8),
|
||||
* Usage (Pointer), ; Pointer (01h, physical collection)
|
||||
* Collection (Physical),
|
||||
* Usage Page (Button), ; Button (09h)
|
||||
* Usage Minimum (01h),
|
||||
* Usage Maximum (03h),
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (1),
|
||||
* Report Count (3),
|
||||
* Report Size (1),
|
||||
* Input (Variable),
|
||||
* Report Count (5),
|
||||
* Input (Constant),
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (X), ; X (30h, dynamic value)
|
||||
* Usage (Y), ; Y (31h, dynamic value)
|
||||
* Usage (Wheel), ; Wheel (38h, dynamic value)
|
||||
* Usage (00h),
|
||||
* Logical Minimum (-127),
|
||||
* Logical Maximum (127),
|
||||
* Report Size (8),
|
||||
* Report Count (4),
|
||||
* Input (Variable, Relative),
|
||||
* End Collection,
|
||||
* End Collection,
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (Mouse), ; Mouse (02h, application collection)
|
||||
* Collection (Application),
|
||||
* Report ID (9),
|
||||
* Usage (Pointer), ; Pointer (01h, physical collection)
|
||||
* Collection (Physical),
|
||||
* Usage Page (Button), ; Button (09h)
|
||||
* Usage Minimum (01h),
|
||||
* Usage Maximum (03h),
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (1),
|
||||
* Report Count (3),
|
||||
* Report Size (1),
|
||||
* Input (Variable),
|
||||
* Report Count (5),
|
||||
* Input (Constant),
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (X), ; X (30h, dynamic value)
|
||||
* Usage (Y), ; Y (31h, dynamic value)
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (32767),
|
||||
* Physical Minimum (0),
|
||||
* Physical Maximum (32767),
|
||||
* Report Count (2),
|
||||
* Report Size (16),
|
||||
* Input (Variable),
|
||||
* Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
* Usage (Tip Pressure), ; Tip pressure (30h, dynamic value)
|
||||
* Logical Maximum (1023),
|
||||
* Report Count (1),
|
||||
* Report Size (16),
|
||||
* Input (Variable),
|
||||
* End Collection,
|
||||
* End Collection,
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (00h),
|
||||
* Collection (Application),
|
||||
* Report ID (4),
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (255),
|
||||
* Usage (00h),
|
||||
* Report Size (8),
|
||||
* Report Count (3),
|
||||
* Feature (Variable),
|
||||
* End Collection
|
||||
*/
|
||||
|
||||
/* Size of the original descriptor of PF1209 tablet */
|
||||
#define PF1209_RDESC_ORIG_SIZE 234
|
||||
|
||||
/*
|
||||
* Fixed PF1209 report descriptor
|
||||
*
|
||||
* The descriptor is fixed similarly to WP5540U and WP8060U, plus the
|
||||
* feature report is removed, because its purpose is unknown and it is of no
|
||||
* use to the generic HID driver anyway for now.
|
||||
*/
|
||||
static __u8 pf1209_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x09, /* Report ID (9), */
|
||||
0x09, 0x20, /* Usage (Stylus), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x09, 0x44, /* Usage (Barrel Switch), */
|
||||
0x09, 0x46, /* Usage (Tablet Pick), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x05, /* Report Count (5), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0xA4, /* Push, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x55, 0xFD, /* Unit Exponent (-3), */
|
||||
0x65, 0x13, /* Unit (Inch), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x46, 0xE0, 0x2E, /* Physical Maximum (12000), */
|
||||
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x46, 0x28, 0x23, /* Physical Maximum (9000), */
|
||||
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xB4, /* Pop, */
|
||||
0x09, 0x30, /* Usage (Tip Pressure), */
|
||||
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0, /* End Collection, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x02, /* Usage (Mouse), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x08, /* Report ID (8), */
|
||||
0x09, 0x01, /* Usage (Pointer), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x05, 0x09, /* Usage Page (Button), */
|
||||
0x19, 0x01, /* Usage Minimum (01h), */
|
||||
0x29, 0x03, /* Usage Maximum (03h), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x05, /* Report Count (5), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x15, 0x81, /* Logical Minimum (-127), */
|
||||
0x25, 0x7F, /* Logical Maximum (127), */
|
||||
0x95, 0x02, /* Report Count (2), */
|
||||
0x81, 0x06, /* Input (Variable, Relative), */
|
||||
0x09, 0x38, /* Usage (Wheel), */
|
||||
0x15, 0xFF, /* Logical Minimum (-1), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x06, /* Input (Variable, Relative), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209:
|
||||
if (*rsize == PF1209_RDESC_ORIG_SIZE) {
|
||||
rdesc = pf1209_rdesc_fixed;
|
||||
*rsize = sizeof(pf1209_rdesc_fixed);
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U:
|
||||
if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
|
||||
rdesc = wp4030u_rdesc_fixed;
|
||||
*rsize = sizeof(wp4030u_rdesc_fixed);
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U:
|
||||
if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
|
||||
rdesc = wp5540u_rdesc_fixed;
|
||||
*rsize = sizeof(wp5540u_rdesc_fixed);
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U:
|
||||
if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
|
||||
rdesc = wp8060u_rdesc_fixed;
|
||||
*rsize = sizeof(wp8060u_rdesc_fixed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static const struct hid_device_id uclogic_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
|
||||
USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
|
||||
USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
|
||||
USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
|
||||
USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, uclogic_devices);
|
||||
|
||||
static struct hid_driver uclogic_driver = {
|
||||
.name = "uclogic",
|
||||
.id_table = uclogic_devices,
|
||||
.report_fixup = uclogic_report_fixup,
|
||||
};
|
||||
|
||||
static int __init uclogic_init(void)
|
||||
{
|
||||
return hid_register_driver(&uclogic_driver);
|
||||
}
|
||||
|
||||
static void __exit uclogic_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&uclogic_driver);
|
||||
}
|
||||
|
||||
module_init(uclogic_init);
|
||||
module_exit(uclogic_exit);
|
||||
MODULE_LICENSE("GPL");
|
1099
drivers/hid/hid-waltop.c
Normal file
1099
drivers/hid/hid-waltop.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -27,10 +27,10 @@ struct zc_device {
|
||||
* Zydacron remote control has an invalid HID report descriptor,
|
||||
* that needs fixing before we can parse it.
|
||||
*/
|
||||
static void zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int rsize)
|
||||
static __u8 *zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (rsize >= 253 &&
|
||||
if (*rsize >= 253 &&
|
||||
rdesc[0x96] == 0xbc && rdesc[0x97] == 0xff &&
|
||||
rdesc[0xca] == 0xbc && rdesc[0xcb] == 0xff &&
|
||||
rdesc[0xe1] == 0xbc && rdesc[0xe2] == 0xff) {
|
||||
@ -40,6 +40,7 @@ static void zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
rdesc[0x96] = rdesc[0xca] = rdesc[0xe1] = 0x0c;
|
||||
rdesc[0x97] = rdesc[0xcb] = rdesc[0xe2] = 0x00;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
#define zc_map_key_clear(c) \
|
||||
|
@ -218,9 +218,13 @@ static int hidraw_release(struct inode * inode, struct file * file)
|
||||
unsigned int minor = iminor(inode);
|
||||
struct hidraw *dev;
|
||||
struct hidraw_list *list = file->private_data;
|
||||
int ret;
|
||||
|
||||
if (!hidraw_table[minor])
|
||||
return -ENODEV;
|
||||
mutex_lock(&minors_lock);
|
||||
if (!hidraw_table[minor]) {
|
||||
ret = -ENODEV;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
list_del(&list->node);
|
||||
dev = hidraw_table[minor];
|
||||
@ -233,10 +237,12 @@ static int hidraw_release(struct inode * inode, struct file * file)
|
||||
kfree(list->hidraw);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(list);
|
||||
ret = 0;
|
||||
unlock:
|
||||
mutex_unlock(&minors_lock);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long hidraw_ioctl(struct file *file, unsigned int cmd,
|
||||
|
@ -807,9 +807,10 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co
|
||||
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||
int ret;
|
||||
|
||||
if (usbhid->urbout) {
|
||||
if (usbhid->urbout && report_type != HID_FEATURE_REPORT) {
|
||||
int actual_length;
|
||||
int skipped_report_id = 0;
|
||||
|
||||
if (buf[0] == 0x0) {
|
||||
/* Don't send the Report ID */
|
||||
buf++;
|
||||
@ -1469,9 +1470,6 @@ static int __init hid_init(void)
|
||||
retval = usbhid_quirks_init(quirks_param);
|
||||
if (retval)
|
||||
goto usbhid_quirks_init_fail;
|
||||
retval = hiddev_init();
|
||||
if (retval)
|
||||
goto hiddev_init_fail;
|
||||
retval = usb_register(&hid_driver);
|
||||
if (retval)
|
||||
goto usb_register_fail;
|
||||
@ -1479,8 +1477,6 @@ static int __init hid_init(void)
|
||||
|
||||
return 0;
|
||||
usb_register_fail:
|
||||
hiddev_exit();
|
||||
hiddev_init_fail:
|
||||
usbhid_quirks_exit();
|
||||
usbhid_quirks_init_fail:
|
||||
hid_unregister_driver(&hid_usb_driver);
|
||||
@ -1493,7 +1489,6 @@ no_queue:
|
||||
static void __exit hid_exit(void)
|
||||
{
|
||||
usb_deregister(&hid_driver);
|
||||
hiddev_exit();
|
||||
usbhid_quirks_exit();
|
||||
hid_unregister_driver(&hid_usb_driver);
|
||||
destroy_workqueue(resumption_waker);
|
||||
|
@ -34,7 +34,6 @@ static const struct hid_blacklist {
|
||||
{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_DWAV, USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER, HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
|
||||
@ -63,6 +62,7 @@ static const struct hid_blacklist {
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET },
|
||||
{ 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_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
|
||||
@ -72,6 +72,10 @@ static const struct hid_blacklist {
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_KNA5, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
|
||||
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
||||
|
||||
|
@ -67,8 +67,6 @@ struct hiddev_list {
|
||||
struct mutex thread_lock;
|
||||
};
|
||||
|
||||
static struct usb_driver hiddev_driver;
|
||||
|
||||
/*
|
||||
* Find a report, given the report's type and ID. The ID can be specified
|
||||
* indirectly by REPORT_ID_FIRST (which returns the first report of the given
|
||||
@ -926,41 +924,3 @@ void hiddev_disconnect(struct hid_device *hid)
|
||||
kfree(hiddev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Currently this driver is a USB driver. It's not a conventional one in
|
||||
* the sense that it doesn't probe at the USB level. Instead it waits to
|
||||
* be connected by HID through the hiddev_connect / hiddev_disconnect
|
||||
* routines. The reason to register as a USB device is to gain part of the
|
||||
* minor number space from the USB major.
|
||||
*
|
||||
* In theory, should the HID code be generalized to more than one physical
|
||||
* medium (say, IEEE 1384), this driver will probably need to register its
|
||||
* own major number, and in doing so, no longer need to register with USB.
|
||||
* At that point the probe routine and hiddev_driver struct below will no
|
||||
* longer be useful.
|
||||
*/
|
||||
|
||||
|
||||
/* We never attach in this manner, and rely on HID to connect us. This
|
||||
* is why there is no disconnect routine defined in the usb_driver either.
|
||||
*/
|
||||
static int hiddev_usbd_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *hiddev_info)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static /* const */ struct usb_driver hiddev_driver = {
|
||||
.name = "hiddev",
|
||||
.probe = hiddev_usbd_probe,
|
||||
};
|
||||
|
||||
int __init hiddev_init(void)
|
||||
{
|
||||
return usb_register(&hiddev_driver);
|
||||
}
|
||||
|
||||
void hiddev_exit(void)
|
||||
{
|
||||
usb_deregister(&hiddev_driver);
|
||||
}
|
||||
|
@ -316,6 +316,7 @@ struct hid_item {
|
||||
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
|
||||
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000
|
||||
#define HID_QUIRK_NO_IGNORE 0x40000000
|
||||
#define HID_QUIRK_NO_INPUT_SYNC 0x80000000
|
||||
|
||||
/*
|
||||
* This is the global environment of the parser. This information is
|
||||
@ -626,8 +627,8 @@ struct hid_driver {
|
||||
int (*event)(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value);
|
||||
|
||||
void (*report_fixup)(struct hid_device *hdev, __u8 *buf,
|
||||
unsigned int size);
|
||||
__u8 *(*report_fixup)(struct hid_device *hdev, __u8 *buf,
|
||||
unsigned int *size);
|
||||
|
||||
int (*input_mapping)(struct hid_device *hdev,
|
||||
struct hid_input *hidinput, struct hid_field *field,
|
||||
|
@ -226,8 +226,6 @@ void hiddev_disconnect(struct hid_device *);
|
||||
void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value);
|
||||
void hiddev_report_event(struct hid_device *hid, struct hid_report *report);
|
||||
int __init hiddev_init(void);
|
||||
void hiddev_exit(void);
|
||||
#else
|
||||
static inline int hiddev_connect(struct hid_device *hid,
|
||||
unsigned int force)
|
||||
@ -236,8 +234,6 @@ static inline void hiddev_disconnect(struct hid_device *hid) { }
|
||||
static inline void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value) { }
|
||||
static inline void hiddev_report_event(struct hid_device *hid, struct hid_report *report) { }
|
||||
static inline int hiddev_init(void) { return 0; }
|
||||
static inline void hiddev_exit(void) { }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user