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:
Linus Torvalds 2010-10-24 12:44:59 -07:00
commit 7c024e9534
42 changed files with 3933 additions and 373 deletions

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

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

View file

@ -56,20 +56,20 @@ menu "Special HID drivers"
depends on HID depends on HID
config HID_3M_PCT config HID_3M_PCT
tristate "3M PCT" tristate "3M PCT touchscreen"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for 3M PCT touch screens. Support for 3M PCT touch screens.
config HID_A4TECH config HID_A4TECH
tristate "A4 tech" if EMBEDDED tristate "A4 tech mice" if EMBEDDED
depends on USB_HID depends on USB_HID
default !EMBEDDED default !EMBEDDED
---help--- ---help---
Support for A4 tech X5 and WOP-35 / Trust 450L mice. Support for A4 tech X5 and WOP-35 / Trust 450L mice.
config HID_ACRUX_FF config HID_ACRUX_FF
tristate "ACRUX force feedback support" tristate "ACRUX force feedback"
depends on USB_HID depends on USB_HID
select INPUT_FF_MEMLESS select INPUT_FF_MEMLESS
---help--- ---help---
@ -77,7 +77,7 @@ config HID_ACRUX_FF
game controllers. game controllers.
config HID_APPLE config HID_APPLE
tristate "Apple" if EMBEDDED tristate "Apple {i,Power,Mac}Books" if EMBEDDED
depends on (USB_HID || BT_HIDP) depends on (USB_HID || BT_HIDP)
default !EMBEDDED default !EMBEDDED
---help--- ---help---
@ -88,7 +88,7 @@ config HID_APPLE
MacBooks, MacBook Pros and Apple Aluminum. MacBooks, MacBook Pros and Apple Aluminum.
config HID_BELKIN config HID_BELKIN
tristate "Belkin" if EMBEDDED tristate "Belkin Flip KVM and Wireless keyboard" if EMBEDDED
depends on USB_HID depends on USB_HID
default !EMBEDDED default !EMBEDDED
---help--- ---help---
@ -101,14 +101,14 @@ config HID_CANDO
Support for Cando dual touch panel. Support for Cando dual touch panel.
config HID_CHERRY config HID_CHERRY
tristate "Cherry" if EMBEDDED tristate "Cherry Cymotion keyboard" if EMBEDDED
depends on USB_HID depends on USB_HID
default !EMBEDDED default !EMBEDDED
---help--- ---help---
Support for Cherry Cymotion keyboard. Support for Cherry Cymotion keyboard.
config HID_CHICONY config HID_CHICONY
tristate "Chicony" if EMBEDDED tristate "Chicony Tactical pad" if EMBEDDED
depends on USB_HID depends on USB_HID
default !EMBEDDED default !EMBEDDED
---help--- ---help---
@ -130,20 +130,20 @@ config HID_PRODIKEYS
and some additional multimedia keys. and some additional multimedia keys.
config HID_CYPRESS config HID_CYPRESS
tristate "Cypress" if EMBEDDED tristate "Cypress mouse and barcode readers" if EMBEDDED
depends on USB_HID depends on USB_HID
default !EMBEDDED default !EMBEDDED
---help--- ---help---
Support for cypress mouse and barcode readers. Support for cypress mouse and barcode readers.
config HID_DRAGONRISE config HID_DRAGONRISE
tristate "DragonRise Inc. support" tristate "DragonRise Inc. game controller"
depends on USB_HID depends on USB_HID
---help--- ---help---
Say Y here if you have DragonRise Inc.game controllers. Say Y here if you have DragonRise Inc.game controllers.
config DRAGONRISE_FF config DRAGONRISE_FF
bool "DragonRise Inc. force feedback support" bool "DragonRise Inc. force feedback"
depends on HID_DRAGONRISE depends on HID_DRAGONRISE
select INPUT_FF_MEMLESS select INPUT_FF_MEMLESS
---help--- ---help---
@ -157,46 +157,58 @@ config HID_EGALAX
Support for the eGalax dual-touch panel. Support for the eGalax dual-touch panel.
config HID_ELECOM config HID_ELECOM
tristate "ELECOM" tristate "ELECOM BM084 bluetooth mouse"
depends on BT_HIDP depends on BT_HIDP
---help--- ---help---
Support for the ELECOM BM084 (bluetooth mouse). Support for the ELECOM BM084 (bluetooth mouse).
config HID_EZKEY config HID_EZKEY
tristate "Ezkey" if EMBEDDED tristate "Ezkey BTC 8193 keyboard" if EMBEDDED
depends on USB_HID depends on USB_HID
default !EMBEDDED default !EMBEDDED
---help--- ---help---
Support for Ezkey BTC 8193 keyboard. Support for Ezkey BTC 8193 keyboard.
config HID_KYE config HID_KYE
tristate "Kye" if EMBEDDED tristate "Kye/Genius Ergo Mouse" if EMBEDDED
depends on USB_HID depends on USB_HID
default !EMBEDDED default !EMBEDDED
---help--- ---help---
Support for Kye/Genius Ergo Mouse. 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 config HID_GYRATION
tristate "Gyration" tristate "Gyration remote control"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for Gyration remote control. Support for Gyration remote control.
config HID_TWINHAN config HID_TWINHAN
tristate "Twinhan" tristate "Twinhan IR remote control"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for Twinhan IR remote control. Support for Twinhan IR remote control.
config HID_KENSINGTON config HID_KENSINGTON
tristate "Kensington" if EMBEDDED tristate "Kensington Slimblade Trackball" if EMBEDDED
depends on USB_HID depends on USB_HID
default !EMBEDDED default !EMBEDDED
---help--- ---help---
Support for Kensington Slimblade Trackball. Support for Kensington Slimblade Trackball.
config HID_LOGITECH config HID_LOGITECH
tristate "Logitech" if EMBEDDED tristate "Logitech devices" if EMBEDDED
depends on USB_HID depends on USB_HID
default !EMBEDDED default !EMBEDDED
---help--- ---help---
@ -220,12 +232,12 @@ config LOGITECH_FF
force feedback. force feedback.
config LOGIRUMBLEPAD2_FF config LOGIRUMBLEPAD2_FF
bool "Logitech Rumblepad 2 force feedback support" bool "Logitech RumblePad/Rumblepad 2 force feedback support"
depends on HID_LOGITECH depends on HID_LOGITECH
select INPUT_FF_MEMLESS select INPUT_FF_MEMLESS
help help
Say Y here if you want to enable force feedback support for Logitech Say Y here if you want to enable force feedback support for Logitech
Rumblepad 2 devices. RumblePad and Rumblepad 2 devices.
config LOGIG940_FF config LOGIG940_FF
bool "Logitech Flight System G940 force feedback support" 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 Say Y here if you want to enable force feedback support for Logitech
Flight System G940 devices. 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 config HID_MAGICMOUSE
tristate "Apple MagicMouse multi-touch support" tristate "Apple MagicMouse multi-touch support"
depends on BT_HIDP depends on BT_HIDP
@ -245,39 +265,39 @@ config HID_MAGICMOUSE
Apple Wireless "Magic" Mouse. Apple Wireless "Magic" Mouse.
config HID_MICROSOFT config HID_MICROSOFT
tristate "Microsoft" if EMBEDDED tristate "Microsoft non-fully HID-compliant devices" if EMBEDDED
depends on USB_HID depends on USB_HID
default !EMBEDDED default !EMBEDDED
---help--- ---help---
Support for Microsoft devices that are not fully compliant with HID standard. Support for Microsoft devices that are not fully compliant with HID standard.
config HID_MOSART config HID_MOSART
tristate "MosArt" tristate "MosArt dual-touch panels"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for MosArt dual-touch panels. Support for MosArt dual-touch panels.
config HID_MONTEREY config HID_MONTEREY
tristate "Monterey" if EMBEDDED tristate "Monterey Genius KB29E keyboard" if EMBEDDED
depends on USB_HID depends on USB_HID
default !EMBEDDED default !EMBEDDED
---help--- ---help---
Support for Monterey Genius KB29E. Support for Monterey Genius KB29E.
config HID_NTRIG config HID_NTRIG
tristate "NTrig" tristate "N-Trig touch screen"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for N-Trig touch screen. Support for N-Trig touch screen.
config HID_ORTEK config HID_ORTEK
tristate "Ortek" tristate "Ortek WKB-2000 wireless keyboard and mouse trackpad"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for Ortek WKB-2000 wireless keyboard + mouse trackpad. Support for Ortek WKB-2000 wireless keyboard + mouse trackpad.
config HID_PANTHERLORD config HID_PANTHERLORD
tristate "Pantherlord support" tristate "Pantherlord/GreenAsia game controller"
depends on USB_HID depends on USB_HID
---help--- ---help---
Say Y here if you have a PantherLord/GreenAsia based game controller 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. or adapter and want to enable force feedback support for it.
config HID_PETALYNX config HID_PETALYNX
tristate "Petalynx" tristate "Petalynx Maxter remote control"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for Petalynx Maxter remote control. Support for Petalynx Maxter remote control.
@ -356,7 +376,7 @@ config HID_PICOLCD_LEDS
Provide access to PicoLCD's GPO pins via leds class. Provide access to PicoLCD's GPO pins via leds class.
config HID_QUANTA config HID_QUANTA
tristate "Quanta Optical Touch" tristate "Quanta Optical Touch panels"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for Quanta Optical Touch dual-touch panels. Support for Quanta Optical Touch dual-touch panels.
@ -376,32 +396,39 @@ config HID_ROCCAT_KONE
---help--- ---help---
Support for Roccat Kone mouse. 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 config HID_SAMSUNG
tristate "Samsung" tristate "Samsung InfraRed remote control or keyboards"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for Samsung InfraRed remote control or keyboards. Support for Samsung InfraRed remote control or keyboards.
config HID_SONY config HID_SONY
tristate "Sony" tristate "Sony PS3 controller"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for Sony PS3 controller. Support for Sony PS3 controller.
config HID_STANTUM config HID_STANTUM
tristate "Stantum" tristate "Stantum multitouch panel"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for Stantum multitouch panel. Support for Stantum multitouch panel.
config HID_SUNPLUS config HID_SUNPLUS
tristate "Sunplus" tristate "Sunplus wireless desktop"
depends on USB_HID depends on USB_HID
---help--- ---help---
Support for Sunplus wireless desktop. Support for Sunplus wireless desktop.
config HID_GREENASIA config HID_GREENASIA
tristate "GreenAsia (Product ID 0x12) support" tristate "GreenAsia (Product ID 0x12) game controller support"
depends on USB_HID depends on USB_HID
---help--- ---help---
Say Y here if you have a GreenAsia (Product ID 0x12) based game Say Y here if you have a GreenAsia (Product ID 0x12) based game

View file

@ -21,6 +21,9 @@ endif
ifdef CONFIG_LOGIG940_FF ifdef CONFIG_LOGIG940_FF
hid-logitech-objs += hid-lg3ff.o hid-logitech-objs += hid-lg3ff.o
endif endif
ifdef CONFIG_LOGIWII_FF
hid-logitech-objs += hid-lg4ff.o
endif
obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.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_PICOLCD) += hid-picolcd.o
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o
obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.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_SAMSUNG) += hid-samsung.o
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
obj-$(CONFIG_HID_SONY) += hid-sony.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_THRUSTMASTER) += hid-tmff.o
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.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_ZEROPLUS) += hid-zpff.o
obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
obj-$(CONFIG_HID_WACOM) += hid-wacom.o obj-$(CONFIG_HID_WACOM) += hid-wacom.o
obj-$(CONFIG_HID_WALTOP) += hid-waltop.o
obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_HID) += usbhid/
obj-$(CONFIG_USB_MOUSE) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/

View file

@ -2,6 +2,8 @@
* HID driver for 3M PCT multitouch panels * HID driver for 3M PCT multitouch panels
* *
* Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr> * 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" #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 { struct mmm_finger {
__s32 x, y, w, h; __s32 x, y, w, h;
__u8 rank; __u16 id;
bool prev_touch;
bool touch, valid; bool touch, valid;
}; };
struct mmm_data { struct mmm_data {
struct mmm_finger f[10]; struct mmm_finger f[MAX_SLOTS];
__u8 curid, num; __u16 id;
__u8 curid;
__u8 nexp, nreal;
bool touch, valid; 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, struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max) 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) { switch (usage->hid & HID_USAGE_PAGE) {
case HID_UP_BUTTON: 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: case HID_GD_X:
hid_map_usage(hi, usage, bit, max, hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_POSITION_X); EV_ABS, ABS_MT_POSITION_X);
input_set_abs_params(hi->input, ABS_MT_POSITION_X,
f1, f2, df / SN_MOVE, 0);
/* touchscreen emulation */ /* touchscreen emulation */
input_set_abs_params(hi->input, ABS_X, input_set_abs_params(hi->input, ABS_X,
field->logical_minimum, f1, f2, df / SN_MOVE, 0);
field->logical_maximum, 0, 0);
return 1; return 1;
case HID_GD_Y: case HID_GD_Y:
hid_map_usage(hi, usage, bit, max, hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_POSITION_Y); EV_ABS, ABS_MT_POSITION_Y);
input_set_abs_params(hi->input, ABS_MT_POSITION_Y,
f1, f2, df / SN_MOVE, 0);
/* touchscreen emulation */ /* touchscreen emulation */
input_set_abs_params(hi->input, ABS_Y, input_set_abs_params(hi->input, ABS_Y,
field->logical_minimum, f1, f2, df / SN_MOVE, 0);
field->logical_maximum, 0, 0);
return 1; return 1;
} }
return 0; return 0;
@ -81,21 +100,31 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_DG_TIPSWITCH: case HID_DG_TIPSWITCH:
/* touchscreen emulation */ /* touchscreen emulation */
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
input_set_capability(hi->input, EV_KEY, BTN_TOUCH);
return 1; return 1;
case HID_DG_WIDTH: case HID_DG_WIDTH:
hid_map_usage(hi, usage, bit, max, hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TOUCH_MAJOR); EV_ABS, ABS_MT_TOUCH_MAJOR);
input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR,
f1, f2, df / SN_WIDTH, 0);
return 1; return 1;
case HID_DG_HEIGHT: case HID_DG_HEIGHT:
hid_map_usage(hi, usage, bit, max, 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_TOUCH_MINOR,
f1, f2, df / SN_WIDTH, 0);
input_set_abs_params(hi->input, ABS_MT_ORIENTATION, input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
1, 1, 0, 0); 0, 1, 0, 0);
return 1; return 1;
case HID_DG_CONTACTID: case HID_DG_CONTACTID:
field->logical_maximum = 59; field->logical_maximum = MAX_TRKID;
hid_map_usage(hi, usage, bit, max, hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TRACKING_ID); 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; return 1;
} }
/* let hid-input decide for the others */ /* 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, struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max) unsigned long **bit, int *max)
{ {
/* tell hid-input to skip setup of these event types */
if (usage->type == EV_KEY || usage->type == EV_ABS) if (usage->type == EV_KEY || usage->type == EV_ABS)
clear_bit(usage->code, *bit); set_bit(usage->type, hi->input->evbit);
return -1;
return 0;
} }
/* /*
@ -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) static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
{ {
struct mmm_finger *oldest = 0; struct mmm_finger *oldest = 0;
bool pressed = false, released = false;
int i; int i;
for (i = 0; i < MAX_SLOTS; ++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) {
struct mmm_finger *f = &md->f[i]; struct mmm_finger *f = &md->f[i];
if (!f->valid) { if (!f->valid) {
/* this finger is just placeholder data, ignore */ /* 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 */ /* this finger is on the screen */
int wide = (f->w > f->h); 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_X, f->x);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); 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_ORIENTATION, wide);
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
wide ? f->w : f->h); input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, /* touchscreen emulation: pick the oldest contact */
wide ? f->h : f->w); if (!oldest || ((f->id - oldest->id) & (SHRT_MAX + 1)))
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)
oldest = f; oldest = f;
} else { } else {
/* this finger took off the screen */ /* this finger took off the screen */
/* touchscreen emulation: maintain age rank of others */ input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1);
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;
} }
f->prev_touch = f->touch;
f->valid = 0; f->valid = 0;
} }
/* touchscreen emulation */ /* touchscreen emulation */
if (oldest) { 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_X, oldest->x);
input_event(input, EV_ABS, ABS_Y, oldest->y); input_event(input, EV_ABS, ABS_Y, oldest->y);
} else if (released) { } else {
input_event(input, EV_KEY, BTN_TOUCH, 0); 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; md->f[md->curid].h = value;
break; break;
case HID_DG_CONTACTID: case HID_DG_CONTACTID:
value = clamp_val(value, 0, MAX_SLOTS - 1);
if (md->valid) { if (md->valid) {
md->curid = value; md->curid = value;
md->f[value].touch = md->touch; md->f[value].touch = md->touch;
md->f[value].valid = 1; md->f[value].valid = 1;
md->nreal++;
} }
break; break;
case HID_GD_X: 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; md->f[md->curid].y = value;
break; break;
case HID_DG_CONTACTCOUNT: 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; break;
} }
} }
@ -255,6 +270,8 @@ static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id)
int ret; int ret;
struct mmm_data *md; struct mmm_data *md;
hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL); md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL);
if (!md) { if (!md) {
dev_err(&hdev->dev, "cannot allocate 3M data\n"); dev_err(&hdev->dev, "cannot allocate 3M data\n");

View file

@ -133,6 +133,8 @@ static const struct hid_device_id a4_devices[] = {
.driver_data = A4_2WHEEL_MOUSE_HACK_7 }, .driver_data = A4_2WHEEL_MOUSE_HACK_7 },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D), { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D),
.driver_data = A4_2WHEEL_MOUSE_HACK_B8 }, .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); MODULE_DEVICE_TABLE(hid, a4_devices);

View file

@ -246,17 +246,18 @@ static int apple_event(struct hid_device *hdev, struct hid_field *field,
/* /*
* MacBook JIS keyboard has wrong logical maximum * MacBook JIS keyboard has wrong logical maximum
*/ */
static void apple_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) unsigned int *rsize)
{ {
struct apple_sc *asc = hid_get_drvdata(hdev); 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) { rdesc[53] == 0x65 && rdesc[59] == 0x65) {
dev_info(&hdev->dev, "fixing up MacBook JIS keyboard report " dev_info(&hdev->dev, "fixing up MacBook JIS keyboard report "
"descriptor\n"); "descriptor\n");
rdesc[53] = rdesc[59] = 0xe7; rdesc[53] = rdesc[59] = 0xe7;
} }
return rdesc;
} }
static void apple_setup_input(struct input_dev *input) static void apple_setup_input(struct input_dev *input)

View file

@ -26,15 +26,16 @@
* Cherry Cymotion keyboard have an invalid HID report descriptor, * Cherry Cymotion keyboard have an invalid HID report descriptor,
* that needs fixing before we can parse it. * that needs fixing before we can parse it.
*/ */
static void ch_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *ch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) 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 " dev_info(&hdev->dev, "fixing up Cherry Cymotion report "
"descriptor\n"); "descriptor\n");
rdesc[11] = rdesc[16] = 0xff; rdesc[11] = rdesc[16] = 0xff;
rdesc[12] = rdesc[17] = 0x03; rdesc[12] = rdesc[17] = 0x03;
} }
return rdesc;
} }
#define ch_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ #define ch_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \

View file

@ -388,12 +388,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
__u32 data; __u32 data;
unsigned n; 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); data = item_udata(item);
switch (item->tag) { switch (item->tag) {
@ -651,7 +645,7 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
}; };
if (device->driver->report_fixup) 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); device->rdesc = kmemdup(start, size, GFP_KERNEL);
if (device->rdesc == NULL) 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_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_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_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) #if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE)
{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) },
#endif #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_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, { 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_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_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_FOUNTAIN_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) }, { 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_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_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_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_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_RUMBLEPAD2_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, { 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_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_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_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_LOGITECH_RUMBLEPAD2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, { 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_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_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_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_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, { 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_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_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_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, 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_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, { 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_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_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_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_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_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, 0x0005) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },

View file

@ -31,16 +31,16 @@
* Some USB barcode readers from cypress have usage min and usage max in * Some USB barcode readers from cypress have usage min and usage max in
* the wrong order * the wrong order
*/ */
static void cp_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) unsigned int *rsize)
{ {
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
unsigned int i; unsigned int i;
if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX)) 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) { if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
__u8 tmp; __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 + 3] = rdesc[i + 1];
rdesc[i + 1] = tmp; rdesc[i + 1] = tmp;
} }
return rdesc;
} }
static int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi, static int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi,

View file

@ -570,6 +570,8 @@ void hid_debug_event(struct hid_device *hdev, char *buf)
buf[i]; buf[i];
list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE; list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
} }
wake_up_interruptible(&hdev->debug_wait);
} }
EXPORT_SYMBOL_GPL(hid_debug_event); EXPORT_SYMBOL_GPL(hid_debug_event);

View file

@ -31,7 +31,7 @@ struct egalax_data {
bool first; /* is this the first finger in the frame? */ bool first; /* is this the first finger in the frame? */
bool valid; /* valid finger data, or just placeholder? */ bool valid; /* valid finger data, or just placeholder? */
bool activity; /* at least one active finger previously? */ 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, 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: case HID_DG_TIPPRESSURE:
hid_map_usage(hi, usage, bit, max, hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_PRESSURE); 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 1;
} }
return 0; return 0;
@ -109,8 +113,8 @@ static void egalax_filter_event(struct egalax_data *td, struct input_dev *input)
if (td->valid) { if (td->valid) {
/* emit multitouch events */ /* emit multitouch events */
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id); 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_X, td->x >> 3);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y); input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y >> 3);
input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z); input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z);
input_mt_sync(input); 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->lastx = td->x;
td->lasty = td->y; 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 * the oldest on the panel, the one we want for single touch
*/ */
if (!td->first && td->activity) { if (!td->first && td->activity) {
input_event(input, EV_ABS, ABS_X, td->lastx); input_event(input, EV_ABS, ABS_X, td->lastx >> 3);
input_event(input, EV_ABS, ABS_Y, td->lasty); input_event(input, EV_ABS, ABS_Y, td->lasty >> 3);
input_event(input, EV_ABS, ABS_PRESSURE, td->lastz);
} }
if (!td->valid) { if (!td->valid) {

View file

@ -20,14 +20,15 @@
#include "hid-ids.h" #include "hid-ids.h"
static void elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) 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 " dev_info(&hdev->dev, "Fixing up Elecom BM084 "
"report descriptor.\n"); "report descriptor.\n");
rdesc[47] = 0x00; rdesc[47] = 0x00;
} }
return rdesc;
} }
static const struct hid_device_id elecom_devices[] = { static const struct hid_device_id elecom_devices[] = {

View file

@ -25,6 +25,7 @@
#define USB_VENDOR_ID_A4TECH 0x09da #define USB_VENDOR_ID_A4TECH 0x09da
#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 #define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006
#define USB_DEVICE_ID_A4TECH_X5_005D 0x000a #define USB_DEVICE_ID_A4TECH_X5_005D 0x000a
#define USB_DEVICE_ID_A4TECH_RP_649 0x001a
#define USB_VENDOR_ID_AASHIMA 0x06d6 #define USB_VENDOR_ID_AASHIMA 0x06d6
#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 #define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025
@ -63,6 +64,7 @@
#define USB_VENDOR_ID_APPLE 0x05ac #define USB_VENDOR_ID_APPLE 0x05ac
#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d #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_ANSI 0x020e
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f #define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f
#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 #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_ECLIPSE_YOKE 0x0051
#define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE 0x00ff #define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE 0x00ff
#define USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK 0x00d3 #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_VENDOR_ID_CHERRY 0x046a
#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 #define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
@ -343,6 +346,7 @@
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f #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_RUMBLEPAD 0xc211
#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 #define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218
@ -354,6 +358,7 @@
#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 #define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 #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_LOGITECH_ELITE_KBD 0xc30a
#define USB_DEVICE_ID_S510_RECEIVER 0xc50c #define USB_DEVICE_ID_S510_RECEIVER 0xc50c
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
@ -466,6 +471,8 @@
#define USB_VENDOR_ID_ROCCAT 0x1e7d #define USB_VENDOR_ID_ROCCAT 0x1e7d
#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #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_VENDOR_ID_SAITEK 0x06a3
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
@ -485,6 +492,12 @@
#define USB_VENDOR_ID_STANTUM 0x1f87 #define USB_VENDOR_ID_STANTUM 0x1f87
#define USB_DEVICE_ID_MTP 0x0002 #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_VENDOR_ID_SUN 0x0430
#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab #define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
@ -514,8 +527,10 @@
#define USB_VENDOR_ID_UCLOGIC 0x5543 #define USB_VENDOR_ID_UCLOGIC 0x5543
#define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042 #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_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_VENDOR_ID_VERNIER 0x08f7
#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 #define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
@ -527,6 +542,12 @@
#define USB_VENDOR_ID_WACOM 0x056a #define USB_VENDOR_ID_WACOM 0x056a
#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81 #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_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005 #define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 #define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101

View file

@ -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, static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
struct hid_usage *usage) 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); map_key_clear(BTN_STYLUS);
break; break;
case 0x46: /* TabletPick */
map_key_clear(BTN_STYLUS2);
break;
default: goto unknown; default: goto unknown;
} }
break; break;
@ -537,6 +618,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4); 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); 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 */ /* use a larger default input buffer for MT devices */
if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0) if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0)
input_set_events_per_packet(input, 60); 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; struct hid_input *hidinput;
if (hid->quirks & HID_QUIRK_NO_INPUT_SYNC)
return;
list_for_each_entry(hidinput, &hid->inputs, list) list_for_each_entry(hidinput, &hid->inputs, list)
input_sync(hidinput->input); input_sync(hidinput->input);
} }

View file

@ -23,10 +23,10 @@
* - report size 8 count 1 must be size 1 count 8 for button bitfield * - 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 * - change the button usage range to 4-7 for the extra buttons
*/ */
static void kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) unsigned int *rsize)
{ {
if (rsize >= 74 && if (*rsize >= 74 &&
rdesc[61] == 0x05 && rdesc[62] == 0x08 && rdesc[61] == 0x05 && rdesc[62] == 0x08 &&
rdesc[63] == 0x19 && rdesc[64] == 0x08 && rdesc[63] == 0x19 && rdesc[64] == 0x08 &&
rdesc[65] == 0x29 && rdesc[66] == 0x0f && 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[72] = 0x01;
rdesc[74] = 0x08; rdesc[74] = 0x08;
} }
return rdesc;
} }
static const struct hid_device_id kye_devices[] = { static const struct hid_device_id kye_devices[] = {

View file

@ -7,6 +7,7 @@
* Copyright (c) 2006-2007 Jiri Kosina * Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby * Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2010 Hendrik Iben
*/ */
/* /*
@ -19,6 +20,9 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/random.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include "hid-ids.h" #include "hid-ids.h"
#include "hid-lg.h" #include "hid-lg.h"
@ -35,31 +39,43 @@
#define LG_FF2 0x400 #define LG_FF2 0x400
#define LG_RDESC_REL_ABS 0x800 #define LG_RDESC_REL_ABS 0x800
#define LG_FF3 0x1000 #define LG_FF3 0x1000
#define LG_FF4 0x2000
/* /*
* Certain Logitech keyboards send in report #3 keys which are far * Certain Logitech keyboards send in report #3 keys which are far
* above the logical maximum described in descriptor. This extends * above the logical maximum described in descriptor. This extends
* the original value of 0x28c of logical maximum to 0x104d * the original value of 0x28c of logical maximum to 0x104d
*/ */
static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) unsigned int *rsize)
{ {
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); 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) { rdesc[84] == 0x8c && rdesc[85] == 0x02) {
dev_info(&hdev->dev, "fixing up Logitech keyboard report " dev_info(&hdev->dev, "fixing up Logitech keyboard report "
"descriptor\n"); "descriptor\n");
rdesc[84] = rdesc[89] = 0x4d; rdesc[84] = rdesc[89] = 0x4d;
rdesc[85] = rdesc[90] = 0x10; 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[32] == 0x81 && rdesc[33] == 0x06 &&
rdesc[49] == 0x81 && rdesc[50] == 0x06) { rdesc[49] == 0x81 && rdesc[50] == 0x06) {
dev_info(&hdev->dev, "fixing up rel/abs in Logitech " dev_info(&hdev->dev, "fixing up rel/abs in Logitech "
"report descriptor\n"); "report descriptor\n");
rdesc[33] = rdesc[50] = 0x02; 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, \ #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; 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) if (quirks & LG_FF)
lgff_init(hdev); lgff_init(hdev);
if (quirks & LG_FF2) if (quirks & LG_FF2)
lg2ff_init(hdev); lg2ff_init(hdev);
if (quirks & LG_FF3) if (quirks & LG_FF3)
lg3ff_init(hdev); lg3ff_init(hdev);
if (quirks & LG_FF4)
lg4ff_init(hdev);
return 0; return 0;
err_free: 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), { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
.driver_data = LG_NOGET | LG_FF }, .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), { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
.driver_data = LG_FF }, .driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2), { 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 }, .driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
.driver_data = LG_FF }, .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 ), { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
.driver_data = LG_FF }, .driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),

View file

@ -19,4 +19,10 @@ int lg3ff_init(struct hid_device *hdev);
static inline int lg3ff_init(struct hid_device *hdev) { return -1; } static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
#endif #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 #endif

View file

@ -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> * 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); 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"); "Anssi Hannula <anssi.hannula@gmail.com>\n");
return 0; return 0;

136
drivers/hid/hid-lg4ff.c Normal file
View 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;
}

View file

@ -2,6 +2,7 @@
* Apple "Magic" Wireless Mouse driver * Apple "Magic" Wireless Mouse driver
* *
* Copyright (c) 2010 Michael Poole <mdpoole@troilus.org> * 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_param(report_undeciphered, bool, 0644);
MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event"); 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 /* These definitions are not precise, but they're close enough. (Bits
* 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem * 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, * 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 #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. * struct magicmouse_sc - Tracks Magic Mouse-specific data.
* @input: Input device through which we report events. * @input: Input device through which we report events.
* @quirks: Currently unused. * @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. * @ntouches: Number of touches in most recent touch report.
* @scroll_accel: Number of consecutive scroll motions. * @scroll_accel: Number of consecutive scroll motions.
* @scroll_jiffies: Time of last scroll motion. * @scroll_jiffies: Time of last scroll motion.
@ -86,8 +93,6 @@ struct magicmouse_sc {
struct input_dev *input; struct input_dev *input;
unsigned long quirks; unsigned long quirks;
int last_timestamp;
int delta_time;
int ntouches; int ntouches;
int scroll_accel; int scroll_accel;
unsigned long scroll_jiffies; unsigned long scroll_jiffies;
@ -98,9 +103,9 @@ struct magicmouse_sc {
short scroll_x; short scroll_x;
short scroll_y; short scroll_y;
u8 size; u8 size;
u8 down;
} touches[16]; } touches[16];
int tracking_ids[16]; int tracking_ids[16];
int single_touch_id;
}; };
static int magicmouse_firm_touch(struct magicmouse_sc *msc) 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) static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata)
{ {
struct input_dev *input = msc->input; struct input_dev *input = msc->input;
__s32 x_y = tdata[0] << 8 | tdata[1] << 16 | tdata[2] << 24; int id, x, y, size, orientation, touch_major, touch_minor, state, down;
int misc = tdata[5] | tdata[6] << 8;
int id = (misc >> 6) & 15; if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
int x = x_y << 12 >> 20; id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
int y = -(x_y >> 20); x = (tdata[1] << 28 | tdata[0] << 20) >> 20;
int down = (tdata[7] & TOUCH_STATE_MASK) != TOUCH_STATE_NONE; 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. */ /* Store tracking ID and other fields. */
msc->tracking_ids[raw_id] = id; msc->tracking_ids[raw_id] = id;
msc->touches[id].x = x; msc->touches[id].x = x;
msc->touches[id].y = y; 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 /* If requested, emulate a scroll wheel by detecting small
* vertical touch motions. * 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; int step_y = msc->touches[id].scroll_y - y;
/* Calculate and apply the scroll motion. */ /* Calculate and apply the scroll motion. */
switch (tdata[7] & TOUCH_STATE_MASK) { switch (state) {
case TOUCH_STATE_START: case TOUCH_STATE_START:
msc->touches[id].scroll_x = x; msc->touches[id].scroll_x = x;
msc->touches[id].scroll_y = y; 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. */ /* Generate the input events for this touch. */
if (report_touches && down) { 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_TRACKING_ID, id);
input_report_abs(input, ABS_MT_TOUCH_MAJOR, tdata[3]); input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2);
input_report_abs(input, ABS_MT_TOUCH_MINOR, tdata[4]); 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_ORIENTATION, orientation);
input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y); input_report_abs(input, ABS_MT_POSITION_Y, y);
if (report_undeciphered) if (report_undeciphered) {
input_event(input, EV_MSC, MSC_RAW, tdata[7]); 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); 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 magicmouse_sc *msc = hid_get_drvdata(hdev);
struct input_dev *input = msc->input; 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]) { switch (data[0]) {
case 0x10: case TRACKPAD_REPORT_ID:
if (size != 6) /* Expect four bytes of prefix, and N*9 bytes of touch data. */
if (size < 4 || ((size - 4) % 9) != 0)
return 0; return 0;
x = (__s16)(data[2] | data[3] << 8); npoints = (size - 4) / 9;
y = (__s16)(data[4] | data[5] << 8); 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]; 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; break;
case TOUCH_REPORT_ID: case MOUSE_REPORT_ID:
/* Expect six bytes of prefix, and N*8 bytes of touch data. */ /* Expect six bytes of prefix, and N*8 bytes of touch data. */
if (size < 6 || ((size - 6) % 8) != 0) if (size < 6 || ((size - 6) % 8) != 0)
return 0; return 0;
ts = data[3] >> 6 | data[4] << 2 | data[5] << 10; npoints = (size - 6) / 8;
msc->delta_time = (ts - msc->last_timestamp) & 0x3ffff; msc->ntouches = 0;
msc->last_timestamp = ts; for (ii = 0; ii < npoints; ii++)
msc->ntouches = (size - 6) / 8;
for (ii = 0; ii < msc->ntouches; ii++)
magicmouse_emit_touch(msc, ii, data + ii * 8 + 6); magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);
if (report_touches) { if (report_touches && msc->ntouches == 0)
last_up = 1; input_mt_sync(input);
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);
}
}
/* When emulating three-button mode, it is important /* When emulating three-button mode, it is important
* to have the current touch information before * 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; x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22;
y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22; y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22;
clicks = data[3]; 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; 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: default:
return 0; return 0;
} }
magicmouse_emit_buttons(msc, clicks & 3); if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
input_report_rel(input, REL_X, x); magicmouse_emit_buttons(msc, clicks & 3);
input_report_rel(input, REL_Y, y); 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); input_sync(input);
return 1; 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) 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(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); if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
__set_bit(REL_X, input->relbit); __set_bit(BTN_LEFT, input->keybit);
__set_bit(REL_Y, input->relbit); __set_bit(BTN_RIGHT, input->keybit);
if (emulate_scroll_wheel) { if (emulate_3button)
__set_bit(REL_WHEEL, input->relbit); __set_bit(BTN_MIDDLE, input->keybit);
__set_bit(REL_HWHEEL, input->relbit);
__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) { 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_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_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_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 /* Note: Touch Y position from the device is inverted relative
* to how pointer motion is reported (and relative to how USB * to how pointer motion is reported (and relative to how USB
* HID recommends the coordinates work). This driver keeps * HID recommends the coordinates work). This driver keeps
* the origin at the same position, and just uses the additive * the origin at the same position, and just uses the additive
* inverse of the reported Y. * inverse of the reported Y.
*/ */
input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, 2047, if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
4, 0); 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) { 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, static int magicmouse_probe(struct hid_device *hdev,
const struct hid_device_id *id) const struct hid_device_id *id)
{ {
__u8 feature_1[] = { 0xd7, 0x01 }; __u8 feature[] = { 0xd7, 0x01 };
__u8 feature_2[] = { 0xf8, 0x01, 0x32 };
struct input_dev *input;
struct magicmouse_sc *msc; struct magicmouse_sc *msc;
struct hid_report *report; struct hid_report *report;
int ret; int ret;
@ -398,6 +455,8 @@ static int magicmouse_probe(struct hid_device *hdev,
msc->quirks = id->driver_data; msc->quirks = id->driver_data;
hid_set_drvdata(hdev, msc); hid_set_drvdata(hdev, msc);
msc->single_touch_id = NO_TOUCHES;
ret = hid_parse(hdev); ret = hid_parse(hdev);
if (ret) { if (ret) {
dev_err(&hdev->dev, "magicmouse hid parse failed\n"); dev_err(&hdev->dev, "magicmouse hid parse failed\n");
@ -410,10 +469,22 @@ static int magicmouse_probe(struct hid_device *hdev,
goto err_free; goto err_free;
} }
/* we are handling the input ourselves */ /* We do this after hid-input is done parsing reports so that
hidinput_disconnect(hdev); * 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) { if (!report) {
dev_err(&hdev->dev, "unable to register touch report\n"); dev_err(&hdev->dev, "unable to register touch report\n");
ret = -ENOMEM; ret = -ENOMEM;
@ -421,39 +492,15 @@ static int magicmouse_probe(struct hid_device *hdev,
} }
report->size = 6; 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); HID_FEATURE_REPORT);
if (ret != sizeof(feature_1)) { if (ret != sizeof(feature)) {
dev_err(&hdev->dev, "unable to request touch data (1:%d)\n", dev_err(&hdev->dev, "unable to request touch data (%d)\n",
ret); ret);
goto err_stop_hw; 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; return 0;
err_input:
input_free_device(input);
err_stop_hw: err_stop_hw:
hid_hw_stop(hdev); hid_hw_stop(hdev);
err_free: err_free:
@ -466,13 +513,14 @@ static void magicmouse_remove(struct hid_device *hdev)
struct magicmouse_sc *msc = hid_get_drvdata(hdev); struct magicmouse_sc *msc = hid_get_drvdata(hdev);
hid_hw_stop(hdev); hid_hw_stop(hdev);
input_unregister_device(msc->input);
kfree(msc); kfree(msc);
} }
static const struct hid_device_id magic_mice[] = { static const struct hid_device_id magic_mice[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE), { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
.driver_data = 0 }, 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); MODULE_DEVICE_TABLE(hid, magic_mice);
@ -483,6 +531,7 @@ static struct hid_driver magicmouse_driver = {
.probe = magicmouse_probe, .probe = magicmouse_probe,
.remove = magicmouse_remove, .remove = magicmouse_remove,
.raw_event = magicmouse_raw_event, .raw_event = magicmouse_raw_event,
.input_mapping = magicmouse_input_mapping,
}; };
static int __init magicmouse_init(void) static int __init magicmouse_init(void)

View file

@ -33,18 +33,19 @@
* Microsoft Wireless Desktop Receiver (Model 1028) has * Microsoft Wireless Desktop Receiver (Model 1028) has
* 'Usage Min/Max' where it ought to have 'Physical Min/Max' * 'Usage Min/Max' where it ought to have 'Physical Min/Max'
*/ */
static void ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) unsigned int *rsize)
{ {
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); 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) { rdesc[559] == 0x29) {
dev_info(&hdev->dev, "fixing up Microsoft Wireless Receiver " dev_info(&hdev->dev, "fixing up Microsoft Wireless Receiver "
"Model 1028 report descriptor\n"); "Model 1028 report descriptor\n");
rdesc[557] = 0x35; rdesc[557] = 0x35;
rdesc[559] = 0x45; rdesc[559] = 0x45;
} }
return rdesc;
} }
#define ms_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ #define ms_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \

View file

@ -22,14 +22,15 @@
#include "hid-ids.h" #include "hid-ids.h"
static void mr_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *mr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) 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 " dev_info(&hdev->dev, "fixing up button/consumer in HID report "
"descriptor\n"); "descriptor\n");
rdesc[30] = 0x0c; rdesc[30] = 0x0c;
} }
return rdesc;
} }
#define mr_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ #define mr_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \

View file

@ -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, static ssize_t show_phys_width(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) 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, static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage, struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max) unsigned long **bit, int *max)
{ {
struct ntrig_data *nd = hid_get_drvdata(hdev); 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 */ /* width/height mapped on TouchMajor/TouchMinor/Orientation */
case HID_DG_WIDTH: case HID_DG_WIDTH:
hid_map_usage(hi, usage, bit, max, hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TOUCH_MAJOR); EV_ABS, ABS_MT_TOUCH_MAJOR);
return 1; return 1;
case HID_DG_HEIGHT: case HID_DG_HEIGHT:
hid_map_usage(hi, usage, bit, max, 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, input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
0, 1, 0, 0); 0, 1, 0, 0);
return 1; return 1;
} }
return 0; 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, static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage, struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max) unsigned long **bit, int *max)
{ {
/* No special mappings needed for the pen and single touch */ /* No special mappings needed for the pen and single touch */
if (field->physical) 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 * and call input_mt_sync after each point if necessary
*/ */
static int ntrig_event (struct hid_device *hid, struct hid_field *field, 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 input_dev *input = field->hidinput->input;
struct ntrig_data *nd = hid_get_drvdata(hid); 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) if (report)
usbhid_submit_report(hdev, report, USB_DIR_OUT); usbhid_submit_report(hdev, report, USB_DIR_OUT);
ntrig_report_version(hdev);
ret = sysfs_create_group(&hdev->dev.kobj, ret = sysfs_create_group(&hdev->dev.kobj,
&ntrig_attribute_group); &ntrig_attribute_group);
@ -860,7 +911,7 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
static void ntrig_remove(struct hid_device *hdev) static void ntrig_remove(struct hid_device *hdev)
{ {
sysfs_remove_group(&hdev->dev.kobj, sysfs_remove_group(&hdev->dev.kobj,
&ntrig_attribute_group); &ntrig_attribute_group);
hid_hw_stop(hdev); hid_hw_stop(hdev);
kfree(hid_get_drvdata(hdev)); kfree(hid_get_drvdata(hdev));
} }

View file

@ -19,14 +19,15 @@
#include "hid-ids.h" #include "hid-ids.h"
static void ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) 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 " dev_info(&hdev->dev, "Fixing up Ortek WKB-2000 "
"report descriptor.\n"); "report descriptor.\n");
rdesc[55] = 0x92; rdesc[55] = 0x92;
} }
return rdesc;
} }
static const struct hid_device_id ortek_devices[] = { static const struct hid_device_id ortek_devices[] = {

View file

@ -23,10 +23,10 @@
#include "hid-ids.h" #include "hid-ids.h"
/* Petalynx Maxter Remote has maximum for consumer page set too low */ /* Petalynx Maxter Remote has maximum for consumer page set too low */
static void pl_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) 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[41] == 0x00 && rdesc[59] == 0x26 &&
rdesc[60] == 0xf9 && rdesc[61] == 0x00) { rdesc[60] == 0xf9 && rdesc[61] == 0x00) {
dev_info(&hdev->dev, "fixing up Petalynx Maxter Remote report " 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[60] = 0xfa;
rdesc[40] = 0xfa; rdesc[40] = 0xfa;
} }
return rdesc;
} }
#define pl_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ #define pl_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \

View file

@ -740,10 +740,10 @@ int pcmidi_snd_terminate(struct pcmidi_snd *pm)
/* /*
* PC-MIDI report descriptor for report id is wrong. * PC-MIDI report descriptor for report id is wrong.
*/ */
static void pk_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) unsigned int *rsize)
{ {
if (rsize == 178 && if (*rsize == 178 &&
rdesc[111] == 0x06 && rdesc[112] == 0x00 && rdesc[111] == 0x06 && rdesc[112] == 0x00 &&
rdesc[113] == 0xff) { rdesc[113] == 0xff) {
dev_info(&hdev->dev, "fixing up pc-midi keyboard report " 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 */ rdesc[144] = 0x18; /* report 4: was 0x10 report count */
} }
return rdesc;
} }
static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi, static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,

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

View 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

View file

@ -61,10 +61,10 @@ static inline void samsung_irda_dev_trace(struct hid_device *hdev,
"descriptor\n", rsize); "descriptor\n", rsize);
} }
static void samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) 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[177] == 0x75 && rdesc[178] == 0x30 &&
rdesc[179] == 0x95 && rdesc[180] == 0x01 && rdesc[179] == 0x95 && rdesc[180] == 0x01 &&
rdesc[182] == 0x40) { rdesc[182] == 0x40) {
@ -74,24 +74,25 @@ static void samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,
rdesc[180] = 0x06; rdesc[180] = 0x06;
rdesc[182] = 0x42; rdesc[182] = 0x42;
} else } 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) { rdesc[194] == 0x25 && rdesc[195] == 0x12) {
samsung_irda_dev_trace(hdev, 203); samsung_irda_dev_trace(hdev, 203);
rdesc[193] = 0x1; rdesc[193] = 0x1;
rdesc[195] = 0xf; rdesc[195] = 0xf;
} else } 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) { rdesc[126] == 0x25 && rdesc[127] == 0x11) {
samsung_irda_dev_trace(hdev, 135); samsung_irda_dev_trace(hdev, 135);
rdesc[125] = 0x1; rdesc[125] = 0x1;
rdesc[127] = 0xe; rdesc[127] = 0xe;
} else } 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) { rdesc[162] == 0x25 && rdesc[163] == 0x01) {
samsung_irda_dev_trace(hdev, 171); samsung_irda_dev_trace(hdev, 171);
rdesc[161] = 0x1; rdesc[161] = 0x1;
rdesc[163] = 0x3; rdesc[163] = 0x3;
} }
return rdesc;
} }
#define samsung_kbd_mouse_map_key_clear(c) \ #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; return 1;
} }
static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) unsigned int *rsize)
{ {
if (USB_DEVICE_ID_SAMSUNG_IR_REMOTE == hdev->product) 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, static int samsung_input_mapping(struct hid_device *hdev, struct hid_input *hi,

View file

@ -24,24 +24,46 @@
#include "hid-ids.h" #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 { struct sony_sc {
unsigned long quirks; unsigned long quirks;
}; };
/* Sony Vaio VGX has wrongly mouse pointer declared as constant */ /* Sony Vaio VGX has wrongly mouse pointer declared as constant */
static void sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) unsigned int *rsize)
{ {
struct sony_sc *sc = hid_get_drvdata(hdev); struct sony_sc *sc = hid_get_drvdata(hdev);
if ((sc->quirks & VAIO_RDESC_CONSTANT) && 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 " dev_info(&hdev->dev, "Fixing up Sony Vaio VGX report "
"descriptor\n"); "descriptor\n");
rdesc[55] = 0x06; 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 * to "operational". Without this, the ps3 controller will not report any
* events. * 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_interface *intf = to_usb_interface(hdev->dev.parent);
struct usb_device *dev = interface_to_usbdev(intf); struct usb_device *dev = interface_to_usbdev(intf);
@ -74,7 +96,7 @@ static int sony_set_operational_usb(struct hid_device *hdev)
return ret; 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 }; unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); 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; goto err_free;
} }
switch (hdev->bus) { if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
case BUS_USB: hdev->hid_output_raw_report = sixaxis_usb_output_raw_report;
ret = sony_set_operational_usb(hdev); ret = sixaxis_set_operational_usb(hdev);
break;
case BUS_BLUETOOTH:
ret = sony_set_operational_bt(hdev);
break;
default:
ret = 0;
} }
else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
ret = sixaxis_set_operational_bt(hdev);
else
ret = 0;
if (ret < 0) if (ret < 0)
goto err_stop; goto err_stop;
@ -137,8 +157,10 @@ static void sony_remove(struct hid_device *hdev)
} }
static const struct hid_device_id sony_devices[] = { static const struct hid_device_id sony_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { 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) }, .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), { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE),
.driver_data = VAIO_RDESC_CONSTANT }, .driver_data = VAIO_RDESC_CONSTANT },
{ } { }

View file

@ -249,6 +249,8 @@ static void stantum_remove(struct hid_device *hdev)
static const struct hid_device_id stantum_devices[] = { 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, 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); MODULE_DEVICE_TABLE(hid, stantum_devices);

View file

@ -22,16 +22,17 @@
#include "hid-ids.h" #include "hid-ids.h"
static void sp_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *sp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) unsigned int *rsize)
{ {
if (rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 && if (*rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 &&
rdesc[106] == 0x03) { rdesc[106] == 0x03) {
dev_info(&hdev->dev, "fixing up Sunplus Wireless Desktop " dev_info(&hdev->dev, "fixing up Sunplus Wireless Desktop "
"report descriptor\n"); "report descriptor\n");
rdesc[105] = rdesc[110] = 0x03; rdesc[105] = rdesc[110] = 0x03;
rdesc[106] = rdesc[111] = 0x21; rdesc[106] = rdesc[111] = 0x21;
} }
return rdesc;
} }
#define sp_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ #define sp_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \

623
drivers/hid/hid-uclogic.c Normal file
View 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

File diff suppressed because it is too large Load diff

View file

@ -27,10 +27,10 @@ struct zc_device {
* Zydacron remote control has an invalid HID report descriptor, * Zydacron remote control has an invalid HID report descriptor,
* that needs fixing before we can parse it. * that needs fixing before we can parse it.
*/ */
static void zc_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) unsigned int *rsize)
{ {
if (rsize >= 253 && if (*rsize >= 253 &&
rdesc[0x96] == 0xbc && rdesc[0x97] == 0xff && rdesc[0x96] == 0xbc && rdesc[0x97] == 0xff &&
rdesc[0xca] == 0xbc && rdesc[0xcb] == 0xff && rdesc[0xca] == 0xbc && rdesc[0xcb] == 0xff &&
rdesc[0xe1] == 0xbc && rdesc[0xe2] == 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[0x96] = rdesc[0xca] = rdesc[0xe1] = 0x0c;
rdesc[0x97] = rdesc[0xcb] = rdesc[0xe2] = 0x00; rdesc[0x97] = rdesc[0xcb] = rdesc[0xe2] = 0x00;
} }
return rdesc;
} }
#define zc_map_key_clear(c) \ #define zc_map_key_clear(c) \

View file

@ -218,9 +218,13 @@ static int hidraw_release(struct inode * inode, struct file * file)
unsigned int minor = iminor(inode); unsigned int minor = iminor(inode);
struct hidraw *dev; struct hidraw *dev;
struct hidraw_list *list = file->private_data; struct hidraw_list *list = file->private_data;
int ret;
if (!hidraw_table[minor]) mutex_lock(&minors_lock);
return -ENODEV; if (!hidraw_table[minor]) {
ret = -ENODEV;
goto unlock;
}
list_del(&list->node); list_del(&list->node);
dev = hidraw_table[minor]; dev = hidraw_table[minor];
@ -233,10 +237,12 @@ static int hidraw_release(struct inode * inode, struct file * file)
kfree(list->hidraw); kfree(list->hidraw);
} }
} }
kfree(list); kfree(list);
ret = 0;
unlock:
mutex_unlock(&minors_lock);
return 0; return ret;
} }
static long hidraw_ioctl(struct file *file, unsigned int cmd, static long hidraw_ioctl(struct file *file, unsigned int cmd,

View file

@ -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; struct usb_host_interface *interface = intf->cur_altsetting;
int ret; int ret;
if (usbhid->urbout) { if (usbhid->urbout && report_type != HID_FEATURE_REPORT) {
int actual_length; int actual_length;
int skipped_report_id = 0; int skipped_report_id = 0;
if (buf[0] == 0x0) { if (buf[0] == 0x0) {
/* Don't send the Report ID */ /* Don't send the Report ID */
buf++; buf++;
@ -1469,9 +1470,6 @@ static int __init hid_init(void)
retval = usbhid_quirks_init(quirks_param); retval = usbhid_quirks_init(quirks_param);
if (retval) if (retval)
goto usbhid_quirks_init_fail; goto usbhid_quirks_init_fail;
retval = hiddev_init();
if (retval)
goto hiddev_init_fail;
retval = usb_register(&hid_driver); retval = usb_register(&hid_driver);
if (retval) if (retval)
goto usb_register_fail; goto usb_register_fail;
@ -1479,8 +1477,6 @@ static int __init hid_init(void)
return 0; return 0;
usb_register_fail: usb_register_fail:
hiddev_exit();
hiddev_init_fail:
usbhid_quirks_exit(); usbhid_quirks_exit();
usbhid_quirks_init_fail: usbhid_quirks_init_fail:
hid_unregister_driver(&hid_usb_driver); hid_unregister_driver(&hid_usb_driver);
@ -1493,7 +1489,6 @@ static int __init hid_init(void)
static void __exit hid_exit(void) static void __exit hid_exit(void)
{ {
usb_deregister(&hid_driver); usb_deregister(&hid_driver);
hiddev_exit();
usbhid_quirks_exit(); usbhid_quirks_exit();
hid_unregister_driver(&hid_usb_driver); hid_unregister_driver(&hid_usb_driver);
destroy_workqueue(resumption_waker); destroy_workqueue(resumption_waker);

View file

@ -34,7 +34,6 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, { 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_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_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_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_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 }, { 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_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_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_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_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, 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 }, { 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_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_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_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_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 }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },

View file

@ -67,8 +67,6 @@ struct hiddev_list {
struct mutex thread_lock; 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 * 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 * 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); 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);
}

View file

@ -316,6 +316,7 @@ struct hid_item {
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000 #define HID_QUIRK_NO_INIT_REPORTS 0x20000000
#define HID_QUIRK_NO_IGNORE 0x40000000 #define HID_QUIRK_NO_IGNORE 0x40000000
#define HID_QUIRK_NO_INPUT_SYNC 0x80000000
/* /*
* This is the global environment of the parser. This information is * 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, int (*event)(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value); struct hid_usage *usage, __s32 value);
void (*report_fixup)(struct hid_device *hdev, __u8 *buf, __u8 *(*report_fixup)(struct hid_device *hdev, __u8 *buf,
unsigned int size); unsigned int *size);
int (*input_mapping)(struct hid_device *hdev, int (*input_mapping)(struct hid_device *hdev,
struct hid_input *hidinput, struct hid_field *field, struct hid_input *hidinput, struct hid_field *field,

View file

@ -226,8 +226,6 @@ void hiddev_disconnect(struct hid_device *);
void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value); struct hid_usage *usage, __s32 value);
void hiddev_report_event(struct hid_device *hid, struct hid_report *report); void hiddev_report_event(struct hid_device *hid, struct hid_report *report);
int __init hiddev_init(void);
void hiddev_exit(void);
#else #else
static inline int hiddev_connect(struct hid_device *hid, static inline int hiddev_connect(struct hid_device *hid,
unsigned int force) 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, static inline void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value) { } struct hid_usage *usage, __s32 value) { }
static inline void hiddev_report_event(struct hid_device *hid, struct hid_report *report) { } 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
#endif #endif