Commit bbd128b5acae85b2ef346e95cc5a729ac5252f19
Exists in
master
and in
7 other branches
Merge branches '3m', 'egalax', 'logitech', 'magicmouse', 'ntrig' and 'roccat' into for-linus
Showing 20 changed files Side-by-side Diff
- Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra
- Documentation/input/ntrig.txt
- drivers/hid/Kconfig
- drivers/hid/Makefile
- drivers/hid/hid-3m-pct.c
- drivers/hid/hid-core.c
- drivers/hid/hid-egalax.c
- drivers/hid/hid-ids.h
- drivers/hid/hid-input.c
- drivers/hid/hid-lg.c
- drivers/hid/hid-lg.h
- drivers/hid/hid-lg2ff.c
- drivers/hid/hid-lg4ff.c
- drivers/hid/hid-magicmouse.c
- drivers/hid/hid-ntrig.c
- drivers/hid/hid-roccat-pyra.c
- drivers/hid/hid-roccat-pyra.h
- drivers/hid/usbhid/hid-core.c
- drivers/hid/usbhid/hid-quirks.c
- include/linux/hid.h
Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra
1 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_cpi | |
2 | +Date: August 2010 | |
3 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
4 | +Description: It is possible to switch the cpi setting of the mouse with the | |
5 | + press of a button. | |
6 | + When read, this file returns the raw number of the actual cpi | |
7 | + setting reported by the mouse. This number has to be further | |
8 | + processed to receive the real dpi value. | |
9 | + | |
10 | + VALUE DPI | |
11 | + 1 400 | |
12 | + 2 800 | |
13 | + 4 1600 | |
14 | + | |
15 | + This file is readonly. | |
16 | + | |
17 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_profile | |
18 | +Date: August 2010 | |
19 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
20 | +Description: When read, this file returns the number of the actual profile in | |
21 | + range 0-4. | |
22 | + This file is readonly. | |
23 | + | |
24 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/firmware_version | |
25 | +Date: August 2010 | |
26 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
27 | +Description: When read, this file returns the raw integer version number of the | |
28 | + firmware reported by the mouse. Using the integer value eases | |
29 | + further usage in other programs. To receive the real version | |
30 | + number the decimal point has to be shifted 2 positions to the | |
31 | + left. E.g. a returned value of 138 means 1.38 | |
32 | + This file is readonly. | |
33 | + | |
34 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile_settings | |
35 | +Date: August 2010 | |
36 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
37 | +Description: The mouse can store 5 profiles which can be switched by the | |
38 | + press of a button. A profile is split in settings and buttons. | |
39 | + profile_settings holds informations like resolution, sensitivity | |
40 | + and light effects. | |
41 | + When written, this file lets one write the respective profile | |
42 | + settings back to the mouse. The data has to be 13 bytes long. | |
43 | + The mouse will reject invalid data. | |
44 | + Which profile to write is determined by the profile number | |
45 | + contained in the data. | |
46 | + This file is writeonly. | |
47 | + | |
48 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5]_settings | |
49 | +Date: August 2010 | |
50 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
51 | +Description: The mouse can store 5 profiles which can be switched by the | |
52 | + press of a button. A profile is split in settings and buttons. | |
53 | + profile_settings holds informations like resolution, sensitivity | |
54 | + and light effects. | |
55 | + When read, these files return the respective profile settings. | |
56 | + The returned data is 13 bytes in size. | |
57 | + This file is readonly. | |
58 | + | |
59 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile_buttons | |
60 | +Date: August 2010 | |
61 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
62 | +Description: The mouse can store 5 profiles which can be switched by the | |
63 | + press of a button. A profile is split in settings and buttons. | |
64 | + profile_buttons holds informations about button layout. | |
65 | + When written, this file lets one write the respective profile | |
66 | + buttons back to the mouse. The data has to be 19 bytes long. | |
67 | + The mouse will reject invalid data. | |
68 | + Which profile to write is determined by the profile number | |
69 | + contained in the data. | |
70 | + This file is writeonly. | |
71 | + | |
72 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5]_buttons | |
73 | +Date: August 2010 | |
74 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
75 | +Description: The mouse can store 5 profiles which can be switched by the | |
76 | + press of a button. A profile is split in settings and buttons. | |
77 | + profile_buttons holds informations about button layout. | |
78 | + When read, these files return the respective profile buttons. | |
79 | + The returned data is 19 bytes in size. | |
80 | + This file is readonly. | |
81 | + | |
82 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/startup_profile | |
83 | +Date: August 2010 | |
84 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
85 | +Description: The integer value of this attribute ranges from 0-4. | |
86 | + When read, this attribute returns the number of the profile | |
87 | + that's active when the mouse is powered on. | |
88 | + This file is readonly. | |
89 | + | |
90 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/settings | |
91 | +Date: August 2010 | |
92 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
93 | +Description: When read, this file returns the settings stored in the mouse. | |
94 | + The size of the data is 3 bytes and holds information on the | |
95 | + startup_profile. | |
96 | + When written, this file lets write settings back to the mouse. | |
97 | + The data has to be 3 bytes long. The mouse will reject invalid | |
98 | + data. |
Documentation/input/ntrig.txt
1 | +N-Trig touchscreen Driver | |
2 | +------------------------- | |
3 | + Copyright (c) 2008-2010 Rafi Rubin <rafi@seas.upenn.edu> | |
4 | + Copyright (c) 2009-2010 Stephane Chatty | |
5 | + | |
6 | +This driver provides support for N-Trig pen and multi-touch sensors. Single | |
7 | +and multi-touch events are translated to the appropriate protocols for | |
8 | +the hid and input systems. Pen events are sufficiently hid compliant and | |
9 | +are left to the hid core. The driver also provides additional filtering | |
10 | +and utility functions accessible with sysfs and module parameters. | |
11 | + | |
12 | +This driver has been reported to work properly with multiple N-Trig devices | |
13 | +attached. | |
14 | + | |
15 | + | |
16 | +Parameters | |
17 | +---------- | |
18 | + | |
19 | +Note: values set at load time are global and will apply to all applicable | |
20 | +devices. Adjusting parameters with sysfs will override the load time values, | |
21 | +but only for that one device. | |
22 | + | |
23 | +The following parameters are used to configure filters to reduce noise: | |
24 | + | |
25 | +activate_slack number of fingers to ignore before processing events | |
26 | + | |
27 | +activation_height size threshold to activate immediately | |
28 | +activation_width | |
29 | + | |
30 | +min_height size threshold bellow which fingers are ignored | |
31 | +min_width both to decide activation and during activity | |
32 | + | |
33 | +deactivate_slack the number of "no contact" frames to ignore before | |
34 | + propagating the end of activity events | |
35 | + | |
36 | +When the last finger is removed from the device, it sends a number of empty | |
37 | +frames. By holding off on deactivation for a few frames we can tolerate false | |
38 | +erroneous disconnects, where the sensor may mistakenly not detect a finger that | |
39 | +is still present. Thus deactivate_slack addresses problems where a users might | |
40 | +see breaks in lines during drawing, or drop an object during a long drag. | |
41 | + | |
42 | + | |
43 | +Additional sysfs items | |
44 | +---------------------- | |
45 | + | |
46 | +These nodes just provide easy access to the ranges reported by the device. | |
47 | +sensor_logical_height the range for positions reported during activity | |
48 | +sensor_logical_width | |
49 | + | |
50 | +sensor_physical_height internal ranges not used for normal events but | |
51 | +sensor_physical_width useful for tuning | |
52 | + | |
53 | +All N-Trig devices with product id of 1 report events in the ranges of | |
54 | +X: 0-9600 | |
55 | +Y: 0-7200 | |
56 | +However not all of these devices have the same physical dimensions. Most | |
57 | +seem to be 12" sensors (Dell Latitude XT and XT2 and the HP TX2), and | |
58 | +at least one model (Dell Studio 17) has a 17" sensor. The ratio of physical | |
59 | +to logical sizes is used to adjust the size based filter parameters. | |
60 | + | |
61 | + | |
62 | +Filtering | |
63 | +--------- | |
64 | + | |
65 | +With the release of the early multi-touch firmwares it became increasingly | |
66 | +obvious that these sensors were prone to erroneous events. Users reported | |
67 | +seeing both inappropriately dropped contact and ghosts, contacts reported | |
68 | +where no finger was actually touching the screen. | |
69 | + | |
70 | +Deactivation slack helps prevent dropped contact for single touch use, but does | |
71 | +not address the problem of dropping one of more contacts while other contacts | |
72 | +are still active. Drops in the multi-touch context require additional | |
73 | +processing and should be handled in tandem with tacking. | |
74 | + | |
75 | +As observed ghost contacts are similar to actual use of the sensor, but they | |
76 | +seem to have different profiles. Ghost activity typically shows up as small | |
77 | +short lived touches. As such, I assume that the longer the continuous stream | |
78 | +of events the more likely those events are from a real contact, and that the | |
79 | +larger the size of each contact the more likely it is real. Balancing the | |
80 | +goals of preventing ghosts and accepting real events quickly (to minimize | |
81 | +user observable latency), the filter accumulates confidence for incoming | |
82 | +events until it hits thresholds and begins propagating. In the interest in | |
83 | +minimizing stored state as well as the cost of operations to make a decision, | |
84 | +I've kept that decision simple. | |
85 | + | |
86 | +Time is measured in terms of the number of fingers reported, not frames since | |
87 | +the probability of multiple simultaneous ghosts is expected to drop off | |
88 | +dramatically with increasing numbers. Rather than accumulate weight as a | |
89 | +function of size, I just use it as a binary threshold. A sufficiently large | |
90 | +contact immediately overrides the waiting period and leads to activation. | |
91 | + | |
92 | +Setting the activation size thresholds to large values will result in deciding | |
93 | +primarily on activation slack. If you see longer lived ghosts, turning up the | |
94 | +activation slack while reducing the size thresholds may suffice to eliminate | |
95 | +the ghosts while keeping the screen quite responsive to firm taps. | |
96 | + | |
97 | +Contacts continue to be filtered with min_height and min_width even after | |
98 | +the initial activation filter is satisfied. The intent is to provide | |
99 | +a mechanism for filtering out ghosts in the form of an extra finger while | |
100 | +you actually are using the screen. In practice this sort of ghost has | |
101 | +been far less problematic or relatively rare and I've left the defaults | |
102 | +set to 0 for both parameters, effectively turning off that filter. | |
103 | + | |
104 | +I don't know what the optimal values are for these filters. If the defaults | |
105 | +don't work for you, please play with the parameters. If you do find other | |
106 | +values more comfortable, I would appreciate feedback. | |
107 | + | |
108 | +The calibration of these devices does drift over time. If ghosts or contact | |
109 | +dropping worsen and interfere with the normal usage of your device, try | |
110 | +recalibrating it. | |
111 | + | |
112 | + | |
113 | +Calibration | |
114 | +----------- | |
115 | + | |
116 | +The N-Trig windows tools provide calibration and testing routines. Also an | |
117 | +unofficial unsupported set of user space tools including a calibrator is | |
118 | +available at: | |
119 | +http://code.launchpad.net/~rafi-seas/+junk/ntrig_calib | |
120 | + | |
121 | + | |
122 | +Tracking | |
123 | +-------- | |
124 | + | |
125 | +As of yet, all tested N-Trig firmwares do not track fingers. When multiple | |
126 | +contacts are active they seem to be sorted primarily by Y position. |
drivers/hid/Kconfig
... | ... | @@ -220,12 +220,12 @@ |
220 | 220 | force feedback. |
221 | 221 | |
222 | 222 | config LOGIRUMBLEPAD2_FF |
223 | - bool "Logitech Rumblepad 2 force feedback support" | |
223 | + bool "Logitech RumblePad/Rumblepad 2 force feedback support" | |
224 | 224 | depends on HID_LOGITECH |
225 | 225 | select INPUT_FF_MEMLESS |
226 | 226 | help |
227 | 227 | Say Y here if you want to enable force feedback support for Logitech |
228 | - Rumblepad 2 devices. | |
228 | + RumblePad and Rumblepad 2 devices. | |
229 | 229 | |
230 | 230 | config LOGIG940_FF |
231 | 231 | bool "Logitech Flight System G940 force feedback support" |
... | ... | @@ -235,6 +235,14 @@ |
235 | 235 | Say Y here if you want to enable force feedback support for Logitech |
236 | 236 | Flight System G940 devices. |
237 | 237 | |
238 | +config LOGIWII_FF | |
239 | + bool "Logitech Speed Force Wireless force feedback support" | |
240 | + depends on HID_LOGITECH | |
241 | + select INPUT_FF_MEMLESS | |
242 | + help | |
243 | + Say Y here if you want to enable force feedback support for Logitech | |
244 | + Speed Force Wireless (Wii) devices. | |
245 | + | |
238 | 246 | config HID_MAGICMOUSE |
239 | 247 | tristate "Apple MagicMouse multi-touch support" |
240 | 248 | depends on BT_HIDP |
... | ... | @@ -375,6 +383,13 @@ |
375 | 383 | select HID_ROCCAT |
376 | 384 | ---help--- |
377 | 385 | Support for Roccat Kone mouse. |
386 | + | |
387 | +config HID_ROCCAT_PYRA | |
388 | + tristate "Roccat Pyra mouse support" | |
389 | + depends on USB_HID | |
390 | + select HID_ROCCAT | |
391 | + ---help--- | |
392 | + Support for Roccat Pyra mouse. | |
378 | 393 | |
379 | 394 | config HID_SAMSUNG |
380 | 395 | tristate "Samsung InfraRed remote control or keyboards" |
drivers/hid/Makefile
... | ... | @@ -21,6 +21,9 @@ |
21 | 21 | ifdef CONFIG_LOGIG940_FF |
22 | 22 | hid-logitech-objs += hid-lg3ff.o |
23 | 23 | endif |
24 | +ifdef CONFIG_LOGIWII_FF | |
25 | + hid-logitech-objs += hid-lg4ff.o | |
26 | +endif | |
24 | 27 | |
25 | 28 | obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o |
26 | 29 | obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o |
... | ... | @@ -52,6 +55,7 @@ |
52 | 55 | obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o |
53 | 56 | obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o |
54 | 57 | obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o |
58 | +obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o | |
55 | 59 | obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o |
56 | 60 | obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o |
57 | 61 | obj-$(CONFIG_HID_SONY) += hid-sony.o |
drivers/hid/hid-3m-pct.c
... | ... | @@ -2,6 +2,8 @@ |
2 | 2 | * HID driver for 3M PCT multitouch panels |
3 | 3 | * |
4 | 4 | * Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr> |
5 | + * Copyright (c) 2010 Henrik Rydberg <rydberg@euromail.se> | |
6 | + * Copyright (c) 2010 Canonical, Ltd. | |
5 | 7 | * |
6 | 8 | */ |
7 | 9 | |
8 | 10 | |
9 | 11 | |
... | ... | @@ -24,15 +26,26 @@ |
24 | 26 | |
25 | 27 | #include "hid-ids.h" |
26 | 28 | |
29 | +#define MAX_SLOTS 60 | |
30 | +#define MAX_TRKID USHRT_MAX | |
31 | +#define MAX_EVENTS 360 | |
32 | + | |
33 | +/* estimated signal-to-noise ratios */ | |
34 | +#define SN_MOVE 2048 | |
35 | +#define SN_WIDTH 128 | |
36 | + | |
27 | 37 | struct mmm_finger { |
28 | 38 | __s32 x, y, w, h; |
29 | - __u8 rank; | |
39 | + __u16 id; | |
40 | + bool prev_touch; | |
30 | 41 | bool touch, valid; |
31 | 42 | }; |
32 | 43 | |
33 | 44 | struct mmm_data { |
34 | - struct mmm_finger f[10]; | |
35 | - __u8 curid, num; | |
45 | + struct mmm_finger f[MAX_SLOTS]; | |
46 | + __u16 id; | |
47 | + __u8 curid; | |
48 | + __u8 nexp, nreal; | |
36 | 49 | bool touch, valid; |
37 | 50 | }; |
38 | 51 | |
... | ... | @@ -40,6 +53,10 @@ |
40 | 53 | struct hid_field *field, struct hid_usage *usage, |
41 | 54 | unsigned long **bit, int *max) |
42 | 55 | { |
56 | + int f1 = field->logical_minimum; | |
57 | + int f2 = field->logical_maximum; | |
58 | + int df = f2 - f1; | |
59 | + | |
43 | 60 | switch (usage->hid & HID_USAGE_PAGE) { |
44 | 61 | |
45 | 62 | case HID_UP_BUTTON: |
46 | 63 | |
47 | 64 | |
48 | 65 | |
... | ... | @@ -50,18 +67,20 @@ |
50 | 67 | case HID_GD_X: |
51 | 68 | hid_map_usage(hi, usage, bit, max, |
52 | 69 | EV_ABS, ABS_MT_POSITION_X); |
70 | + input_set_abs_params(hi->input, ABS_MT_POSITION_X, | |
71 | + f1, f2, df / SN_MOVE, 0); | |
53 | 72 | /* touchscreen emulation */ |
54 | 73 | input_set_abs_params(hi->input, ABS_X, |
55 | - field->logical_minimum, | |
56 | - field->logical_maximum, 0, 0); | |
74 | + f1, f2, df / SN_MOVE, 0); | |
57 | 75 | return 1; |
58 | 76 | case HID_GD_Y: |
59 | 77 | hid_map_usage(hi, usage, bit, max, |
60 | 78 | EV_ABS, ABS_MT_POSITION_Y); |
79 | + input_set_abs_params(hi->input, ABS_MT_POSITION_Y, | |
80 | + f1, f2, df / SN_MOVE, 0); | |
61 | 81 | /* touchscreen emulation */ |
62 | 82 | input_set_abs_params(hi->input, ABS_Y, |
63 | - field->logical_minimum, | |
64 | - field->logical_maximum, 0, 0); | |
83 | + f1, f2, df / SN_MOVE, 0); | |
65 | 84 | return 1; |
66 | 85 | } |
67 | 86 | return 0; |
68 | 87 | |
69 | 88 | |
70 | 89 | |
71 | 90 | |
72 | 91 | |
... | ... | @@ -81,21 +100,31 @@ |
81 | 100 | case HID_DG_TIPSWITCH: |
82 | 101 | /* touchscreen emulation */ |
83 | 102 | hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); |
103 | + input_set_capability(hi->input, EV_KEY, BTN_TOUCH); | |
84 | 104 | return 1; |
85 | 105 | case HID_DG_WIDTH: |
86 | 106 | hid_map_usage(hi, usage, bit, max, |
87 | 107 | EV_ABS, ABS_MT_TOUCH_MAJOR); |
108 | + input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR, | |
109 | + f1, f2, df / SN_WIDTH, 0); | |
88 | 110 | return 1; |
89 | 111 | case HID_DG_HEIGHT: |
90 | 112 | hid_map_usage(hi, usage, bit, max, |
91 | 113 | EV_ABS, ABS_MT_TOUCH_MINOR); |
114 | + input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR, | |
115 | + f1, f2, df / SN_WIDTH, 0); | |
92 | 116 | input_set_abs_params(hi->input, ABS_MT_ORIENTATION, |
93 | - 1, 1, 0, 0); | |
117 | + 0, 1, 0, 0); | |
94 | 118 | return 1; |
95 | 119 | case HID_DG_CONTACTID: |
96 | - field->logical_maximum = 59; | |
120 | + field->logical_maximum = MAX_TRKID; | |
97 | 121 | hid_map_usage(hi, usage, bit, max, |
98 | 122 | EV_ABS, ABS_MT_TRACKING_ID); |
123 | + input_set_abs_params(hi->input, ABS_MT_TRACKING_ID, | |
124 | + 0, MAX_TRKID, 0, 0); | |
125 | + if (!hi->input->mt) | |
126 | + input_mt_create_slots(hi->input, MAX_SLOTS); | |
127 | + input_set_events_per_packet(hi->input, MAX_EVENTS); | |
99 | 128 | return 1; |
100 | 129 | } |
101 | 130 | /* let hid-input decide for the others */ |
102 | 131 | |
... | ... | @@ -113,10 +142,10 @@ |
113 | 142 | struct hid_field *field, struct hid_usage *usage, |
114 | 143 | unsigned long **bit, int *max) |
115 | 144 | { |
145 | + /* tell hid-input to skip setup of these event types */ | |
116 | 146 | if (usage->type == EV_KEY || usage->type == EV_ABS) |
117 | - clear_bit(usage->code, *bit); | |
118 | - | |
119 | - return 0; | |
147 | + set_bit(usage->type, hi->input->evbit); | |
148 | + return -1; | |
120 | 149 | } |
121 | 150 | |
122 | 151 | /* |
123 | 152 | |
124 | 153 | |
125 | 154 | |
126 | 155 | |
127 | 156 | |
128 | 157 | |
129 | 158 | |
130 | 159 | |
131 | 160 | |
... | ... | @@ -126,70 +155,49 @@ |
126 | 155 | static void mmm_filter_event(struct mmm_data *md, struct input_dev *input) |
127 | 156 | { |
128 | 157 | struct mmm_finger *oldest = 0; |
129 | - bool pressed = false, released = false; | |
130 | 158 | int i; |
131 | - | |
132 | - /* | |
133 | - * we need to iterate on all fingers to decide if we have a press | |
134 | - * or a release event in our touchscreen emulation. | |
135 | - */ | |
136 | - for (i = 0; i < 10; ++i) { | |
159 | + for (i = 0; i < MAX_SLOTS; ++i) { | |
137 | 160 | struct mmm_finger *f = &md->f[i]; |
138 | 161 | if (!f->valid) { |
139 | 162 | /* this finger is just placeholder data, ignore */ |
140 | - } else if (f->touch) { | |
163 | + continue; | |
164 | + } | |
165 | + input_mt_slot(input, i); | |
166 | + if (f->touch) { | |
141 | 167 | /* this finger is on the screen */ |
142 | 168 | int wide = (f->w > f->h); |
143 | - input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i); | |
169 | + /* divided by two to match visual scale of touch */ | |
170 | + int major = max(f->w, f->h) >> 1; | |
171 | + int minor = min(f->w, f->h) >> 1; | |
172 | + | |
173 | + if (!f->prev_touch) | |
174 | + f->id = md->id++; | |
175 | + input_event(input, EV_ABS, ABS_MT_TRACKING_ID, f->id); | |
144 | 176 | input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x); |
145 | 177 | input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); |
146 | 178 | input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); |
147 | - input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, | |
148 | - wide ? f->w : f->h); | |
149 | - input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, | |
150 | - wide ? f->h : f->w); | |
151 | - input_mt_sync(input); | |
152 | - /* | |
153 | - * touchscreen emulation: maintain the age rank | |
154 | - * of this finger, decide if we have a press | |
155 | - */ | |
156 | - if (f->rank == 0) { | |
157 | - f->rank = ++(md->num); | |
158 | - if (f->rank == 1) | |
159 | - pressed = true; | |
160 | - } | |
161 | - if (f->rank == 1) | |
179 | + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); | |
180 | + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); | |
181 | + /* touchscreen emulation: pick the oldest contact */ | |
182 | + if (!oldest || ((f->id - oldest->id) & (SHRT_MAX + 1))) | |
162 | 183 | oldest = f; |
163 | 184 | } else { |
164 | 185 | /* this finger took off the screen */ |
165 | - /* touchscreen emulation: maintain age rank of others */ | |
166 | - int j; | |
167 | - | |
168 | - for (j = 0; j < 10; ++j) { | |
169 | - struct mmm_finger *g = &md->f[j]; | |
170 | - if (g->rank > f->rank) { | |
171 | - g->rank--; | |
172 | - if (g->rank == 1) | |
173 | - oldest = g; | |
174 | - } | |
175 | - } | |
176 | - f->rank = 0; | |
177 | - --(md->num); | |
178 | - if (md->num == 0) | |
179 | - released = true; | |
186 | + input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1); | |
180 | 187 | } |
188 | + f->prev_touch = f->touch; | |
181 | 189 | f->valid = 0; |
182 | 190 | } |
183 | 191 | |
184 | 192 | /* touchscreen emulation */ |
185 | 193 | if (oldest) { |
186 | - if (pressed) | |
187 | - input_event(input, EV_KEY, BTN_TOUCH, 1); | |
194 | + input_event(input, EV_KEY, BTN_TOUCH, 1); | |
188 | 195 | input_event(input, EV_ABS, ABS_X, oldest->x); |
189 | 196 | input_event(input, EV_ABS, ABS_Y, oldest->y); |
190 | - } else if (released) { | |
197 | + } else { | |
191 | 198 | input_event(input, EV_KEY, BTN_TOUCH, 0); |
192 | 199 | } |
200 | + input_sync(input); | |
193 | 201 | } |
194 | 202 | |
195 | 203 | /* |
196 | 204 | |
... | ... | @@ -223,10 +231,12 @@ |
223 | 231 | md->f[md->curid].h = value; |
224 | 232 | break; |
225 | 233 | case HID_DG_CONTACTID: |
234 | + value = clamp_val(value, 0, MAX_SLOTS - 1); | |
226 | 235 | if (md->valid) { |
227 | 236 | md->curid = value; |
228 | 237 | md->f[value].touch = md->touch; |
229 | 238 | md->f[value].valid = 1; |
239 | + md->nreal++; | |
230 | 240 | } |
231 | 241 | break; |
232 | 242 | case HID_GD_X: |
... | ... | @@ -238,7 +248,12 @@ |
238 | 248 | md->f[md->curid].y = value; |
239 | 249 | break; |
240 | 250 | case HID_DG_CONTACTCOUNT: |
241 | - mmm_filter_event(md, input); | |
251 | + if (value) | |
252 | + md->nexp = value; | |
253 | + if (md->nreal >= md->nexp) { | |
254 | + mmm_filter_event(md, input); | |
255 | + md->nreal = 0; | |
256 | + } | |
242 | 257 | break; |
243 | 258 | } |
244 | 259 | } |
... | ... | @@ -254,6 +269,8 @@ |
254 | 269 | { |
255 | 270 | int ret; |
256 | 271 | struct mmm_data *md; |
272 | + | |
273 | + hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; | |
257 | 274 | |
258 | 275 | md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL); |
259 | 276 | if (!md) { |
drivers/hid/hid-core.c
... | ... | @@ -1249,6 +1249,7 @@ |
1249 | 1249 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, |
1250 | 1250 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, |
1251 | 1251 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) }, |
1252 | + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) }, | |
1252 | 1253 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) }, |
1253 | 1254 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) }, |
1254 | 1255 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) }, |
... | ... | @@ -1328,6 +1329,7 @@ |
1328 | 1329 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) }, |
1329 | 1330 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, |
1330 | 1331 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) }, |
1332 | + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) }, | |
1331 | 1333 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, |
1332 | 1334 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, |
1333 | 1335 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, |
... | ... | @@ -1337,6 +1339,7 @@ |
1337 | 1339 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, |
1338 | 1340 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, |
1339 | 1341 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, |
1342 | + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) }, | |
1340 | 1343 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, |
1341 | 1344 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, |
1342 | 1345 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, |
... | ... | @@ -1372,6 +1375,7 @@ |
1372 | 1375 | { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, |
1373 | 1376 | { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, |
1374 | 1377 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, |
1378 | + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, | |
1375 | 1379 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, |
1376 | 1380 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, |
1377 | 1381 | { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, |
drivers/hid/hid-egalax.c
... | ... | @@ -31,7 +31,7 @@ |
31 | 31 | bool first; /* is this the first finger in the frame? */ |
32 | 32 | bool valid; /* valid finger data, or just placeholder? */ |
33 | 33 | bool activity; /* at least one active finger previously? */ |
34 | - __u16 lastx, lasty; /* latest valid (x, y) in the frame */ | |
34 | + __u16 lastx, lasty, lastz; /* latest valid (x, y, z) in the frame */ | |
35 | 35 | }; |
36 | 36 | |
37 | 37 | static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, |
... | ... | @@ -79,6 +79,10 @@ |
79 | 79 | case HID_DG_TIPPRESSURE: |
80 | 80 | hid_map_usage(hi, usage, bit, max, |
81 | 81 | EV_ABS, ABS_MT_PRESSURE); |
82 | + /* touchscreen emulation */ | |
83 | + input_set_abs_params(hi->input, ABS_PRESSURE, | |
84 | + field->logical_minimum, | |
85 | + field->logical_maximum, 0, 0); | |
82 | 86 | return 1; |
83 | 87 | } |
84 | 88 | return 0; |
... | ... | @@ -109,8 +113,8 @@ |
109 | 113 | if (td->valid) { |
110 | 114 | /* emit multitouch events */ |
111 | 115 | input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id); |
112 | - input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x); | |
113 | - input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y); | |
116 | + input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x >> 3); | |
117 | + input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y >> 3); | |
114 | 118 | input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z); |
115 | 119 | |
116 | 120 | input_mt_sync(input); |
... | ... | @@ -121,6 +125,7 @@ |
121 | 125 | */ |
122 | 126 | td->lastx = td->x; |
123 | 127 | td->lasty = td->y; |
128 | + td->lastz = td->z; | |
124 | 129 | } |
125 | 130 | |
126 | 131 | /* |
... | ... | @@ -129,8 +134,9 @@ |
129 | 134 | * the oldest on the panel, the one we want for single touch |
130 | 135 | */ |
131 | 136 | if (!td->first && td->activity) { |
132 | - input_event(input, EV_ABS, ABS_X, td->lastx); | |
133 | - input_event(input, EV_ABS, ABS_Y, td->lasty); | |
137 | + input_event(input, EV_ABS, ABS_X, td->lastx >> 3); | |
138 | + input_event(input, EV_ABS, ABS_Y, td->lasty >> 3); | |
139 | + input_event(input, EV_ABS, ABS_PRESSURE, td->lastz); | |
134 | 140 | } |
135 | 141 | |
136 | 142 | if (!td->valid) { |
drivers/hid/hid-ids.h
... | ... | @@ -64,6 +64,7 @@ |
64 | 64 | #define USB_VENDOR_ID_APPLE 0x05ac |
65 | 65 | #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 |
66 | 66 | #define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d |
67 | +#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e | |
67 | 68 | #define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e |
68 | 69 | #define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f |
69 | 70 | #define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 |
... | ... | @@ -345,6 +346,7 @@ |
345 | 346 | #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 |
346 | 347 | #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 |
347 | 348 | #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f |
349 | +#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a | |
348 | 350 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211 |
349 | 351 | #define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 |
350 | 352 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 |
... | ... | @@ -356,6 +358,7 @@ |
356 | 358 | #define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 |
357 | 359 | #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 |
358 | 360 | #define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 |
361 | +#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c | |
359 | 362 | #define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a |
360 | 363 | #define USB_DEVICE_ID_S510_RECEIVER 0xc50c |
361 | 364 | #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 |
... | ... | @@ -468,6 +471,8 @@ |
468 | 471 | |
469 | 472 | #define USB_VENDOR_ID_ROCCAT 0x1e7d |
470 | 473 | #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced |
474 | +#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 | |
475 | +#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 | |
471 | 476 | |
472 | 477 | #define USB_VENDOR_ID_SAITEK 0x06a3 |
473 | 478 | #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 |
drivers/hid/hid-input.c
drivers/hid/hid-lg.c
... | ... | @@ -7,6 +7,7 @@ |
7 | 7 | * Copyright (c) 2006-2007 Jiri Kosina |
8 | 8 | * Copyright (c) 2007 Paul Walmsley |
9 | 9 | * Copyright (c) 2008 Jiri Slaby |
10 | + * Copyright (c) 2010 Hendrik Iben | |
10 | 11 | */ |
11 | 12 | |
12 | 13 | /* |
... | ... | @@ -19,6 +20,9 @@ |
19 | 20 | #include <linux/device.h> |
20 | 21 | #include <linux/hid.h> |
21 | 22 | #include <linux/module.h> |
23 | +#include <linux/random.h> | |
24 | +#include <linux/sched.h> | |
25 | +#include <linux/wait.h> | |
22 | 26 | |
23 | 27 | #include "hid-ids.h" |
24 | 28 | #include "hid-lg.h" |
... | ... | @@ -35,6 +39,7 @@ |
35 | 39 | #define LG_FF2 0x400 |
36 | 40 | #define LG_RDESC_REL_ABS 0x800 |
37 | 41 | #define LG_FF3 0x1000 |
42 | +#define LG_FF4 0x2000 | |
38 | 43 | |
39 | 44 | /* |
40 | 45 | * Certain Logitech keyboards send in report #3 keys which are far |
... | ... | @@ -60,6 +65,17 @@ |
60 | 65 | "report descriptor\n"); |
61 | 66 | rdesc[33] = rdesc[50] = 0x02; |
62 | 67 | } |
68 | + | |
69 | + if ((quirks & LG_FF4) && rsize >= 101 && | |
70 | + rdesc[41] == 0x95 && rdesc[42] == 0x0B && | |
71 | + rdesc[47] == 0x05 && rdesc[48] == 0x09) { | |
72 | + dev_info(&hdev->dev, "fixing up Logitech Speed Force Wireless " | |
73 | + "button descriptor\n"); | |
74 | + rdesc[41] = 0x05; | |
75 | + rdesc[42] = 0x09; | |
76 | + rdesc[47] = 0x95; | |
77 | + rdesc[48] = 0x0B; | |
78 | + } | |
63 | 79 | } |
64 | 80 | |
65 | 81 | #define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ |
66 | 82 | |
... | ... | @@ -285,12 +301,33 @@ |
285 | 301 | goto err_free; |
286 | 302 | } |
287 | 303 | |
304 | + if (quirks & LG_FF4) { | |
305 | + unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |
306 | + | |
307 | + ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); | |
308 | + | |
309 | + if (ret >= 0) { | |
310 | + /* insert a little delay of 10 jiffies ~ 40ms */ | |
311 | + wait_queue_head_t wait; | |
312 | + init_waitqueue_head (&wait); | |
313 | + wait_event_interruptible_timeout(wait, 0, 10); | |
314 | + | |
315 | + /* Select random Address */ | |
316 | + buf[1] = 0xB2; | |
317 | + get_random_bytes(&buf[2], 2); | |
318 | + | |
319 | + ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); | |
320 | + } | |
321 | + } | |
322 | + | |
288 | 323 | if (quirks & LG_FF) |
289 | 324 | lgff_init(hdev); |
290 | 325 | if (quirks & LG_FF2) |
291 | 326 | lg2ff_init(hdev); |
292 | 327 | if (quirks & LG_FF3) |
293 | 328 | lg3ff_init(hdev); |
329 | + if (quirks & LG_FF4) | |
330 | + lg4ff_init(hdev); | |
294 | 331 | |
295 | 332 | return 0; |
296 | 333 | err_free: |
... | ... | @@ -325,6 +362,8 @@ |
325 | 362 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), |
326 | 363 | .driver_data = LG_NOGET | LG_FF }, |
327 | 364 | |
365 | + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD), | |
366 | + .driver_data = LG_FF2 }, | |
328 | 367 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD), |
329 | 368 | .driver_data = LG_FF }, |
330 | 369 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2), |
... | ... | @@ -339,6 +378,8 @@ |
339 | 378 | .driver_data = LG_FF }, |
340 | 379 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), |
341 | 380 | .driver_data = LG_FF }, |
381 | + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), | |
382 | + .driver_data = LG_FF4 }, | |
342 | 383 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ), |
343 | 384 | .driver_data = LG_FF }, |
344 | 385 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), |
drivers/hid/hid-lg.h
... | ... | @@ -19,5 +19,11 @@ |
19 | 19 | static inline int lg3ff_init(struct hid_device *hdev) { return -1; } |
20 | 20 | #endif |
21 | 21 | |
22 | +#ifdef CONFIG_LOGIWII_FF | |
23 | +int lg4ff_init(struct hid_device *hdev); | |
24 | +#else | |
25 | +static inline int lg4ff_init(struct hid_device *hdev) { return -1; } | |
26 | +#endif | |
27 | + | |
22 | 28 | #endif |
drivers/hid/hid-lg2ff.c
1 | 1 | /* |
2 | - * Force feedback support for Logitech Rumblepad 2 | |
2 | + * Force feedback support for Logitech RumblePad and Rumblepad 2 | |
3 | 3 | * |
4 | 4 | * Copyright (c) 2008 Anssi Hannula <anssi.hannula@gmail.com> |
5 | 5 | */ |
... | ... | @@ -110,7 +110,7 @@ |
110 | 110 | |
111 | 111 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
112 | 112 | |
113 | - dev_info(&hid->dev, "Force feedback for Logitech Rumblepad 2 by " | |
113 | + dev_info(&hid->dev, "Force feedback for Logitech RumblePad/Rumblepad 2 by " | |
114 | 114 | "Anssi Hannula <anssi.hannula@gmail.com>\n"); |
115 | 115 | |
116 | 116 | return 0; |
drivers/hid/hid-lg4ff.c
1 | +/* | |
2 | + * Force feedback support for Logitech Speed Force Wireless | |
3 | + * | |
4 | + * http://wiibrew.org/wiki/Logitech_USB_steering_wheel | |
5 | + * | |
6 | + * Copyright (c) 2010 Simon Wood <simon@mungewell.org> | |
7 | + */ | |
8 | + | |
9 | +/* | |
10 | + * This program is free software; you can redistribute it and/or modify | |
11 | + * it under the terms of the GNU General Public License as published by | |
12 | + * the Free Software Foundation; either version 2 of the License, or | |
13 | + * (at your option) any later version. | |
14 | + * | |
15 | + * This program is distributed in the hope that it will be useful, | |
16 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | + * GNU General Public License for more details. | |
19 | + * | |
20 | + * You should have received a copy of the GNU General Public License | |
21 | + * along with this program; if not, write to the Free Software | |
22 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 | + */ | |
24 | + | |
25 | + | |
26 | +#include <linux/input.h> | |
27 | +#include <linux/usb.h> | |
28 | +#include <linux/hid.h> | |
29 | + | |
30 | +#include "usbhid/usbhid.h" | |
31 | +#include "hid-lg.h" | |
32 | + | |
33 | +struct lg4ff_device { | |
34 | + struct hid_report *report; | |
35 | +}; | |
36 | + | |
37 | +static const signed short ff4_wheel_ac[] = { | |
38 | + FF_CONSTANT, | |
39 | + FF_AUTOCENTER, | |
40 | + -1 | |
41 | +}; | |
42 | + | |
43 | +static int hid_lg4ff_play(struct input_dev *dev, void *data, | |
44 | + struct ff_effect *effect) | |
45 | +{ | |
46 | + struct hid_device *hid = input_get_drvdata(dev); | |
47 | + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | |
48 | + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | |
49 | + int x; | |
50 | + | |
51 | +#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff | |
52 | + | |
53 | + switch (effect->type) { | |
54 | + case FF_CONSTANT: | |
55 | + x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */ | |
56 | + CLAMP(x); | |
57 | + report->field[0]->value[0] = 0x11; /* Slot 1 */ | |
58 | + report->field[0]->value[1] = 0x10; | |
59 | + report->field[0]->value[2] = x; | |
60 | + report->field[0]->value[3] = 0x00; | |
61 | + report->field[0]->value[4] = 0x00; | |
62 | + report->field[0]->value[5] = 0x08; | |
63 | + report->field[0]->value[6] = 0x00; | |
64 | + dbg_hid("Autocenter, x=0x%02X\n", x); | |
65 | + | |
66 | + usbhid_submit_report(hid, report, USB_DIR_OUT); | |
67 | + break; | |
68 | + } | |
69 | + return 0; | |
70 | +} | |
71 | + | |
72 | +static void hid_lg4ff_set_autocenter(struct input_dev *dev, u16 magnitude) | |
73 | +{ | |
74 | + struct hid_device *hid = input_get_drvdata(dev); | |
75 | + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | |
76 | + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | |
77 | + __s32 *value = report->field[0]->value; | |
78 | + | |
79 | + *value++ = 0xfe; | |
80 | + *value++ = 0x0d; | |
81 | + *value++ = 0x07; | |
82 | + *value++ = 0x07; | |
83 | + *value++ = (magnitude >> 8) & 0xff; | |
84 | + *value++ = 0x00; | |
85 | + *value = 0x00; | |
86 | + | |
87 | + usbhid_submit_report(hid, report, USB_DIR_OUT); | |
88 | +} | |
89 | + | |
90 | + | |
91 | +int lg4ff_init(struct hid_device *hid) | |
92 | +{ | |
93 | + struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); | |
94 | + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | |
95 | + struct input_dev *dev = hidinput->input; | |
96 | + struct hid_report *report; | |
97 | + struct hid_field *field; | |
98 | + const signed short *ff_bits = ff4_wheel_ac; | |
99 | + int error; | |
100 | + int i; | |
101 | + | |
102 | + /* Find the report to use */ | |
103 | + if (list_empty(report_list)) { | |
104 | + err_hid("No output report found"); | |
105 | + return -1; | |
106 | + } | |
107 | + | |
108 | + /* Check that the report looks ok */ | |
109 | + report = list_entry(report_list->next, struct hid_report, list); | |
110 | + if (!report) { | |
111 | + err_hid("NULL output report"); | |
112 | + return -1; | |
113 | + } | |
114 | + | |
115 | + field = report->field[0]; | |
116 | + if (!field) { | |
117 | + err_hid("NULL field"); | |
118 | + return -1; | |
119 | + } | |
120 | + | |
121 | + for (i = 0; ff_bits[i] >= 0; i++) | |
122 | + set_bit(ff_bits[i], dev->ffbit); | |
123 | + | |
124 | + error = input_ff_create_memless(dev, NULL, hid_lg4ff_play); | |
125 | + | |
126 | + if (error) | |
127 | + return error; | |
128 | + | |
129 | + if (test_bit(FF_AUTOCENTER, dev->ffbit)) | |
130 | + dev->ff->set_autocenter = hid_lg4ff_set_autocenter; | |
131 | + | |
132 | + dev_info(&hid->dev, "Force feedback for Logitech Speed Force Wireless by " | |
133 | + "Simon Wood <simon@mungewell.org>\n"); | |
134 | + return 0; | |
135 | +} |
drivers/hid/hid-magicmouse.c
... | ... | @@ -2,6 +2,7 @@ |
2 | 2 | * Apple "Magic" Wireless Mouse driver |
3 | 3 | * |
4 | 4 | * Copyright (c) 2010 Michael Poole <mdpoole@troilus.org> |
5 | + * Copyright (c) 2010 Chase Douglas <chase.douglas@canonical.com> | |
5 | 6 | */ |
6 | 7 | |
7 | 8 | /* |
... | ... | @@ -53,7 +54,9 @@ |
53 | 54 | module_param(report_undeciphered, bool, 0644); |
54 | 55 | MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event"); |
55 | 56 | |
56 | -#define TOUCH_REPORT_ID 0x29 | |
57 | +#define TRACKPAD_REPORT_ID 0x28 | |
58 | +#define MOUSE_REPORT_ID 0x29 | |
59 | +#define DOUBLE_REPORT_ID 0xf7 | |
57 | 60 | /* These definitions are not precise, but they're close enough. (Bits |
58 | 61 | * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem |
59 | 62 | * to be some kind of bit mask -- 0x20 may be a near-field reading, |
60 | 63 | |
... | ... | @@ -67,15 +70,19 @@ |
67 | 70 | |
68 | 71 | #define SCROLL_ACCEL_DEFAULT 7 |
69 | 72 | |
73 | +/* Single touch emulation should only begin when no touches are currently down. | |
74 | + * This is true when single_touch_id is equal to NO_TOUCHES. If multiple touches | |
75 | + * are down and the touch providing for single touch emulation is lifted, | |
76 | + * single_touch_id is equal to SINGLE_TOUCH_UP. While single touch emulation is | |
77 | + * occuring, single_touch_id corresponds with the tracking id of the touch used. | |
78 | + */ | |
79 | +#define NO_TOUCHES -1 | |
80 | +#define SINGLE_TOUCH_UP -2 | |
81 | + | |
70 | 82 | /** |
71 | 83 | * struct magicmouse_sc - Tracks Magic Mouse-specific data. |
72 | 84 | * @input: Input device through which we report events. |
73 | 85 | * @quirks: Currently unused. |
74 | - * @last_timestamp: Timestamp from most recent (18-bit) touch report | |
75 | - * (units of milliseconds over short windows, but seems to | |
76 | - * increase faster when there are no touches). | |
77 | - * @delta_time: 18-bit difference between the two most recent touch | |
78 | - * reports from the mouse. | |
79 | 86 | * @ntouches: Number of touches in most recent touch report. |
80 | 87 | * @scroll_accel: Number of consecutive scroll motions. |
81 | 88 | * @scroll_jiffies: Time of last scroll motion. |
... | ... | @@ -86,8 +93,6 @@ |
86 | 93 | struct input_dev *input; |
87 | 94 | unsigned long quirks; |
88 | 95 | |
89 | - int last_timestamp; | |
90 | - int delta_time; | |
91 | 96 | int ntouches; |
92 | 97 | int scroll_accel; |
93 | 98 | unsigned long scroll_jiffies; |
94 | 99 | |
... | ... | @@ -98,9 +103,9 @@ |
98 | 103 | short scroll_x; |
99 | 104 | short scroll_y; |
100 | 105 | u8 size; |
101 | - u8 down; | |
102 | 106 | } touches[16]; |
103 | 107 | int tracking_ids[16]; |
108 | + int single_touch_id; | |
104 | 109 | }; |
105 | 110 | |
106 | 111 | static int magicmouse_firm_touch(struct magicmouse_sc *msc) |
107 | 112 | |
108 | 113 | |
... | ... | @@ -166,18 +171,35 @@ |
166 | 171 | static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata) |
167 | 172 | { |
168 | 173 | struct input_dev *input = msc->input; |
169 | - __s32 x_y = tdata[0] << 8 | tdata[1] << 16 | tdata[2] << 24; | |
170 | - int misc = tdata[5] | tdata[6] << 8; | |
171 | - int id = (misc >> 6) & 15; | |
172 | - int x = x_y << 12 >> 20; | |
173 | - int y = -(x_y >> 20); | |
174 | - int down = (tdata[7] & TOUCH_STATE_MASK) != TOUCH_STATE_NONE; | |
174 | + int id, x, y, size, orientation, touch_major, touch_minor, state, down; | |
175 | 175 | |
176 | + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { | |
177 | + id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf; | |
178 | + x = (tdata[1] << 28 | tdata[0] << 20) >> 20; | |
179 | + y = -((tdata[2] << 24 | tdata[1] << 16) >> 20); | |
180 | + size = tdata[5] & 0x3f; | |
181 | + orientation = (tdata[6] >> 2) - 32; | |
182 | + touch_major = tdata[3]; | |
183 | + touch_minor = tdata[4]; | |
184 | + state = tdata[7] & TOUCH_STATE_MASK; | |
185 | + down = state != TOUCH_STATE_NONE; | |
186 | + } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | |
187 | + id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf; | |
188 | + x = (tdata[1] << 27 | tdata[0] << 19) >> 19; | |
189 | + y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19); | |
190 | + size = tdata[6] & 0x3f; | |
191 | + orientation = (tdata[7] >> 2) - 32; | |
192 | + touch_major = tdata[4]; | |
193 | + touch_minor = tdata[5]; | |
194 | + state = tdata[8] & TOUCH_STATE_MASK; | |
195 | + down = state != TOUCH_STATE_NONE; | |
196 | + } | |
197 | + | |
176 | 198 | /* Store tracking ID and other fields. */ |
177 | 199 | msc->tracking_ids[raw_id] = id; |
178 | 200 | msc->touches[id].x = x; |
179 | 201 | msc->touches[id].y = y; |
180 | - msc->touches[id].size = misc & 63; | |
202 | + msc->touches[id].size = size; | |
181 | 203 | |
182 | 204 | /* If requested, emulate a scroll wheel by detecting small |
183 | 205 | * vertical touch motions. |
... | ... | @@ -188,7 +210,7 @@ |
188 | 210 | int step_y = msc->touches[id].scroll_y - y; |
189 | 211 | |
190 | 212 | /* Calculate and apply the scroll motion. */ |
191 | - switch (tdata[7] & TOUCH_STATE_MASK) { | |
213 | + switch (state) { | |
192 | 214 | case TOUCH_STATE_START: |
193 | 215 | msc->touches[id].scroll_x = x; |
194 | 216 | msc->touches[id].scroll_y = y; |
195 | 217 | |
196 | 218 | |
197 | 219 | |
... | ... | @@ -222,21 +244,28 @@ |
222 | 244 | } |
223 | 245 | } |
224 | 246 | |
247 | + if (down) { | |
248 | + msc->ntouches++; | |
249 | + if (msc->single_touch_id == NO_TOUCHES) | |
250 | + msc->single_touch_id = id; | |
251 | + } else if (msc->single_touch_id == id) | |
252 | + msc->single_touch_id = SINGLE_TOUCH_UP; | |
253 | + | |
225 | 254 | /* Generate the input events for this touch. */ |
226 | 255 | if (report_touches && down) { |
227 | - int orientation = (misc >> 10) - 32; | |
228 | - | |
229 | - msc->touches[id].down = 1; | |
230 | - | |
231 | 256 | input_report_abs(input, ABS_MT_TRACKING_ID, id); |
232 | - input_report_abs(input, ABS_MT_TOUCH_MAJOR, tdata[3]); | |
233 | - input_report_abs(input, ABS_MT_TOUCH_MINOR, tdata[4]); | |
257 | + input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2); | |
258 | + input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2); | |
234 | 259 | input_report_abs(input, ABS_MT_ORIENTATION, orientation); |
235 | 260 | input_report_abs(input, ABS_MT_POSITION_X, x); |
236 | 261 | input_report_abs(input, ABS_MT_POSITION_Y, y); |
237 | 262 | |
238 | - if (report_undeciphered) | |
239 | - input_event(input, EV_MSC, MSC_RAW, tdata[7]); | |
263 | + if (report_undeciphered) { | |
264 | + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) | |
265 | + input_event(input, EV_MSC, MSC_RAW, tdata[7]); | |
266 | + else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | |
267 | + input_event(input, EV_MSC, MSC_RAW, tdata[8]); | |
268 | + } | |
240 | 269 | |
241 | 270 | input_mt_sync(input); |
242 | 271 | } |
243 | 272 | |
244 | 273 | |
245 | 274 | |
246 | 275 | |
247 | 276 | |
248 | 277 | |
... | ... | @@ -247,39 +276,43 @@ |
247 | 276 | { |
248 | 277 | struct magicmouse_sc *msc = hid_get_drvdata(hdev); |
249 | 278 | struct input_dev *input = msc->input; |
250 | - int x, y, ts, ii, clicks, last_up; | |
279 | + int x = 0, y = 0, ii, clicks = 0, npoints; | |
251 | 280 | |
252 | 281 | switch (data[0]) { |
253 | - case 0x10: | |
254 | - if (size != 6) | |
282 | + case TRACKPAD_REPORT_ID: | |
283 | + /* Expect four bytes of prefix, and N*9 bytes of touch data. */ | |
284 | + if (size < 4 || ((size - 4) % 9) != 0) | |
255 | 285 | return 0; |
256 | - x = (__s16)(data[2] | data[3] << 8); | |
257 | - y = (__s16)(data[4] | data[5] << 8); | |
286 | + npoints = (size - 4) / 9; | |
287 | + msc->ntouches = 0; | |
288 | + for (ii = 0; ii < npoints; ii++) | |
289 | + magicmouse_emit_touch(msc, ii, data + ii * 9 + 4); | |
290 | + | |
291 | + /* We don't need an MT sync here because trackpad emits a | |
292 | + * BTN_TOUCH event in a new frame when all touches are released. | |
293 | + */ | |
294 | + if (msc->ntouches == 0) | |
295 | + msc->single_touch_id = NO_TOUCHES; | |
296 | + | |
258 | 297 | clicks = data[1]; |
298 | + | |
299 | + /* The following bits provide a device specific timestamp. They | |
300 | + * are unused here. | |
301 | + * | |
302 | + * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10; | |
303 | + */ | |
259 | 304 | break; |
260 | - case TOUCH_REPORT_ID: | |
305 | + case MOUSE_REPORT_ID: | |
261 | 306 | /* Expect six bytes of prefix, and N*8 bytes of touch data. */ |
262 | 307 | if (size < 6 || ((size - 6) % 8) != 0) |
263 | 308 | return 0; |
264 | - ts = data[3] >> 6 | data[4] << 2 | data[5] << 10; | |
265 | - msc->delta_time = (ts - msc->last_timestamp) & 0x3ffff; | |
266 | - msc->last_timestamp = ts; | |
267 | - msc->ntouches = (size - 6) / 8; | |
268 | - for (ii = 0; ii < msc->ntouches; ii++) | |
309 | + npoints = (size - 6) / 8; | |
310 | + msc->ntouches = 0; | |
311 | + for (ii = 0; ii < npoints; ii++) | |
269 | 312 | magicmouse_emit_touch(msc, ii, data + ii * 8 + 6); |
270 | 313 | |
271 | - if (report_touches) { | |
272 | - last_up = 1; | |
273 | - for (ii = 0; ii < ARRAY_SIZE(msc->touches); ii++) { | |
274 | - if (msc->touches[ii].down) { | |
275 | - last_up = 0; | |
276 | - msc->touches[ii].down = 0; | |
277 | - } | |
278 | - } | |
279 | - if (last_up) { | |
280 | - input_mt_sync(input); | |
281 | - } | |
282 | - } | |
314 | + if (report_touches && msc->ntouches == 0) | |
315 | + input_mt_sync(input); | |
283 | 316 | |
284 | 317 | /* When emulating three-button mode, it is important |
285 | 318 | * to have the current touch information before |
286 | 319 | |
287 | 320 | |
288 | 321 | |
289 | 322 | |
290 | 323 | |
291 | 324 | |
... | ... | @@ -288,68 +321,72 @@ |
288 | 321 | x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22; |
289 | 322 | y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22; |
290 | 323 | clicks = data[3]; |
324 | + | |
325 | + /* The following bits provide a device specific timestamp. They | |
326 | + * are unused here. | |
327 | + * | |
328 | + * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10; | |
329 | + */ | |
291 | 330 | break; |
292 | - case 0x20: /* Theoretically battery status (0-100), but I have | |
293 | - * never seen it -- maybe it is only upon request. | |
294 | - */ | |
295 | - case 0x60: /* Unknown, maybe laser on/off. */ | |
296 | - case 0x61: /* Laser reflection status change. | |
297 | - * data[1]: 0 = spotted, 1 = lost | |
298 | - */ | |
331 | + case DOUBLE_REPORT_ID: | |
332 | + /* Sometimes the trackpad sends two touch reports in one | |
333 | + * packet. | |
334 | + */ | |
335 | + magicmouse_raw_event(hdev, report, data + 2, data[1]); | |
336 | + magicmouse_raw_event(hdev, report, data + 2 + data[1], | |
337 | + size - 2 - data[1]); | |
338 | + break; | |
299 | 339 | default: |
300 | 340 | return 0; |
301 | 341 | } |
302 | 342 | |
303 | - magicmouse_emit_buttons(msc, clicks & 3); | |
304 | - input_report_rel(input, REL_X, x); | |
305 | - input_report_rel(input, REL_Y, y); | |
343 | + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { | |
344 | + magicmouse_emit_buttons(msc, clicks & 3); | |
345 | + input_report_rel(input, REL_X, x); | |
346 | + input_report_rel(input, REL_Y, y); | |
347 | + } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | |
348 | + input_report_key(input, BTN_MOUSE, clicks & 1); | |
349 | + input_report_key(input, BTN_TOUCH, msc->ntouches > 0); | |
350 | + input_report_key(input, BTN_TOOL_FINGER, msc->ntouches == 1); | |
351 | + input_report_key(input, BTN_TOOL_DOUBLETAP, msc->ntouches == 2); | |
352 | + input_report_key(input, BTN_TOOL_TRIPLETAP, msc->ntouches == 3); | |
353 | + input_report_key(input, BTN_TOOL_QUADTAP, msc->ntouches == 4); | |
354 | + if (msc->single_touch_id >= 0) { | |
355 | + input_report_abs(input, ABS_X, | |
356 | + msc->touches[msc->single_touch_id].x); | |
357 | + input_report_abs(input, ABS_Y, | |
358 | + msc->touches[msc->single_touch_id].y); | |
359 | + } | |
360 | + } | |
361 | + | |
306 | 362 | input_sync(input); |
307 | 363 | return 1; |
308 | 364 | } |
309 | 365 | |
310 | -static int magicmouse_input_open(struct input_dev *dev) | |
311 | -{ | |
312 | - struct hid_device *hid = input_get_drvdata(dev); | |
313 | - | |
314 | - return hid->ll_driver->open(hid); | |
315 | -} | |
316 | - | |
317 | -static void magicmouse_input_close(struct input_dev *dev) | |
318 | -{ | |
319 | - struct hid_device *hid = input_get_drvdata(dev); | |
320 | - | |
321 | - hid->ll_driver->close(hid); | |
322 | -} | |
323 | - | |
324 | 366 | static void magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev) |
325 | 367 | { |
326 | - input_set_drvdata(input, hdev); | |
327 | - input->event = hdev->ll_driver->hidinput_input_event; | |
328 | - input->open = magicmouse_input_open; | |
329 | - input->close = magicmouse_input_close; | |
330 | - | |
331 | - input->name = hdev->name; | |
332 | - input->phys = hdev->phys; | |
333 | - input->uniq = hdev->uniq; | |
334 | - input->id.bustype = hdev->bus; | |
335 | - input->id.vendor = hdev->vendor; | |
336 | - input->id.product = hdev->product; | |
337 | - input->id.version = hdev->version; | |
338 | - input->dev.parent = hdev->dev.parent; | |
339 | - | |
340 | 368 | __set_bit(EV_KEY, input->evbit); |
341 | - __set_bit(BTN_LEFT, input->keybit); | |
342 | - __set_bit(BTN_RIGHT, input->keybit); | |
343 | - if (emulate_3button) | |
344 | - __set_bit(BTN_MIDDLE, input->keybit); | |
345 | - __set_bit(BTN_TOOL_FINGER, input->keybit); | |
346 | 369 | |
347 | - __set_bit(EV_REL, input->evbit); | |
348 | - __set_bit(REL_X, input->relbit); | |
349 | - __set_bit(REL_Y, input->relbit); | |
350 | - if (emulate_scroll_wheel) { | |
351 | - __set_bit(REL_WHEEL, input->relbit); | |
352 | - __set_bit(REL_HWHEEL, input->relbit); | |
370 | + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { | |
371 | + __set_bit(BTN_LEFT, input->keybit); | |
372 | + __set_bit(BTN_RIGHT, input->keybit); | |
373 | + if (emulate_3button) | |
374 | + __set_bit(BTN_MIDDLE, input->keybit); | |
375 | + | |
376 | + __set_bit(EV_REL, input->evbit); | |
377 | + __set_bit(REL_X, input->relbit); | |
378 | + __set_bit(REL_Y, input->relbit); | |
379 | + if (emulate_scroll_wheel) { | |
380 | + __set_bit(REL_WHEEL, input->relbit); | |
381 | + __set_bit(REL_HWHEEL, input->relbit); | |
382 | + } | |
383 | + } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | |
384 | + __set_bit(BTN_MOUSE, input->keybit); | |
385 | + __set_bit(BTN_TOOL_FINGER, input->keybit); | |
386 | + __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); | |
387 | + __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); | |
388 | + __set_bit(BTN_TOOL_QUADTAP, input->keybit); | |
389 | + __set_bit(BTN_TOUCH, input->keybit); | |
353 | 390 | } |
354 | 391 | |
355 | 392 | if (report_touches) { |
356 | 393 | |
... | ... | @@ -359,16 +396,26 @@ |
359 | 396 | input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0); |
360 | 397 | input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0); |
361 | 398 | input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0); |
362 | - input_set_abs_params(input, ABS_MT_POSITION_X, -1100, 1358, | |
363 | - 4, 0); | |
399 | + | |
364 | 400 | /* Note: Touch Y position from the device is inverted relative |
365 | 401 | * to how pointer motion is reported (and relative to how USB |
366 | 402 | * HID recommends the coordinates work). This driver keeps |
367 | 403 | * the origin at the same position, and just uses the additive |
368 | 404 | * inverse of the reported Y. |
369 | 405 | */ |
370 | - input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, 2047, | |
371 | - 4, 0); | |
406 | + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { | |
407 | + input_set_abs_params(input, ABS_MT_POSITION_X, -1100, | |
408 | + 1358, 4, 0); | |
409 | + input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, | |
410 | + 2047, 4, 0); | |
411 | + } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | |
412 | + input_set_abs_params(input, ABS_X, -2909, 3167, 4, 0); | |
413 | + input_set_abs_params(input, ABS_Y, -2456, 2565, 4, 0); | |
414 | + input_set_abs_params(input, ABS_MT_POSITION_X, -2909, | |
415 | + 3167, 4, 0); | |
416 | + input_set_abs_params(input, ABS_MT_POSITION_Y, -2456, | |
417 | + 2565, 4, 0); | |
418 | + } | |
372 | 419 | } |
373 | 420 | |
374 | 421 | if (report_undeciphered) { |
375 | 422 | |
... | ... | @@ -377,12 +424,22 @@ |
377 | 424 | } |
378 | 425 | } |
379 | 426 | |
427 | +static int magicmouse_input_mapping(struct hid_device *hdev, | |
428 | + struct hid_input *hi, struct hid_field *field, | |
429 | + struct hid_usage *usage, unsigned long **bit, int *max) | |
430 | +{ | |
431 | + struct magicmouse_sc *msc = hid_get_drvdata(hdev); | |
432 | + | |
433 | + if (!msc->input) | |
434 | + msc->input = hi->input; | |
435 | + | |
436 | + return 0; | |
437 | +} | |
438 | + | |
380 | 439 | static int magicmouse_probe(struct hid_device *hdev, |
381 | 440 | const struct hid_device_id *id) |
382 | 441 | { |
383 | - __u8 feature_1[] = { 0xd7, 0x01 }; | |
384 | - __u8 feature_2[] = { 0xf8, 0x01, 0x32 }; | |
385 | - struct input_dev *input; | |
442 | + __u8 feature[] = { 0xd7, 0x01 }; | |
386 | 443 | struct magicmouse_sc *msc; |
387 | 444 | struct hid_report *report; |
388 | 445 | int ret; |
... | ... | @@ -398,6 +455,8 @@ |
398 | 455 | msc->quirks = id->driver_data; |
399 | 456 | hid_set_drvdata(hdev, msc); |
400 | 457 | |
458 | + msc->single_touch_id = NO_TOUCHES; | |
459 | + | |
401 | 460 | ret = hid_parse(hdev); |
402 | 461 | if (ret) { |
403 | 462 | dev_err(&hdev->dev, "magicmouse hid parse failed\n"); |
404 | 463 | |
... | ... | @@ -410,10 +469,22 @@ |
410 | 469 | goto err_free; |
411 | 470 | } |
412 | 471 | |
413 | - /* we are handling the input ourselves */ | |
414 | - hidinput_disconnect(hdev); | |
472 | + /* We do this after hid-input is done parsing reports so that | |
473 | + * hid-input uses the most natural button and axis IDs. | |
474 | + */ | |
475 | + if (msc->input) | |
476 | + magicmouse_setup_input(msc->input, hdev); | |
415 | 477 | |
416 | - report = hid_register_report(hdev, HID_INPUT_REPORT, TOUCH_REPORT_ID); | |
478 | + if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE) | |
479 | + report = hid_register_report(hdev, HID_INPUT_REPORT, | |
480 | + MOUSE_REPORT_ID); | |
481 | + else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | |
482 | + report = hid_register_report(hdev, HID_INPUT_REPORT, | |
483 | + TRACKPAD_REPORT_ID); | |
484 | + report = hid_register_report(hdev, HID_INPUT_REPORT, | |
485 | + DOUBLE_REPORT_ID); | |
486 | + } | |
487 | + | |
417 | 488 | if (!report) { |
418 | 489 | dev_err(&hdev->dev, "unable to register touch report\n"); |
419 | 490 | ret = -ENOMEM; |
420 | 491 | |
421 | 492 | |
422 | 493 | |
423 | 494 | |
... | ... | @@ -421,39 +492,15 @@ |
421 | 492 | } |
422 | 493 | report->size = 6; |
423 | 494 | |
424 | - ret = hdev->hid_output_raw_report(hdev, feature_1, sizeof(feature_1), | |
495 | + ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature), | |
425 | 496 | HID_FEATURE_REPORT); |
426 | - if (ret != sizeof(feature_1)) { | |
427 | - dev_err(&hdev->dev, "unable to request touch data (1:%d)\n", | |
497 | + if (ret != sizeof(feature)) { | |
498 | + dev_err(&hdev->dev, "unable to request touch data (%d)\n", | |
428 | 499 | ret); |
429 | 500 | goto err_stop_hw; |
430 | 501 | } |
431 | - ret = hdev->hid_output_raw_report(hdev, feature_2, | |
432 | - sizeof(feature_2), HID_FEATURE_REPORT); | |
433 | - if (ret != sizeof(feature_2)) { | |
434 | - dev_err(&hdev->dev, "unable to request touch data (2:%d)\n", | |
435 | - ret); | |
436 | - goto err_stop_hw; | |
437 | - } | |
438 | 502 | |
439 | - input = input_allocate_device(); | |
440 | - if (!input) { | |
441 | - dev_err(&hdev->dev, "can't alloc input device\n"); | |
442 | - ret = -ENOMEM; | |
443 | - goto err_stop_hw; | |
444 | - } | |
445 | - magicmouse_setup_input(input, hdev); | |
446 | - | |
447 | - ret = input_register_device(input); | |
448 | - if (ret) { | |
449 | - dev_err(&hdev->dev, "input device registration failed\n"); | |
450 | - goto err_input; | |
451 | - } | |
452 | - msc->input = input; | |
453 | - | |
454 | 503 | return 0; |
455 | -err_input: | |
456 | - input_free_device(input); | |
457 | 504 | err_stop_hw: |
458 | 505 | hid_hw_stop(hdev); |
459 | 506 | err_free: |
460 | 507 | |
... | ... | @@ -466,13 +513,14 @@ |
466 | 513 | struct magicmouse_sc *msc = hid_get_drvdata(hdev); |
467 | 514 | |
468 | 515 | hid_hw_stop(hdev); |
469 | - input_unregister_device(msc->input); | |
470 | 516 | kfree(msc); |
471 | 517 | } |
472 | 518 | |
473 | 519 | static const struct hid_device_id magic_mice[] = { |
474 | - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE), | |
475 | - .driver_data = 0 }, | |
520 | + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, | |
521 | + USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 }, | |
522 | + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, | |
523 | + USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 }, | |
476 | 524 | { } |
477 | 525 | }; |
478 | 526 | MODULE_DEVICE_TABLE(hid, magic_mice); |
... | ... | @@ -483,6 +531,7 @@ |
483 | 531 | .probe = magicmouse_probe, |
484 | 532 | .remove = magicmouse_remove, |
485 | 533 | .raw_event = magicmouse_raw_event, |
534 | + .input_mapping = magicmouse_input_mapping, | |
486 | 535 | }; |
487 | 536 | |
488 | 537 | static int __init magicmouse_init(void) |
drivers/hid/hid-ntrig.c
... | ... | @@ -90,6 +90,55 @@ |
90 | 90 | }; |
91 | 91 | |
92 | 92 | |
93 | +/* | |
94 | + * This function converts the 4 byte raw firmware code into | |
95 | + * a string containing 5 comma separated numbers. | |
96 | + */ | |
97 | +static int ntrig_version_string(unsigned char *raw, char *buf) | |
98 | +{ | |
99 | + __u8 a = (raw[1] & 0x0e) >> 1; | |
100 | + __u8 b = (raw[0] & 0x3c) >> 2; | |
101 | + __u8 c = ((raw[0] & 0x03) << 3) | ((raw[3] & 0xe0) >> 5); | |
102 | + __u8 d = ((raw[3] & 0x07) << 3) | ((raw[2] & 0xe0) >> 5); | |
103 | + __u8 e = raw[2] & 0x07; | |
104 | + | |
105 | + /* | |
106 | + * As yet unmapped bits: | |
107 | + * 0b11000000 0b11110001 0b00011000 0b00011000 | |
108 | + */ | |
109 | + | |
110 | + return sprintf(buf, "%u.%u.%u.%u.%u", a, b, c, d, e); | |
111 | +} | |
112 | + | |
113 | +static void ntrig_report_version(struct hid_device *hdev) | |
114 | +{ | |
115 | + int ret; | |
116 | + char buf[20]; | |
117 | + struct usb_device *usb_dev = hid_to_usb_dev(hdev); | |
118 | + unsigned char *data = kmalloc(8, GFP_KERNEL); | |
119 | + | |
120 | + if (!data) | |
121 | + goto err_free; | |
122 | + | |
123 | + ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | |
124 | + USB_REQ_CLEAR_FEATURE, | |
125 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | | |
126 | + USB_DIR_IN, | |
127 | + 0x30c, 1, data, 8, | |
128 | + USB_CTRL_SET_TIMEOUT); | |
129 | + | |
130 | + if (ret == 8) { | |
131 | + ret = ntrig_version_string(&data[2], buf); | |
132 | + | |
133 | + dev_info(&hdev->dev, | |
134 | + "Firmware version: %s (%02x%02x %02x%02x)\n", | |
135 | + buf, data[2], data[3], data[4], data[5]); | |
136 | + } | |
137 | + | |
138 | +err_free: | |
139 | + kfree(data); | |
140 | +} | |
141 | + | |
93 | 142 | static ssize_t show_phys_width(struct device *dev, |
94 | 143 | struct device_attribute *attr, |
95 | 144 | char *buf) |
... | ... | @@ -377,8 +426,8 @@ |
377 | 426 | */ |
378 | 427 | |
379 | 428 | static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, |
380 | - struct hid_field *field, struct hid_usage *usage, | |
381 | - unsigned long **bit, int *max) | |
429 | + struct hid_field *field, struct hid_usage *usage, | |
430 | + unsigned long **bit, int *max) | |
382 | 431 | { |
383 | 432 | struct ntrig_data *nd = hid_get_drvdata(hdev); |
384 | 433 | |
385 | 434 | |
386 | 435 | |
... | ... | @@ -448,13 +497,13 @@ |
448 | 497 | /* width/height mapped on TouchMajor/TouchMinor/Orientation */ |
449 | 498 | case HID_DG_WIDTH: |
450 | 499 | hid_map_usage(hi, usage, bit, max, |
451 | - EV_ABS, ABS_MT_TOUCH_MAJOR); | |
500 | + EV_ABS, ABS_MT_TOUCH_MAJOR); | |
452 | 501 | return 1; |
453 | 502 | case HID_DG_HEIGHT: |
454 | 503 | hid_map_usage(hi, usage, bit, max, |
455 | - EV_ABS, ABS_MT_TOUCH_MINOR); | |
504 | + EV_ABS, ABS_MT_TOUCH_MINOR); | |
456 | 505 | input_set_abs_params(hi->input, ABS_MT_ORIENTATION, |
457 | - 0, 1, 0, 0); | |
506 | + 0, 1, 0, 0); | |
458 | 507 | return 1; |
459 | 508 | } |
460 | 509 | return 0; |
... | ... | @@ -468,8 +517,8 @@ |
468 | 517 | } |
469 | 518 | |
470 | 519 | static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, |
471 | - struct hid_field *field, struct hid_usage *usage, | |
472 | - unsigned long **bit, int *max) | |
520 | + struct hid_field *field, struct hid_usage *usage, | |
521 | + unsigned long **bit, int *max) | |
473 | 522 | { |
474 | 523 | /* No special mappings needed for the pen and single touch */ |
475 | 524 | if (field->physical) |
... | ... | @@ -489,7 +538,7 @@ |
489 | 538 | * and call input_mt_sync after each point if necessary |
490 | 539 | */ |
491 | 540 | static int ntrig_event (struct hid_device *hid, struct hid_field *field, |
492 | - struct hid_usage *usage, __s32 value) | |
541 | + struct hid_usage *usage, __s32 value) | |
493 | 542 | { |
494 | 543 | struct input_dev *input = field->hidinput->input; |
495 | 544 | struct ntrig_data *nd = hid_get_drvdata(hid); |
... | ... | @@ -848,6 +897,8 @@ |
848 | 897 | if (report) |
849 | 898 | usbhid_submit_report(hdev, report, USB_DIR_OUT); |
850 | 899 | |
900 | + ntrig_report_version(hdev); | |
901 | + | |
851 | 902 | ret = sysfs_create_group(&hdev->dev.kobj, |
852 | 903 | &ntrig_attribute_group); |
853 | 904 | |
... | ... | @@ -860,7 +911,7 @@ |
860 | 911 | static void ntrig_remove(struct hid_device *hdev) |
861 | 912 | { |
862 | 913 | sysfs_remove_group(&hdev->dev.kobj, |
863 | - &ntrig_attribute_group); | |
914 | + &ntrig_attribute_group); | |
864 | 915 | hid_hw_stop(hdev); |
865 | 916 | kfree(hid_get_drvdata(hdev)); |
866 | 917 | } |
drivers/hid/hid-roccat-pyra.c
1 | +/* | |
2 | + * Roccat Pyra driver for Linux | |
3 | + * | |
4 | + * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net> | |
5 | + */ | |
6 | + | |
7 | +/* | |
8 | + * This program is free software; you can redistribute it and/or modify it | |
9 | + * under the terms of the GNU General Public License as published by the Free | |
10 | + * Software Foundation; either version 2 of the License, or (at your option) | |
11 | + * any later version. | |
12 | + */ | |
13 | + | |
14 | +/* | |
15 | + * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless | |
16 | + * variant. Wireless variant is not tested. | |
17 | + * Userland tools can be found at http://sourceforge.net/projects/roccat | |
18 | + */ | |
19 | + | |
20 | +#include <linux/device.h> | |
21 | +#include <linux/input.h> | |
22 | +#include <linux/hid.h> | |
23 | +#include <linux/usb.h> | |
24 | +#include <linux/module.h> | |
25 | +#include <linux/slab.h> | |
26 | +#include "hid-ids.h" | |
27 | +#include "hid-roccat.h" | |
28 | +#include "hid-roccat-pyra.h" | |
29 | + | |
30 | +static void profile_activated(struct pyra_device *pyra, | |
31 | + unsigned int new_profile) | |
32 | +{ | |
33 | + pyra->actual_profile = new_profile; | |
34 | + pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi; | |
35 | +} | |
36 | + | |
37 | +static int pyra_send_control(struct usb_device *usb_dev, int value, | |
38 | + enum pyra_control_requests request) | |
39 | +{ | |
40 | + int len; | |
41 | + struct pyra_control control; | |
42 | + | |
43 | + if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS || | |
44 | + request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) && | |
45 | + (value < 0 || value > 4)) | |
46 | + return -EINVAL; | |
47 | + | |
48 | + control.command = PYRA_COMMAND_CONTROL; | |
49 | + control.value = value; | |
50 | + control.request = request; | |
51 | + | |
52 | + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), | |
53 | + USB_REQ_SET_CONFIGURATION, | |
54 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | |
55 | + PYRA_USB_COMMAND_CONTROL, 0, (char *)&control, | |
56 | + sizeof(struct pyra_control), | |
57 | + USB_CTRL_SET_TIMEOUT); | |
58 | + | |
59 | + if (len != sizeof(struct pyra_control)) | |
60 | + return len; | |
61 | + | |
62 | + return 0; | |
63 | +} | |
64 | + | |
65 | +static int pyra_receive_control_status(struct usb_device *usb_dev) | |
66 | +{ | |
67 | + int len; | |
68 | + struct pyra_control control; | |
69 | + | |
70 | + do { | |
71 | + msleep(10); | |
72 | + | |
73 | + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | |
74 | + USB_REQ_CLEAR_FEATURE, | |
75 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | | |
76 | + USB_DIR_IN, | |
77 | + PYRA_USB_COMMAND_CONTROL, 0, (char *)&control, | |
78 | + sizeof(struct pyra_control), | |
79 | + USB_CTRL_SET_TIMEOUT); | |
80 | + | |
81 | + /* requested too early, try again */ | |
82 | + } while (len == -EPROTO); | |
83 | + | |
84 | + if (len == sizeof(struct pyra_control) && | |
85 | + control.command == PYRA_COMMAND_CONTROL && | |
86 | + control.request == PYRA_CONTROL_REQUEST_STATUS && | |
87 | + control.value == 1) | |
88 | + return 0; | |
89 | + else { | |
90 | + dev_err(&usb_dev->dev, "receive control status: " | |
91 | + "unknown response 0x%x 0x%x\n", | |
92 | + control.request, control.value); | |
93 | + return -EINVAL; | |
94 | + } | |
95 | +} | |
96 | + | |
97 | +static int pyra_get_profile_settings(struct usb_device *usb_dev, | |
98 | + struct pyra_profile_settings *buf, int number) | |
99 | +{ | |
100 | + int retval; | |
101 | + | |
102 | + retval = pyra_send_control(usb_dev, number, | |
103 | + PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); | |
104 | + | |
105 | + if (retval) | |
106 | + return retval; | |
107 | + | |
108 | + retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | |
109 | + USB_REQ_CLEAR_FEATURE, | |
110 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | |
111 | + PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)buf, | |
112 | + sizeof(struct pyra_profile_settings), | |
113 | + USB_CTRL_SET_TIMEOUT); | |
114 | + | |
115 | + if (retval != sizeof(struct pyra_profile_settings)) | |
116 | + return retval; | |
117 | + | |
118 | + return 0; | |
119 | +} | |
120 | + | |
121 | +static int pyra_get_profile_buttons(struct usb_device *usb_dev, | |
122 | + struct pyra_profile_buttons *buf, int number) | |
123 | +{ | |
124 | + int retval; | |
125 | + | |
126 | + retval = pyra_send_control(usb_dev, number, | |
127 | + PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); | |
128 | + | |
129 | + if (retval) | |
130 | + return retval; | |
131 | + | |
132 | + retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | |
133 | + USB_REQ_CLEAR_FEATURE, | |
134 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | |
135 | + PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buf, | |
136 | + sizeof(struct pyra_profile_buttons), | |
137 | + USB_CTRL_SET_TIMEOUT); | |
138 | + | |
139 | + if (retval != sizeof(struct pyra_profile_buttons)) | |
140 | + return retval; | |
141 | + | |
142 | + return 0; | |
143 | +} | |
144 | + | |
145 | +static int pyra_get_settings(struct usb_device *usb_dev, | |
146 | + struct pyra_settings *buf) | |
147 | +{ | |
148 | + int len; | |
149 | + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | |
150 | + USB_REQ_CLEAR_FEATURE, | |
151 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | |
152 | + PYRA_USB_COMMAND_SETTINGS, 0, buf, | |
153 | + sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT); | |
154 | + if (len != sizeof(struct pyra_settings)) | |
155 | + return -EIO; | |
156 | + return 0; | |
157 | +} | |
158 | + | |
159 | +static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf) | |
160 | +{ | |
161 | + int len; | |
162 | + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | |
163 | + USB_REQ_CLEAR_FEATURE, | |
164 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | |
165 | + PYRA_USB_COMMAND_INFO, 0, buf, | |
166 | + sizeof(struct pyra_info), USB_CTRL_SET_TIMEOUT); | |
167 | + if (len != sizeof(struct pyra_info)) | |
168 | + return -EIO; | |
169 | + return 0; | |
170 | +} | |
171 | + | |
172 | +static int pyra_set_profile_settings(struct usb_device *usb_dev, | |
173 | + struct pyra_profile_settings const *settings) | |
174 | +{ | |
175 | + int len; | |
176 | + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), | |
177 | + USB_REQ_SET_CONFIGURATION, | |
178 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | |
179 | + PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)settings, | |
180 | + sizeof(struct pyra_profile_settings), | |
181 | + USB_CTRL_SET_TIMEOUT); | |
182 | + if (len != sizeof(struct pyra_profile_settings)) | |
183 | + return -EIO; | |
184 | + if (pyra_receive_control_status(usb_dev)) | |
185 | + return -EIO; | |
186 | + return 0; | |
187 | +} | |
188 | + | |
189 | +static int pyra_set_profile_buttons(struct usb_device *usb_dev, | |
190 | + struct pyra_profile_buttons const *buttons) | |
191 | +{ | |
192 | + int len; | |
193 | + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), | |
194 | + USB_REQ_SET_CONFIGURATION, | |
195 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | |
196 | + PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buttons, | |
197 | + sizeof(struct pyra_profile_buttons), | |
198 | + USB_CTRL_SET_TIMEOUT); | |
199 | + if (len != sizeof(struct pyra_profile_buttons)) | |
200 | + return -EIO; | |
201 | + if (pyra_receive_control_status(usb_dev)) | |
202 | + return -EIO; | |
203 | + return 0; | |
204 | +} | |
205 | + | |
206 | +static int pyra_set_settings(struct usb_device *usb_dev, | |
207 | + struct pyra_settings const *settings) | |
208 | +{ | |
209 | + int len; | |
210 | + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), | |
211 | + USB_REQ_SET_CONFIGURATION, | |
212 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | |
213 | + PYRA_USB_COMMAND_SETTINGS, 0, (char *)settings, | |
214 | + sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT); | |
215 | + if (len != sizeof(struct pyra_settings)) | |
216 | + return -EIO; | |
217 | + if (pyra_receive_control_status(usb_dev)) | |
218 | + return -EIO; | |
219 | + return 0; | |
220 | +} | |
221 | + | |
222 | +static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, | |
223 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
224 | + loff_t off, size_t count, int number) | |
225 | +{ | |
226 | + struct device *dev = container_of(kobj, struct device, kobj); | |
227 | + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); | |
228 | + | |
229 | + if (off >= sizeof(struct pyra_profile_settings)) | |
230 | + return 0; | |
231 | + | |
232 | + if (off + count > sizeof(struct pyra_profile_settings)) | |
233 | + count = sizeof(struct pyra_profile_settings) - off; | |
234 | + | |
235 | + mutex_lock(&pyra->pyra_lock); | |
236 | + memcpy(buf, ((char const *)&pyra->profile_settings[number]) + off, | |
237 | + count); | |
238 | + mutex_unlock(&pyra->pyra_lock); | |
239 | + | |
240 | + return count; | |
241 | +} | |
242 | + | |
243 | +static ssize_t pyra_sysfs_read_profile1_settings(struct file *fp, | |
244 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
245 | + loff_t off, size_t count) | |
246 | +{ | |
247 | + return pyra_sysfs_read_profilex_settings(fp, kobj, | |
248 | + attr, buf, off, count, 0); | |
249 | +} | |
250 | + | |
251 | +static ssize_t pyra_sysfs_read_profile2_settings(struct file *fp, | |
252 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
253 | + loff_t off, size_t count) | |
254 | +{ | |
255 | + return pyra_sysfs_read_profilex_settings(fp, kobj, | |
256 | + attr, buf, off, count, 1); | |
257 | +} | |
258 | + | |
259 | +static ssize_t pyra_sysfs_read_profile3_settings(struct file *fp, | |
260 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
261 | + loff_t off, size_t count) | |
262 | +{ | |
263 | + return pyra_sysfs_read_profilex_settings(fp, kobj, | |
264 | + attr, buf, off, count, 2); | |
265 | +} | |
266 | + | |
267 | +static ssize_t pyra_sysfs_read_profile4_settings(struct file *fp, | |
268 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
269 | + loff_t off, size_t count) | |
270 | +{ | |
271 | + return pyra_sysfs_read_profilex_settings(fp, kobj, | |
272 | + attr, buf, off, count, 3); | |
273 | +} | |
274 | + | |
275 | +static ssize_t pyra_sysfs_read_profile5_settings(struct file *fp, | |
276 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
277 | + loff_t off, size_t count) | |
278 | +{ | |
279 | + return pyra_sysfs_read_profilex_settings(fp, kobj, | |
280 | + attr, buf, off, count, 4); | |
281 | +} | |
282 | + | |
283 | +static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, | |
284 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
285 | + loff_t off, size_t count, int number) | |
286 | +{ | |
287 | + struct device *dev = container_of(kobj, struct device, kobj); | |
288 | + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); | |
289 | + | |
290 | + if (off >= sizeof(struct pyra_profile_buttons)) | |
291 | + return 0; | |
292 | + | |
293 | + if (off + count > sizeof(struct pyra_profile_buttons)) | |
294 | + count = sizeof(struct pyra_profile_buttons) - off; | |
295 | + | |
296 | + mutex_lock(&pyra->pyra_lock); | |
297 | + memcpy(buf, ((char const *)&pyra->profile_buttons[number]) + off, | |
298 | + count); | |
299 | + mutex_unlock(&pyra->pyra_lock); | |
300 | + | |
301 | + return count; | |
302 | +} | |
303 | + | |
304 | +static ssize_t pyra_sysfs_read_profile1_buttons(struct file *fp, | |
305 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
306 | + loff_t off, size_t count) | |
307 | +{ | |
308 | + return pyra_sysfs_read_profilex_buttons(fp, kobj, | |
309 | + attr, buf, off, count, 0); | |
310 | +} | |
311 | + | |
312 | +static ssize_t pyra_sysfs_read_profile2_buttons(struct file *fp, | |
313 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
314 | + loff_t off, size_t count) | |
315 | +{ | |
316 | + return pyra_sysfs_read_profilex_buttons(fp, kobj, | |
317 | + attr, buf, off, count, 1); | |
318 | +} | |
319 | + | |
320 | +static ssize_t pyra_sysfs_read_profile3_buttons(struct file *fp, | |
321 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
322 | + loff_t off, size_t count) | |
323 | +{ | |
324 | + return pyra_sysfs_read_profilex_buttons(fp, kobj, | |
325 | + attr, buf, off, count, 2); | |
326 | +} | |
327 | + | |
328 | +static ssize_t pyra_sysfs_read_profile4_buttons(struct file *fp, | |
329 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
330 | + loff_t off, size_t count) | |
331 | +{ | |
332 | + return pyra_sysfs_read_profilex_buttons(fp, kobj, | |
333 | + attr, buf, off, count, 3); | |
334 | +} | |
335 | + | |
336 | +static ssize_t pyra_sysfs_read_profile5_buttons(struct file *fp, | |
337 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
338 | + loff_t off, size_t count) | |
339 | +{ | |
340 | + return pyra_sysfs_read_profilex_buttons(fp, kobj, | |
341 | + attr, buf, off, count, 4); | |
342 | +} | |
343 | + | |
344 | +static ssize_t pyra_sysfs_write_profile_settings(struct file *fp, | |
345 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
346 | + loff_t off, size_t count) | |
347 | +{ | |
348 | + struct device *dev = container_of(kobj, struct device, kobj); | |
349 | + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); | |
350 | + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | |
351 | + int retval = 0; | |
352 | + int difference; | |
353 | + int profile_number; | |
354 | + struct pyra_profile_settings *profile_settings; | |
355 | + | |
356 | + if (off != 0 || count != sizeof(struct pyra_profile_settings)) | |
357 | + return -EINVAL; | |
358 | + | |
359 | + profile_number = ((struct pyra_profile_settings const *)buf)->number; | |
360 | + profile_settings = &pyra->profile_settings[profile_number]; | |
361 | + | |
362 | + mutex_lock(&pyra->pyra_lock); | |
363 | + difference = memcmp(buf, profile_settings, | |
364 | + sizeof(struct pyra_profile_settings)); | |
365 | + if (difference) { | |
366 | + retval = pyra_set_profile_settings(usb_dev, | |
367 | + (struct pyra_profile_settings const *)buf); | |
368 | + if (!retval) | |
369 | + memcpy(profile_settings, buf, | |
370 | + sizeof(struct pyra_profile_settings)); | |
371 | + } | |
372 | + mutex_unlock(&pyra->pyra_lock); | |
373 | + | |
374 | + if (retval) | |
375 | + return retval; | |
376 | + | |
377 | + return sizeof(struct pyra_profile_settings); | |
378 | +} | |
379 | + | |
380 | +static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp, | |
381 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
382 | + loff_t off, size_t count) | |
383 | +{ | |
384 | + struct device *dev = container_of(kobj, struct device, kobj); | |
385 | + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); | |
386 | + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | |
387 | + int retval = 0; | |
388 | + int difference; | |
389 | + int profile_number; | |
390 | + struct pyra_profile_buttons *profile_buttons; | |
391 | + | |
392 | + if (off != 0 || count != sizeof(struct pyra_profile_buttons)) | |
393 | + return -EINVAL; | |
394 | + | |
395 | + profile_number = ((struct pyra_profile_buttons const *)buf)->number; | |
396 | + profile_buttons = &pyra->profile_buttons[profile_number]; | |
397 | + | |
398 | + mutex_lock(&pyra->pyra_lock); | |
399 | + difference = memcmp(buf, profile_buttons, | |
400 | + sizeof(struct pyra_profile_buttons)); | |
401 | + if (difference) { | |
402 | + retval = pyra_set_profile_buttons(usb_dev, | |
403 | + (struct pyra_profile_buttons const *)buf); | |
404 | + if (!retval) | |
405 | + memcpy(profile_buttons, buf, | |
406 | + sizeof(struct pyra_profile_buttons)); | |
407 | + } | |
408 | + mutex_unlock(&pyra->pyra_lock); | |
409 | + | |
410 | + if (retval) | |
411 | + return retval; | |
412 | + | |
413 | + return sizeof(struct pyra_profile_buttons); | |
414 | +} | |
415 | + | |
416 | +static ssize_t pyra_sysfs_read_settings(struct file *fp, | |
417 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
418 | + loff_t off, size_t count) | |
419 | +{ | |
420 | + struct device *dev = container_of(kobj, struct device, kobj); | |
421 | + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); | |
422 | + | |
423 | + if (off >= sizeof(struct pyra_settings)) | |
424 | + return 0; | |
425 | + | |
426 | + if (off + count > sizeof(struct pyra_settings)) | |
427 | + count = sizeof(struct pyra_settings) - off; | |
428 | + | |
429 | + mutex_lock(&pyra->pyra_lock); | |
430 | + memcpy(buf, ((char const *)&pyra->settings) + off, count); | |
431 | + mutex_unlock(&pyra->pyra_lock); | |
432 | + | |
433 | + return count; | |
434 | +} | |
435 | + | |
436 | +static ssize_t pyra_sysfs_write_settings(struct file *fp, | |
437 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
438 | + loff_t off, size_t count) | |
439 | +{ | |
440 | + struct device *dev = container_of(kobj, struct device, kobj); | |
441 | + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); | |
442 | + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | |
443 | + int retval = 0; | |
444 | + int difference; | |
445 | + | |
446 | + if (off != 0 || count != sizeof(struct pyra_settings)) | |
447 | + return -EINVAL; | |
448 | + | |
449 | + mutex_lock(&pyra->pyra_lock); | |
450 | + difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings)); | |
451 | + if (difference) { | |
452 | + retval = pyra_set_settings(usb_dev, | |
453 | + (struct pyra_settings const *)buf); | |
454 | + if (!retval) | |
455 | + memcpy(&pyra->settings, buf, | |
456 | + sizeof(struct pyra_settings)); | |
457 | + } | |
458 | + mutex_unlock(&pyra->pyra_lock); | |
459 | + | |
460 | + if (retval) | |
461 | + return retval; | |
462 | + | |
463 | + profile_activated(pyra, pyra->settings.startup_profile); | |
464 | + | |
465 | + return sizeof(struct pyra_settings); | |
466 | +} | |
467 | + | |
468 | + | |
469 | +static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev, | |
470 | + struct device_attribute *attr, char *buf) | |
471 | +{ | |
472 | + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); | |
473 | + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi); | |
474 | +} | |
475 | + | |
476 | +static ssize_t pyra_sysfs_show_actual_profile(struct device *dev, | |
477 | + struct device_attribute *attr, char *buf) | |
478 | +{ | |
479 | + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); | |
480 | + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile); | |
481 | +} | |
482 | + | |
483 | +static ssize_t pyra_sysfs_show_firmware_version(struct device *dev, | |
484 | + struct device_attribute *attr, char *buf) | |
485 | +{ | |
486 | + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); | |
487 | + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version); | |
488 | +} | |
489 | + | |
490 | +static ssize_t pyra_sysfs_show_startup_profile(struct device *dev, | |
491 | + struct device_attribute *attr, char *buf) | |
492 | +{ | |
493 | + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); | |
494 | + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile); | |
495 | +} | |
496 | + | |
497 | +static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL); | |
498 | + | |
499 | +static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL); | |
500 | + | |
501 | +static DEVICE_ATTR(firmware_version, 0440, | |
502 | + pyra_sysfs_show_firmware_version, NULL); | |
503 | + | |
504 | +static DEVICE_ATTR(startup_profile, 0440, | |
505 | + pyra_sysfs_show_startup_profile, NULL); | |
506 | + | |
507 | +static struct attribute *pyra_attributes[] = { | |
508 | + &dev_attr_actual_cpi.attr, | |
509 | + &dev_attr_actual_profile.attr, | |
510 | + &dev_attr_firmware_version.attr, | |
511 | + &dev_attr_startup_profile.attr, | |
512 | + NULL | |
513 | +}; | |
514 | + | |
515 | +static struct attribute_group pyra_attribute_group = { | |
516 | + .attrs = pyra_attributes | |
517 | +}; | |
518 | + | |
519 | +static struct bin_attribute pyra_profile_settings_attr = { | |
520 | + .attr = { .name = "profile_settings", .mode = 0220 }, | |
521 | + .size = sizeof(struct pyra_profile_settings), | |
522 | + .write = pyra_sysfs_write_profile_settings | |
523 | +}; | |
524 | + | |
525 | +static struct bin_attribute pyra_profile1_settings_attr = { | |
526 | + .attr = { .name = "profile1_settings", .mode = 0440 }, | |
527 | + .size = sizeof(struct pyra_profile_settings), | |
528 | + .read = pyra_sysfs_read_profile1_settings | |
529 | +}; | |
530 | + | |
531 | +static struct bin_attribute pyra_profile2_settings_attr = { | |
532 | + .attr = { .name = "profile2_settings", .mode = 0440 }, | |
533 | + .size = sizeof(struct pyra_profile_settings), | |
534 | + .read = pyra_sysfs_read_profile2_settings | |
535 | +}; | |
536 | + | |
537 | +static struct bin_attribute pyra_profile3_settings_attr = { | |
538 | + .attr = { .name = "profile3_settings", .mode = 0440 }, | |
539 | + .size = sizeof(struct pyra_profile_settings), | |
540 | + .read = pyra_sysfs_read_profile3_settings | |
541 | +}; | |
542 | + | |
543 | +static struct bin_attribute pyra_profile4_settings_attr = { | |
544 | + .attr = { .name = "profile4_settings", .mode = 0440 }, | |
545 | + .size = sizeof(struct pyra_profile_settings), | |
546 | + .read = pyra_sysfs_read_profile4_settings | |
547 | +}; | |
548 | + | |
549 | +static struct bin_attribute pyra_profile5_settings_attr = { | |
550 | + .attr = { .name = "profile5_settings", .mode = 0440 }, | |
551 | + .size = sizeof(struct pyra_profile_settings), | |
552 | + .read = pyra_sysfs_read_profile5_settings | |
553 | +}; | |
554 | + | |
555 | +static struct bin_attribute pyra_profile_buttons_attr = { | |
556 | + .attr = { .name = "profile_buttons", .mode = 0220 }, | |
557 | + .size = sizeof(struct pyra_profile_buttons), | |
558 | + .write = pyra_sysfs_write_profile_buttons | |
559 | +}; | |
560 | + | |
561 | +static struct bin_attribute pyra_profile1_buttons_attr = { | |
562 | + .attr = { .name = "profile1_buttons", .mode = 0440 }, | |
563 | + .size = sizeof(struct pyra_profile_buttons), | |
564 | + .read = pyra_sysfs_read_profile1_buttons | |
565 | +}; | |
566 | + | |
567 | +static struct bin_attribute pyra_profile2_buttons_attr = { | |
568 | + .attr = { .name = "profile2_buttons", .mode = 0440 }, | |
569 | + .size = sizeof(struct pyra_profile_buttons), | |
570 | + .read = pyra_sysfs_read_profile2_buttons | |
571 | +}; | |
572 | + | |
573 | +static struct bin_attribute pyra_profile3_buttons_attr = { | |
574 | + .attr = { .name = "profile3_buttons", .mode = 0440 }, | |
575 | + .size = sizeof(struct pyra_profile_buttons), | |
576 | + .read = pyra_sysfs_read_profile3_buttons | |
577 | +}; | |
578 | + | |
579 | +static struct bin_attribute pyra_profile4_buttons_attr = { | |
580 | + .attr = { .name = "profile4_buttons", .mode = 0440 }, | |
581 | + .size = sizeof(struct pyra_profile_buttons), | |
582 | + .read = pyra_sysfs_read_profile4_buttons | |
583 | +}; | |
584 | + | |
585 | +static struct bin_attribute pyra_profile5_buttons_attr = { | |
586 | + .attr = { .name = "profile5_buttons", .mode = 0440 }, | |
587 | + .size = sizeof(struct pyra_profile_buttons), | |
588 | + .read = pyra_sysfs_read_profile5_buttons | |
589 | +}; | |
590 | + | |
591 | +static struct bin_attribute pyra_settings_attr = { | |
592 | + .attr = { .name = "settings", .mode = 0660 }, | |
593 | + .size = sizeof(struct pyra_settings), | |
594 | + .read = pyra_sysfs_read_settings, | |
595 | + .write = pyra_sysfs_write_settings | |
596 | +}; | |
597 | + | |
598 | +static int pyra_create_sysfs_attributes(struct usb_interface *intf) | |
599 | +{ | |
600 | + int retval; | |
601 | + | |
602 | + retval = sysfs_create_group(&intf->dev.kobj, &pyra_attribute_group); | |
603 | + if (retval) | |
604 | + goto exit_1; | |
605 | + | |
606 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
607 | + &pyra_profile_settings_attr); | |
608 | + if (retval) | |
609 | + goto exit_2; | |
610 | + | |
611 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
612 | + &pyra_profile1_settings_attr); | |
613 | + if (retval) | |
614 | + goto exit_3; | |
615 | + | |
616 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
617 | + &pyra_profile2_settings_attr); | |
618 | + if (retval) | |
619 | + goto exit_4; | |
620 | + | |
621 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
622 | + &pyra_profile3_settings_attr); | |
623 | + if (retval) | |
624 | + goto exit_5; | |
625 | + | |
626 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
627 | + &pyra_profile4_settings_attr); | |
628 | + if (retval) | |
629 | + goto exit_6; | |
630 | + | |
631 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
632 | + &pyra_profile5_settings_attr); | |
633 | + if (retval) | |
634 | + goto exit_7; | |
635 | + | |
636 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
637 | + &pyra_profile_buttons_attr); | |
638 | + if (retval) | |
639 | + goto exit_8; | |
640 | + | |
641 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
642 | + &pyra_profile1_buttons_attr); | |
643 | + if (retval) | |
644 | + goto exit_9; | |
645 | + | |
646 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
647 | + &pyra_profile2_buttons_attr); | |
648 | + if (retval) | |
649 | + goto exit_10; | |
650 | + | |
651 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
652 | + &pyra_profile3_buttons_attr); | |
653 | + if (retval) | |
654 | + goto exit_11; | |
655 | + | |
656 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
657 | + &pyra_profile4_buttons_attr); | |
658 | + if (retval) | |
659 | + goto exit_12; | |
660 | + | |
661 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
662 | + &pyra_profile5_buttons_attr); | |
663 | + if (retval) | |
664 | + goto exit_13; | |
665 | + | |
666 | + retval = sysfs_create_bin_file(&intf->dev.kobj, | |
667 | + &pyra_settings_attr); | |
668 | + if (retval) | |
669 | + goto exit_14; | |
670 | + | |
671 | + return 0; | |
672 | + | |
673 | +exit_14: | |
674 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr); | |
675 | +exit_13: | |
676 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr); | |
677 | +exit_12: | |
678 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr); | |
679 | +exit_11: | |
680 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr); | |
681 | +exit_10: | |
682 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr); | |
683 | +exit_9: | |
684 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr); | |
685 | +exit_8: | |
686 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr); | |
687 | +exit_7: | |
688 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr); | |
689 | +exit_6: | |
690 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr); | |
691 | +exit_5: | |
692 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr); | |
693 | +exit_4: | |
694 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr); | |
695 | +exit_3: | |
696 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr); | |
697 | +exit_2: | |
698 | + sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group); | |
699 | +exit_1: | |
700 | + return retval; | |
701 | +} | |
702 | + | |
703 | +static void pyra_remove_sysfs_attributes(struct usb_interface *intf) | |
704 | +{ | |
705 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_settings_attr); | |
706 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr); | |
707 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr); | |
708 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr); | |
709 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr); | |
710 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr); | |
711 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr); | |
712 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr); | |
713 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr); | |
714 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr); | |
715 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr); | |
716 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr); | |
717 | + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr); | |
718 | + sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group); | |
719 | +} | |
720 | + | |
721 | +static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, | |
722 | + struct pyra_device *pyra) | |
723 | +{ | |
724 | + struct pyra_info *info; | |
725 | + int retval, i; | |
726 | + | |
727 | + mutex_init(&pyra->pyra_lock); | |
728 | + | |
729 | + info = kmalloc(sizeof(struct pyra_info), GFP_KERNEL); | |
730 | + if (!info) | |
731 | + return -ENOMEM; | |
732 | + retval = pyra_get_info(usb_dev, info); | |
733 | + if (retval) { | |
734 | + kfree(info); | |
735 | + return retval; | |
736 | + } | |
737 | + pyra->firmware_version = info->firmware_version; | |
738 | + kfree(info); | |
739 | + | |
740 | + retval = pyra_get_settings(usb_dev, &pyra->settings); | |
741 | + if (retval) | |
742 | + return retval; | |
743 | + | |
744 | + for (i = 0; i < 5; ++i) { | |
745 | + retval = pyra_get_profile_settings(usb_dev, | |
746 | + &pyra->profile_settings[i], i); | |
747 | + if (retval) | |
748 | + return retval; | |
749 | + | |
750 | + retval = pyra_get_profile_buttons(usb_dev, | |
751 | + &pyra->profile_buttons[i], i); | |
752 | + if (retval) | |
753 | + return retval; | |
754 | + } | |
755 | + | |
756 | + profile_activated(pyra, pyra->settings.startup_profile); | |
757 | + | |
758 | + return 0; | |
759 | +} | |
760 | + | |
761 | +static int pyra_init_specials(struct hid_device *hdev) | |
762 | +{ | |
763 | + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | |
764 | + struct usb_device *usb_dev = interface_to_usbdev(intf); | |
765 | + struct pyra_device *pyra; | |
766 | + int retval; | |
767 | + | |
768 | + if (intf->cur_altsetting->desc.bInterfaceProtocol | |
769 | + == USB_INTERFACE_PROTOCOL_MOUSE) { | |
770 | + | |
771 | + pyra = kzalloc(sizeof(*pyra), GFP_KERNEL); | |
772 | + if (!pyra) { | |
773 | + dev_err(&hdev->dev, "can't alloc device descriptor\n"); | |
774 | + return -ENOMEM; | |
775 | + } | |
776 | + hid_set_drvdata(hdev, pyra); | |
777 | + | |
778 | + retval = pyra_init_pyra_device_struct(usb_dev, pyra); | |
779 | + if (retval) { | |
780 | + dev_err(&hdev->dev, | |
781 | + "couldn't init struct pyra_device\n"); | |
782 | + goto exit_free; | |
783 | + } | |
784 | + | |
785 | + retval = roccat_connect(hdev); | |
786 | + if (retval < 0) { | |
787 | + dev_err(&hdev->dev, "couldn't init char dev\n"); | |
788 | + } else { | |
789 | + pyra->chrdev_minor = retval; | |
790 | + pyra->roccat_claimed = 1; | |
791 | + } | |
792 | + | |
793 | + retval = pyra_create_sysfs_attributes(intf); | |
794 | + if (retval) { | |
795 | + dev_err(&hdev->dev, "cannot create sysfs files\n"); | |
796 | + goto exit_free; | |
797 | + } | |
798 | + } else { | |
799 | + hid_set_drvdata(hdev, NULL); | |
800 | + } | |
801 | + | |
802 | + return 0; | |
803 | +exit_free: | |
804 | + kfree(pyra); | |
805 | + return retval; | |
806 | +} | |
807 | + | |
808 | +static void pyra_remove_specials(struct hid_device *hdev) | |
809 | +{ | |
810 | + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | |
811 | + struct pyra_device *pyra; | |
812 | + | |
813 | + if (intf->cur_altsetting->desc.bInterfaceProtocol | |
814 | + == USB_INTERFACE_PROTOCOL_MOUSE) { | |
815 | + pyra_remove_sysfs_attributes(intf); | |
816 | + pyra = hid_get_drvdata(hdev); | |
817 | + if (pyra->roccat_claimed) | |
818 | + roccat_disconnect(pyra->chrdev_minor); | |
819 | + kfree(hid_get_drvdata(hdev)); | |
820 | + } | |
821 | +} | |
822 | + | |
823 | +static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id) | |
824 | +{ | |
825 | + int retval; | |
826 | + | |
827 | + retval = hid_parse(hdev); | |
828 | + if (retval) { | |
829 | + dev_err(&hdev->dev, "parse failed\n"); | |
830 | + goto exit; | |
831 | + } | |
832 | + | |
833 | + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | |
834 | + if (retval) { | |
835 | + dev_err(&hdev->dev, "hw start failed\n"); | |
836 | + goto exit; | |
837 | + } | |
838 | + | |
839 | + retval = pyra_init_specials(hdev); | |
840 | + if (retval) { | |
841 | + dev_err(&hdev->dev, "couldn't install mouse\n"); | |
842 | + goto exit_stop; | |
843 | + } | |
844 | + return 0; | |
845 | + | |
846 | +exit_stop: | |
847 | + hid_hw_stop(hdev); | |
848 | +exit: | |
849 | + return retval; | |
850 | +} | |
851 | + | |
852 | +static void pyra_remove(struct hid_device *hdev) | |
853 | +{ | |
854 | + pyra_remove_specials(hdev); | |
855 | + hid_hw_stop(hdev); | |
856 | +} | |
857 | + | |
858 | +static void pyra_keep_values_up_to_date(struct pyra_device *pyra, | |
859 | + u8 const *data) | |
860 | +{ | |
861 | + struct pyra_mouse_event_button const *button_event; | |
862 | + | |
863 | + switch (data[0]) { | |
864 | + case PYRA_MOUSE_REPORT_NUMBER_BUTTON: | |
865 | + button_event = (struct pyra_mouse_event_button const *)data; | |
866 | + switch (button_event->type) { | |
867 | + case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: | |
868 | + profile_activated(pyra, button_event->data1 - 1); | |
869 | + break; | |
870 | + case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: | |
871 | + pyra->actual_cpi = button_event->data1; | |
872 | + break; | |
873 | + } | |
874 | + break; | |
875 | + } | |
876 | +} | |
877 | + | |
878 | +static void pyra_report_to_chrdev(struct pyra_device const *pyra, | |
879 | + u8 const *data) | |
880 | +{ | |
881 | + struct pyra_roccat_report roccat_report; | |
882 | + struct pyra_mouse_event_button const *button_event; | |
883 | + | |
884 | + if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON) | |
885 | + return; | |
886 | + | |
887 | + button_event = (struct pyra_mouse_event_button const *)data; | |
888 | + | |
889 | + switch (button_event->type) { | |
890 | + case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: | |
891 | + case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: | |
892 | + roccat_report.type = button_event->type; | |
893 | + roccat_report.value = button_event->data1; | |
894 | + roccat_report.key = 0; | |
895 | + roccat_report_event(pyra->chrdev_minor, | |
896 | + (uint8_t const *)&roccat_report, | |
897 | + sizeof(struct pyra_roccat_report)); | |
898 | + break; | |
899 | + case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO: | |
900 | + case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT: | |
901 | + case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH: | |
902 | + if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) { | |
903 | + roccat_report.type = button_event->type; | |
904 | + roccat_report.key = button_event->data1; | |
905 | + /* | |
906 | + * pyra reports profile numbers with range 1-5. | |
907 | + * Keeping this behaviour. | |
908 | + */ | |
909 | + roccat_report.value = pyra->actual_profile + 1; | |
910 | + roccat_report_event(pyra->chrdev_minor, | |
911 | + (uint8_t const *)&roccat_report, | |
912 | + sizeof(struct pyra_roccat_report)); | |
913 | + } | |
914 | + break; | |
915 | + } | |
916 | +} | |
917 | + | |
918 | +static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report, | |
919 | + u8 *data, int size) | |
920 | +{ | |
921 | + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | |
922 | + struct pyra_device *pyra = hid_get_drvdata(hdev); | |
923 | + | |
924 | + if (intf->cur_altsetting->desc.bInterfaceProtocol | |
925 | + != USB_INTERFACE_PROTOCOL_MOUSE) | |
926 | + return 0; | |
927 | + | |
928 | + pyra_keep_values_up_to_date(pyra, data); | |
929 | + | |
930 | + if (pyra->roccat_claimed) | |
931 | + pyra_report_to_chrdev(pyra, data); | |
932 | + | |
933 | + return 0; | |
934 | +} | |
935 | + | |
936 | +static const struct hid_device_id pyra_devices[] = { | |
937 | + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, | |
938 | + USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, | |
939 | + /* TODO add USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS after testing */ | |
940 | + { } | |
941 | +}; | |
942 | + | |
943 | +MODULE_DEVICE_TABLE(hid, pyra_devices); | |
944 | + | |
945 | +static struct hid_driver pyra_driver = { | |
946 | + .name = "pyra", | |
947 | + .id_table = pyra_devices, | |
948 | + .probe = pyra_probe, | |
949 | + .remove = pyra_remove, | |
950 | + .raw_event = pyra_raw_event | |
951 | +}; | |
952 | + | |
953 | +static int __init pyra_init(void) | |
954 | +{ | |
955 | + return hid_register_driver(&pyra_driver); | |
956 | +} | |
957 | + | |
958 | +static void __exit pyra_exit(void) | |
959 | +{ | |
960 | + hid_unregister_driver(&pyra_driver); | |
961 | +} | |
962 | + | |
963 | +module_init(pyra_init); | |
964 | +module_exit(pyra_exit); | |
965 | + | |
966 | +MODULE_AUTHOR("Stefan Achatz"); | |
967 | +MODULE_DESCRIPTION("USB Roccat Pyra driver"); | |
968 | +MODULE_LICENSE("GPL v2"); |
drivers/hid/hid-roccat-pyra.h
1 | +#ifndef __HID_ROCCAT_PYRA_H | |
2 | +#define __HID_ROCCAT_PYRA_H | |
3 | + | |
4 | +/* | |
5 | + * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net> | |
6 | + */ | |
7 | + | |
8 | +/* | |
9 | + * This program is free software; you can redistribute it and/or modify it | |
10 | + * under the terms of the GNU General Public License as published by the Free | |
11 | + * Software Foundation; either version 2 of the License, or (at your option) | |
12 | + * any later version. | |
13 | + */ | |
14 | + | |
15 | +#include <linux/types.h> | |
16 | + | |
17 | +#pragma pack(push) | |
18 | +#pragma pack(1) | |
19 | + | |
20 | +struct pyra_b { | |
21 | + uint8_t command; /* PYRA_COMMAND_B */ | |
22 | + uint8_t size; /* always 3 */ | |
23 | + uint8_t unknown; /* 1 */ | |
24 | +}; | |
25 | + | |
26 | +struct pyra_control { | |
27 | + uint8_t command; /* PYRA_COMMAND_CONTROL */ | |
28 | + /* | |
29 | + * value is profile number for request_settings and request_buttons | |
30 | + * 1 if status ok for request_status | |
31 | + */ | |
32 | + uint8_t value; /* Range 0-4 */ | |
33 | + uint8_t request; | |
34 | +}; | |
35 | + | |
36 | +enum pyra_control_requests { | |
37 | + PYRA_CONTROL_REQUEST_STATUS = 0x00, | |
38 | + PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10, | |
39 | + PYRA_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20 | |
40 | +}; | |
41 | + | |
42 | +struct pyra_settings { | |
43 | + uint8_t command; /* PYRA_COMMAND_SETTINGS */ | |
44 | + uint8_t size; /* always 3 */ | |
45 | + uint8_t startup_profile; /* Range 0-4! */ | |
46 | +}; | |
47 | + | |
48 | +struct pyra_profile_settings { | |
49 | + uint8_t command; /* PYRA_COMMAND_PROFILE_SETTINGS */ | |
50 | + uint8_t size; /* always 0xd */ | |
51 | + uint8_t number; /* Range 0-4 */ | |
52 | + uint8_t xysync; | |
53 | + uint8_t x_sensitivity; /* 0x1-0xa */ | |
54 | + uint8_t y_sensitivity; | |
55 | + uint8_t x_cpi; /* unused */ | |
56 | + uint8_t y_cpi; /* this value is for x and y */ | |
57 | + uint8_t lightswitch; /* 0 = off, 1 = on */ | |
58 | + uint8_t light_effect; | |
59 | + uint8_t handedness; | |
60 | + uint16_t checksum; /* byte sum */ | |
61 | +}; | |
62 | + | |
63 | +struct pyra_profile_buttons { | |
64 | + uint8_t command; /* PYRA_COMMAND_PROFILE_BUTTONS */ | |
65 | + uint8_t size; /* always 0x13 */ | |
66 | + uint8_t number; /* Range 0-4 */ | |
67 | + uint8_t buttons[14]; | |
68 | + uint16_t checksum; /* byte sum */ | |
69 | +}; | |
70 | + | |
71 | +struct pyra_info { | |
72 | + uint8_t command; /* PYRA_COMMAND_INFO */ | |
73 | + uint8_t size; /* always 6 */ | |
74 | + uint8_t firmware_version; | |
75 | + uint8_t unknown1; /* always 0 */ | |
76 | + uint8_t unknown2; /* always 1 */ | |
77 | + uint8_t unknown3; /* always 0 */ | |
78 | +}; | |
79 | + | |
80 | +enum pyra_commands { | |
81 | + PYRA_COMMAND_CONTROL = 0x4, | |
82 | + PYRA_COMMAND_SETTINGS = 0x5, | |
83 | + PYRA_COMMAND_PROFILE_SETTINGS = 0x6, | |
84 | + PYRA_COMMAND_PROFILE_BUTTONS = 0x7, | |
85 | + PYRA_COMMAND_INFO = 0x9, | |
86 | + PYRA_COMMAND_B = 0xb | |
87 | +}; | |
88 | + | |
89 | +enum pyra_usb_commands { | |
90 | + PYRA_USB_COMMAND_CONTROL = 0x304, | |
91 | + PYRA_USB_COMMAND_SETTINGS = 0x305, | |
92 | + PYRA_USB_COMMAND_PROFILE_SETTINGS = 0x306, | |
93 | + PYRA_USB_COMMAND_PROFILE_BUTTONS = 0x307, | |
94 | + PYRA_USB_COMMAND_INFO = 0x309, | |
95 | + PYRA_USB_COMMAND_B = 0x30b /* writes 3 bytes */ | |
96 | +}; | |
97 | + | |
98 | +enum pyra_mouse_report_numbers { | |
99 | + PYRA_MOUSE_REPORT_NUMBER_HID = 1, | |
100 | + PYRA_MOUSE_REPORT_NUMBER_AUDIO = 2, | |
101 | + PYRA_MOUSE_REPORT_NUMBER_BUTTON = 3, | |
102 | +}; | |
103 | + | |
104 | +struct pyra_mouse_event_button { | |
105 | + uint8_t report_number; /* always 3 */ | |
106 | + uint8_t unknown; /* always 0 */ | |
107 | + uint8_t type; | |
108 | + uint8_t data1; | |
109 | + uint8_t data2; | |
110 | +}; | |
111 | + | |
112 | +struct pyra_mouse_event_audio { | |
113 | + uint8_t report_number; /* always 2 */ | |
114 | + uint8_t type; | |
115 | + uint8_t unused; /* always 0 */ | |
116 | +}; | |
117 | + | |
118 | +/* hid audio controls */ | |
119 | +enum pyra_mouse_event_audio_types { | |
120 | + PYRA_MOUSE_EVENT_AUDIO_TYPE_MUTE = 0xe2, | |
121 | + PYRA_MOUSE_EVENT_AUDIO_TYPE_VOLUME_UP = 0xe9, | |
122 | + PYRA_MOUSE_EVENT_AUDIO_TYPE_VOLUME_DOWN = 0xea, | |
123 | +}; | |
124 | + | |
125 | +enum pyra_mouse_event_button_types { | |
126 | + /* | |
127 | + * Mouse sends tilt events on report_number 1 and 3 | |
128 | + * Tilt events are sent repeatedly with 0.94s between first and second | |
129 | + * event and 0.22s on subsequent | |
130 | + */ | |
131 | + PYRA_MOUSE_EVENT_BUTTON_TYPE_TILT = 0x10, | |
132 | + | |
133 | + /* | |
134 | + * These are sent sequentially | |
135 | + * data1 contains new profile number in range 1-5 | |
136 | + */ | |
137 | + PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_1 = 0x20, | |
138 | + PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2 = 0x30, | |
139 | + | |
140 | + /* | |
141 | + * data1 = button_number (rmp index) | |
142 | + * data2 = pressed/released | |
143 | + */ | |
144 | + PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO = 0x40, | |
145 | + PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT = 0x50, | |
146 | + | |
147 | + /* | |
148 | + * data1 = button_number (rmp index) | |
149 | + */ | |
150 | + PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH = 0x60, | |
151 | + | |
152 | + /* data1 = new cpi */ | |
153 | + PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI = 0xb0, | |
154 | + | |
155 | + /* data1 and data2 = new sensitivity */ | |
156 | + PYRA_MOUSE_EVENT_BUTTON_TYPE_SENSITIVITY = 0xc0, | |
157 | + | |
158 | + PYRA_MOUSE_EVENT_BUTTON_TYPE_MULTIMEDIA = 0xf0, | |
159 | +}; | |
160 | + | |
161 | +enum { | |
162 | + PYRA_MOUSE_EVENT_BUTTON_PRESS = 0, | |
163 | + PYRA_MOUSE_EVENT_BUTTON_RELEASE = 1, | |
164 | +}; | |
165 | + | |
166 | +struct pyra_roccat_report { | |
167 | + uint8_t type; | |
168 | + uint8_t value; | |
169 | + uint8_t key; | |
170 | +}; | |
171 | + | |
172 | +#pragma pack(pop) | |
173 | + | |
174 | +struct pyra_device { | |
175 | + int actual_profile; | |
176 | + int actual_cpi; | |
177 | + int firmware_version; | |
178 | + int roccat_claimed; | |
179 | + int chrdev_minor; | |
180 | + struct mutex pyra_lock; | |
181 | + struct pyra_settings settings; | |
182 | + struct pyra_profile_settings profile_settings[5]; | |
183 | + struct pyra_profile_buttons profile_buttons[5]; | |
184 | +}; | |
185 | + | |
186 | +#endif |
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-quirks.c
... | ... | @@ -34,7 +34,6 @@ |
34 | 34 | { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, |
35 | 35 | { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD }, |
36 | 36 | { USB_VENDOR_ID_DWAV, USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER, HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET }, |
37 | - { USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH, HID_QUIRK_MULTI_INPUT }, | |
38 | 37 | { USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER, HID_QUIRK_MULTI_INPUT }, |
39 | 38 | { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART, HID_QUIRK_MULTI_INPUT }, |
40 | 39 | { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, |
include/linux/hid.h
... | ... | @@ -316,6 +316,7 @@ |
316 | 316 | #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 |
317 | 317 | #define HID_QUIRK_NO_INIT_REPORTS 0x20000000 |
318 | 318 | #define HID_QUIRK_NO_IGNORE 0x40000000 |
319 | +#define HID_QUIRK_NO_INPUT_SYNC 0x80000000 | |
319 | 320 | |
320 | 321 | /* |
321 | 322 | * This is the global environment of the parser. This information is |