Blame view
drivers/hid/hid-microsoft.c
12.2 KB
2874c5fd2 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
78a849a68 HID: move microso... |
2 3 4 5 6 7 8 |
/* * HID driver for some microsoft "special" devices * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc * Copyright (c) 2006-2007 Jiri Kosina |
78a849a68 HID: move microso... |
9 10 11 12 |
* Copyright (c) 2008 Jiri Slaby */ /* |
78a849a68 HID: move microso... |
13 14 15 16 17 18 19 20 |
*/ #include <linux/device.h> #include <linux/input.h> #include <linux/hid.h> #include <linux/module.h> #include "hid-ids.h" |
30576c5f4 HID: microsoft: s... |
21 22 23 24 25 26 27 |
#define MS_HIDINPUT BIT(0) #define MS_ERGONOMY BIT(1) #define MS_PRESENTER BIT(2) #define MS_RDESC BIT(3) #define MS_NOGET BIT(4) #define MS_DUPLICATE_USAGES BIT(5) #define MS_SURFACE_DIAL BIT(6) |
73c5b254c HID: microsoft: A... |
28 |
#define MS_QUIRK_FF BIT(7) |
78a849a68 HID: move microso... |
29 |
|
f2d3b625d HID: microsoft: C... |
30 31 |
struct ms_data { unsigned long quirks; |
73c5b254c HID: microsoft: A... |
32 33 34 35 36 |
struct hid_device *hdev; struct work_struct ff_worker; __u8 strong; __u8 weak; void *output_report_dmabuf; |
f2d3b625d HID: microsoft: C... |
37 |
}; |
73c5b254c HID: microsoft: A... |
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#define XB1S_FF_REPORT 3 #define ENABLE_WEAK BIT(0) #define ENABLE_STRONG BIT(1) enum { MAGNITUDE_STRONG = 2, MAGNITUDE_WEAK, MAGNITUDE_NUM }; struct xb1s_ff_report { __u8 report_id; __u8 enable; __u8 magnitude[MAGNITUDE_NUM]; __u8 duration_10ms; __u8 start_delay_10ms; __u8 loop_count; } __packed; |
73e4008dd HID: allow resizi... |
56 57 |
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) |
78a849a68 HID: move microso... |
58 |
{ |
f2d3b625d HID: microsoft: C... |
59 60 |
struct ms_data *ms = hid_get_drvdata(hdev); unsigned long quirks = ms->quirks; |
78a849a68 HID: move microso... |
61 |
|
3ccc60f9d HID: microsoft: f... |
62 63 64 65 |
/* * Microsoft Wireless Desktop Receiver (Model 1028) has * 'Usage Min/Max' where it ought to have 'Physical Min/Max' */ |
73e4008dd HID: allow resizi... |
66 |
if ((quirks & MS_RDESC) && *rsize == 571 && rdesc[557] == 0x19 && |
78a849a68 HID: move microso... |
67 |
rdesc[559] == 0x29) { |
4291ee305 HID: Add and use ... |
68 69 |
hid_info(hdev, "fixing up Microsoft Wireless Receiver Model 1028 report descriptor "); |
0fb21de07 HID: adjust repor... |
70 71 |
rdesc[557] = 0x35; rdesc[559] = 0x45; |
78a849a68 HID: move microso... |
72 |
} |
73e4008dd HID: allow resizi... |
73 |
return rdesc; |
78a849a68 HID: move microso... |
74 75 76 77 78 79 80 81 |
} #define ms_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ EV_KEY, (c)) static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage, unsigned long **bit, int *max) { struct input_dev *input = hi->input; |
34e75dca4 HID: hid-microsof... |
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) { switch (usage->hid & HID_USAGE) { /* * Microsoft uses these 2 reserved usage ids for 2 keys on * the MS office kb labelled "Office Home" and "Task Pane". */ case 0x29d: ms_map_key_clear(KEY_PROG1); return 1; case 0x29e: ms_map_key_clear(KEY_PROG2); return 1; } return 0; } |
aac59f6a9 HID: hid-microsof... |
97 98 |
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) return 0; |
78a849a68 HID: move microso... |
99 100 101 |
switch (usage->hid & HID_USAGE) { case 0xfd06: ms_map_key_clear(KEY_CHAT); break; case 0xfd07: ms_map_key_clear(KEY_PHONE); break; |
3faed1aff HID: hid-microsof... |
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
case 0xff00: /* Special keypad keys */ ms_map_key_clear(KEY_KPEQUAL); set_bit(KEY_KPLEFTPAREN, input->keybit); set_bit(KEY_KPRIGHTPAREN, input->keybit); break; case 0xff01: /* Scroll wheel */ hid_map_usage_clear(hi, usage, bit, max, EV_REL, REL_WHEEL); break; case 0xff02: /* * This byte contains a copy of the modifier keys byte of a * standard hid keyboard report, as send by interface 0 * (this usage is found on interface 1). * * This byte only gets send when another key in the same report * changes state, and as such is useless, ignore it. */ return -1; |
78a849a68 HID: move microso... |
122 123 124 125 126 127 128 129 |
case 0xff05: set_bit(EV_REP, input->evbit); ms_map_key_clear(KEY_F13); set_bit(KEY_F14, input->keybit); set_bit(KEY_F15, input->keybit); set_bit(KEY_F16, input->keybit); set_bit(KEY_F17, input->keybit); set_bit(KEY_F18, input->keybit); |
b2b98ea87 HID: microsoft: n... |
130 |
break; |
78a849a68 HID: move microso... |
131 132 133 134 135 136 137 138 139 |
default: return 0; } return 1; } static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage, unsigned long **bit, int *max) { |
aac59f6a9 HID: hid-microsof... |
140 141 |
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) return 0; |
78a849a68 HID: move microso... |
142 143 144 145 146 147 148 149 150 151 152 153 |
set_bit(EV_REP, hi->input->evbit); switch (usage->hid & HID_USAGE) { case 0xfd08: ms_map_key_clear(KEY_FORWARD); break; case 0xfd09: ms_map_key_clear(KEY_BACK); break; case 0xfd0b: ms_map_key_clear(KEY_PLAYPAUSE); break; case 0xfd0e: ms_map_key_clear(KEY_CLOSE); break; case 0xfd0f: ms_map_key_clear(KEY_PLAY); break; default: return 0; } return 1; } |
30576c5f4 HID: microsoft: s... |
154 155 156 157 158 |
static int ms_surface_dial_quirk(struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { switch (usage->hid & HID_USAGE_PAGE) { case 0xff070000: |
30576c5f4 HID: microsoft: s... |
159 160 161 162 163 164 |
case HID_UP_DIGITIZER: /* ignore those axis */ return -1; case HID_UP_GENDESK: switch (usage->hid) { case HID_GD_X: |
30576c5f4 HID: microsoft: s... |
165 |
case HID_GD_Y: |
30576c5f4 HID: microsoft: s... |
166 167 168 169 170 171 172 173 |
case HID_GD_RFKILL_BTN: /* ignore those axis */ return -1; } } return 0; } |
78a849a68 HID: move microso... |
174 175 176 177 |
static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { |
f2d3b625d HID: microsoft: C... |
178 179 |
struct ms_data *ms = hid_get_drvdata(hdev); unsigned long quirks = ms->quirks; |
78a849a68 HID: move microso... |
180 |
|
78a849a68 HID: move microso... |
181 182 183 184 185 186 187 188 189 |
if (quirks & MS_ERGONOMY) { int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max); if (ret) return ret; } if ((quirks & MS_PRESENTER) && ms_presenter_8k_quirk(hi, usage, bit, max)) return 1; |
30576c5f4 HID: microsoft: s... |
190 191 192 193 194 195 |
if (quirks & MS_SURFACE_DIAL) { int ret = ms_surface_dial_quirk(hi, field, usage, bit, max); if (ret) return ret; } |
78a849a68 HID: move microso... |
196 197 |
return 0; } |
23c10becd HID: fix horizont... |
198 199 200 201 |
static int ms_input_mapped(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { |
f2d3b625d HID: microsoft: C... |
202 203 |
struct ms_data *ms = hid_get_drvdata(hdev); unsigned long quirks = ms->quirks; |
23c10becd HID: fix horizont... |
204 205 206 207 208 209 |
if (quirks & MS_DUPLICATE_USAGES) clear_bit(usage->code, *bit); return 0; } |
78a849a68 HID: move microso... |
210 211 212 |
static int ms_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { |
f2d3b625d HID: microsoft: C... |
213 214 |
struct ms_data *ms = hid_get_drvdata(hdev); unsigned long quirks = ms->quirks; |
3faed1aff HID: hid-microsof... |
215 |
struct input_dev *input; |
78a849a68 HID: move microso... |
216 217 218 219 |
if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || !usage->type) return 0; |
3faed1aff HID: hid-microsof... |
220 |
input = field->hidinput->input; |
78a849a68 HID: move microso... |
221 |
/* Handling MS keyboards special buttons */ |
3faed1aff HID: hid-microsof... |
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff00)) { /* Special keypad keys */ input_report_key(input, KEY_KPEQUAL, value & 0x01); input_report_key(input, KEY_KPLEFTPAREN, value & 0x02); input_report_key(input, KEY_KPRIGHTPAREN, value & 0x04); return 1; } if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff01)) { /* Scroll wheel */ int step = ((value & 0x60) >> 5) + 1; switch (value & 0x1f) { case 0x01: input_report_rel(input, REL_WHEEL, step); break; case 0x1f: input_report_rel(input, REL_WHEEL, -step); break; } return 1; } |
78a849a68 HID: move microso... |
244 |
if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) { |
78a849a68 HID: move microso... |
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
static unsigned int last_key = 0; unsigned int key = 0; switch (value) { case 0x01: key = KEY_F14; break; case 0x02: key = KEY_F15; break; case 0x04: key = KEY_F16; break; case 0x08: key = KEY_F17; break; case 0x10: key = KEY_F18; break; } if (key) { input_event(input, usage->type, key, 1); last_key = key; } else input_event(input, usage->type, last_key, 0); return 1; } return 0; } |
73c5b254c HID: microsoft: A... |
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
static void ms_ff_worker(struct work_struct *work) { struct ms_data *ms = container_of(work, struct ms_data, ff_worker); struct hid_device *hdev = ms->hdev; struct xb1s_ff_report *r = ms->output_report_dmabuf; int ret; memset(r, 0, sizeof(*r)); r->report_id = XB1S_FF_REPORT; r->enable = ENABLE_WEAK | ENABLE_STRONG; /* * Specifying maximum duration and maximum loop count should * cover maximum duration of a single effect, which is 65536 * ms */ r->duration_10ms = U8_MAX; r->loop_count = U8_MAX; r->magnitude[MAGNITUDE_STRONG] = ms->strong; /* left actuator */ r->magnitude[MAGNITUDE_WEAK] = ms->weak; /* right actuator */ ret = hid_hw_output_report(hdev, (__u8 *)r, sizeof(*r)); |
8bb353709 Bluetooth: hidp: ... |
287 |
if (ret < 0) |
73c5b254c HID: microsoft: A... |
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
hid_warn(hdev, "failed to send FF report "); } static int ms_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) { struct hid_device *hid = input_get_drvdata(dev); struct ms_data *ms = hid_get_drvdata(hid); if (effect->type != FF_RUMBLE) return 0; /* * Magnitude is 0..100 so scale the 16-bit input here */ ms->strong = ((u32) effect->u.rumble.strong_magnitude * 100) / U16_MAX; ms->weak = ((u32) effect->u.rumble.weak_magnitude * 100) / U16_MAX; schedule_work(&ms->ff_worker); return 0; } static int ms_init_ff(struct hid_device *hdev) { |
d9d4b1e46 HID: Fix assumpti... |
313 314 |
struct hid_input *hidinput; struct input_dev *input_dev; |
73c5b254c HID: microsoft: A... |
315 |
struct ms_data *ms = hid_get_drvdata(hdev); |
d9d4b1e46 HID: Fix assumpti... |
316 317 318 319 320 321 322 |
if (list_empty(&hdev->inputs)) { hid_err(hdev, "no inputs found "); return -ENODEV; } hidinput = list_entry(hdev->inputs.next, struct hid_input, list); input_dev = hidinput->input; |
73c5b254c HID: microsoft: A... |
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
if (!(ms->quirks & MS_QUIRK_FF)) return 0; ms->hdev = hdev; INIT_WORK(&ms->ff_worker, ms_ff_worker); ms->output_report_dmabuf = devm_kzalloc(&hdev->dev, sizeof(struct xb1s_ff_report), GFP_KERNEL); if (ms->output_report_dmabuf == NULL) return -ENOMEM; input_set_capability(input_dev, EV_FF, FF_RUMBLE); return input_ff_create_memless(input_dev, NULL, ms_play_effect); } static void ms_remove_ff(struct hid_device *hdev) { struct ms_data *ms = hid_get_drvdata(hdev); if (!(ms->quirks & MS_QUIRK_FF)) return; cancel_work_sync(&ms->ff_worker); } |
78a849a68 HID: move microso... |
348 349 350 |
static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id) { unsigned long quirks = id->driver_data; |
f2d3b625d HID: microsoft: C... |
351 |
struct ms_data *ms; |
78a849a68 HID: move microso... |
352 |
int ret; |
f2d3b625d HID: microsoft: C... |
353 354 355 356 357 358 359 |
ms = devm_kzalloc(&hdev->dev, sizeof(*ms), GFP_KERNEL); if (ms == NULL) return -ENOMEM; ms->quirks = quirks; hid_set_drvdata(hdev, ms); |
78a849a68 HID: move microso... |
360 |
|
78a849a68 HID: move microso... |
361 362 |
if (quirks & MS_NOGET) hdev->quirks |= HID_QUIRK_NOGET; |
30576c5f4 HID: microsoft: s... |
363 364 |
if (quirks & MS_SURFACE_DIAL) hdev->quirks |= HID_QUIRK_INPUT_PER_APP; |
78a849a68 HID: move microso... |
365 366 |
ret = hid_parse(hdev); if (ret) { |
4291ee305 HID: Add and use ... |
367 368 |
hid_err(hdev, "parse failed "); |
78a849a68 HID: move microso... |
369 370 |
goto err_free; } |
93c10132a HID: move connect... |
371 372 |
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ((quirks & MS_HIDINPUT) ? HID_CONNECT_HIDINPUT_FORCE : 0)); |
78a849a68 HID: move microso... |
373 |
if (ret) { |
4291ee305 HID: Add and use ... |
374 375 |
hid_err(hdev, "hw start failed "); |
78a849a68 HID: move microso... |
376 377 |
goto err_free; } |
73c5b254c HID: microsoft: A... |
378 379 380 |
ret = ms_init_ff(hdev); if (ret) hid_err(hdev, "could not initialize ff, continuing anyway"); |
78a849a68 HID: move microso... |
381 382 383 384 |
return 0; err_free: return ret; } |
73c5b254c HID: microsoft: A... |
385 386 387 388 389 |
static void ms_remove(struct hid_device *hdev) { hid_hw_stop(hdev); ms_remove_ff(hdev); } |
78a849a68 HID: move microso... |
390 391 392 |
static const struct hid_device_id ms_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV), .driver_data = MS_HIDINPUT }, |
3faed1aff HID: hid-microsof... |
393 394 |
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB), .driver_data = MS_ERGONOMY }, |
78a849a68 HID: move microso... |
395 396 |
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K), .driver_data = MS_ERGONOMY }, |
89759e20d HID: Add PID for ... |
397 398 |
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP), .driver_data = MS_ERGONOMY }, |
ef567cf9d HID: microsoft: A... |
399 400 |
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE7K), .driver_data = MS_ERGONOMY }, |
78a849a68 HID: move microso... |
401 402 403 404 |
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K), .driver_data = MS_ERGONOMY | MS_RDESC }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), .driver_data = MS_PRESENTER }, |
f3b83d71a HID: add support ... |
405 |
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K), |
52dc085a5 Revert "HID: micr... |
406 |
.driver_data = MS_ERGONOMY }, |
c847a89a8 HID: microsoft: a... |
407 408 409 410 411 412 |
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K), .driver_data = MS_ERGONOMY }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600), .driver_data = MS_ERGONOMY }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1), .driver_data = MS_ERGONOMY }, |
78a849a68 HID: move microso... |
413 414 |
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0), .driver_data = MS_NOGET }, |
23c10becd HID: fix horizont... |
415 416 |
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500), .driver_data = MS_DUPLICATE_USAGES }, |
18eec2cd7 HID: microsoft: A... |
417 418 |
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER), .driver_data = MS_HIDINPUT }, |
f9a82c205 HID: microsoft: A... |
419 420 |
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD), .driver_data = MS_ERGONOMY}, |
78a849a68 HID: move microso... |
421 422 423 |
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), .driver_data = MS_PRESENTER }, |
30576c5f4 HID: microsoft: s... |
424 425 |
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B), .driver_data = MS_SURFACE_DIAL }, |
73c5b254c HID: microsoft: A... |
426 427 |
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER), .driver_data = MS_QUIRK_FF }, |
724a419ea HID: microsoft: A... |
428 429 |
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_8BITDO_SN30_PRO_PLUS), .driver_data = MS_QUIRK_FF }, |
78a849a68 HID: move microso... |
430 431 432 433 434 435 436 437 438 |
{ } }; MODULE_DEVICE_TABLE(hid, ms_devices); static struct hid_driver ms_driver = { .name = "microsoft", .id_table = ms_devices, .report_fixup = ms_report_fixup, .input_mapping = ms_input_mapping, |
23c10becd HID: fix horizont... |
439 |
.input_mapped = ms_input_mapped, |
78a849a68 HID: move microso... |
440 441 |
.event = ms_event, .probe = ms_probe, |
73c5b254c HID: microsoft: A... |
442 |
.remove = ms_remove, |
78a849a68 HID: move microso... |
443 |
}; |
f425458ea HID: Use module_h... |
444 |
module_hid_driver(ms_driver); |
78a849a68 HID: move microso... |
445 |
|
78a849a68 HID: move microso... |
446 |
MODULE_LICENSE("GPL"); |