1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
4 | * |
5 | * HID driver for NVIDIA SHIELD peripherals. |
6 | */ |
7 | |
8 | #include <linux/hid.h> |
9 | #include <linux/idr.h> |
10 | #include <linux/input-event-codes.h> |
11 | #include <linux/input.h> |
12 | #include <linux/jiffies.h> |
13 | #include <linux/leds.h> |
14 | #include <linux/module.h> |
15 | #include <linux/power_supply.h> |
16 | #include <linux/spinlock.h> |
17 | #include <linux/timer.h> |
18 | #include <linux/workqueue.h> |
19 | |
20 | #include "hid-ids.h" |
21 | |
22 | #define NOT_INIT_STR "NOT INITIALIZED" |
23 | #define android_map_key(c) hid_map_usage(hi, usage, bit, max, EV_KEY, (c)) |
24 | |
25 | enum { |
26 | HID_USAGE_ANDROID_PLAYPAUSE_BTN = 0xcd, /* Double-tap volume slider */ |
27 | HID_USAGE_ANDROID_VOLUMEUP_BTN = 0xe9, |
28 | HID_USAGE_ANDROID_VOLUMEDOWN_BTN = 0xea, |
29 | HID_USAGE_ANDROID_SEARCH_BTN = 0x221, /* NVIDIA btn on Thunderstrike */ |
30 | HID_USAGE_ANDROID_HOME_BTN = 0x223, |
31 | HID_USAGE_ANDROID_BACK_BTN = 0x224, |
32 | }; |
33 | |
34 | enum { |
35 | SHIELD_FW_VERSION_INITIALIZED = 0, |
36 | SHIELD_BOARD_INFO_INITIALIZED, |
37 | SHIELD_BATTERY_STATS_INITIALIZED, |
38 | SHIELD_CHARGER_STATE_INITIALIZED, |
39 | }; |
40 | |
41 | enum { |
42 | THUNDERSTRIKE_FW_VERSION_UPDATE = 0, |
43 | THUNDERSTRIKE_BOARD_INFO_UPDATE, |
44 | THUNDERSTRIKE_HAPTICS_UPDATE, |
45 | THUNDERSTRIKE_LED_UPDATE, |
46 | THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE, |
47 | }; |
48 | |
49 | enum { |
50 | THUNDERSTRIKE_HOSTCMD_REPORT_SIZE = 33, |
51 | THUNDERSTRIKE_HOSTCMD_REQ_REPORT_ID = 0x4, |
52 | THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID = 0x3, |
53 | }; |
54 | |
55 | enum { |
56 | THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION = 1, |
57 | THUNDERSTRIKE_HOSTCMD_ID_LED = 6, |
58 | THUNDERSTRIKE_HOSTCMD_ID_BATTERY, |
59 | THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO = 16, |
60 | THUNDERSTRIKE_HOSTCMD_ID_USB_INIT = 53, |
61 | THUNDERSTRIKE_HOSTCMD_ID_HAPTICS = 57, |
62 | THUNDERSTRIKE_HOSTCMD_ID_CHARGER, |
63 | }; |
64 | |
65 | struct power_supply_dev { |
66 | struct power_supply *psy; |
67 | struct power_supply_desc desc; |
68 | }; |
69 | |
70 | struct thunderstrike_psy_prop_values { |
71 | int voltage_min; |
72 | int voltage_now; |
73 | int voltage_avg; |
74 | int voltage_boot; |
75 | int capacity; |
76 | int status; |
77 | int charge_type; |
78 | int temp; |
79 | }; |
80 | |
81 | static const enum power_supply_property thunderstrike_battery_props[] = { |
82 | POWER_SUPPLY_PROP_STATUS, |
83 | POWER_SUPPLY_PROP_CHARGE_TYPE, |
84 | POWER_SUPPLY_PROP_PRESENT, |
85 | POWER_SUPPLY_PROP_VOLTAGE_MIN, |
86 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, |
87 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, |
88 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
89 | POWER_SUPPLY_PROP_VOLTAGE_AVG, |
90 | POWER_SUPPLY_PROP_VOLTAGE_BOOT, |
91 | POWER_SUPPLY_PROP_CAPACITY, |
92 | POWER_SUPPLY_PROP_SCOPE, |
93 | POWER_SUPPLY_PROP_TEMP, |
94 | POWER_SUPPLY_PROP_TEMP_MIN, |
95 | POWER_SUPPLY_PROP_TEMP_MAX, |
96 | POWER_SUPPLY_PROP_TEMP_ALERT_MIN, |
97 | POWER_SUPPLY_PROP_TEMP_ALERT_MAX, |
98 | }; |
99 | |
100 | enum thunderstrike_led_state { |
101 | THUNDERSTRIKE_LED_OFF = 1, |
102 | THUNDERSTRIKE_LED_ON = 8, |
103 | } __packed; |
104 | static_assert(sizeof(enum thunderstrike_led_state) == 1); |
105 | |
106 | struct thunderstrike_hostcmd_battery { |
107 | __le16 voltage_avg; |
108 | u8 reserved_at_10; |
109 | __le16 thermistor; |
110 | __le16 voltage_min; |
111 | __le16 voltage_boot; |
112 | __le16 voltage_now; |
113 | u8 capacity; |
114 | } __packed; |
115 | |
116 | enum thunderstrike_charger_type { |
117 | THUNDERSTRIKE_CHARGER_TYPE_NONE = 0, |
118 | THUNDERSTRIKE_CHARGER_TYPE_TRICKLE, |
119 | THUNDERSTRIKE_CHARGER_TYPE_NORMAL, |
120 | } __packed; |
121 | static_assert(sizeof(enum thunderstrike_charger_type) == 1); |
122 | |
123 | enum thunderstrike_charger_state { |
124 | THUNDERSTRIKE_CHARGER_STATE_UNKNOWN = 0, |
125 | THUNDERSTRIKE_CHARGER_STATE_DISABLED, |
126 | THUNDERSTRIKE_CHARGER_STATE_CHARGING, |
127 | THUNDERSTRIKE_CHARGER_STATE_FULL, |
128 | THUNDERSTRIKE_CHARGER_STATE_FAILED = 8, |
129 | } __packed; |
130 | static_assert(sizeof(enum thunderstrike_charger_state) == 1); |
131 | |
132 | struct thunderstrike_hostcmd_charger { |
133 | u8 connected; |
134 | enum thunderstrike_charger_type type; |
135 | enum thunderstrike_charger_state state; |
136 | } __packed; |
137 | |
138 | struct thunderstrike_hostcmd_board_info { |
139 | __le16 revision; |
140 | __le16 serial[7]; |
141 | } __packed; |
142 | |
143 | struct thunderstrike_hostcmd_haptics { |
144 | u8 motor_left; |
145 | u8 motor_right; |
146 | } __packed; |
147 | |
148 | struct thunderstrike_hostcmd_resp_report { |
149 | u8 report_id; /* THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID */ |
150 | u8 cmd_id; |
151 | u8 reserved_at_10; |
152 | |
153 | union { |
154 | struct thunderstrike_hostcmd_board_info board_info; |
155 | struct thunderstrike_hostcmd_haptics motors; |
156 | __le16 fw_version; |
157 | enum thunderstrike_led_state led_state; |
158 | struct thunderstrike_hostcmd_battery battery; |
159 | struct thunderstrike_hostcmd_charger charger; |
160 | u8 payload[30]; |
161 | } __packed; |
162 | } __packed; |
163 | static_assert(sizeof(struct thunderstrike_hostcmd_resp_report) == |
164 | THUNDERSTRIKE_HOSTCMD_REPORT_SIZE); |
165 | |
166 | struct thunderstrike_hostcmd_req_report { |
167 | u8 report_id; /* THUNDERSTRIKE_HOSTCMD_REQ_REPORT_ID */ |
168 | u8 cmd_id; |
169 | u8 reserved_at_10; |
170 | |
171 | union { |
172 | struct __packed { |
173 | u8 update; |
174 | enum thunderstrike_led_state state; |
175 | } led; |
176 | struct __packed { |
177 | u8 update; |
178 | struct thunderstrike_hostcmd_haptics motors; |
179 | } haptics; |
180 | } __packed; |
181 | u8 reserved_at_30[27]; |
182 | } __packed; |
183 | static_assert(sizeof(struct thunderstrike_hostcmd_req_report) == |
184 | THUNDERSTRIKE_HOSTCMD_REPORT_SIZE); |
185 | |
186 | /* Common struct for shield accessories. */ |
187 | struct shield_device { |
188 | struct hid_device *hdev; |
189 | struct power_supply_dev battery_dev; |
190 | |
191 | unsigned long initialized_flags; |
192 | const char *codename; |
193 | u16 fw_version; |
194 | struct { |
195 | u16 revision; |
196 | char serial_number[15]; |
197 | } board_info; |
198 | }; |
199 | |
200 | /* |
201 | * Non-trivial to uniquely identify Thunderstrike controllers at initialization |
202 | * time. Use an ID allocator to help with this. |
203 | */ |
204 | static DEFINE_IDA(thunderstrike_ida); |
205 | |
206 | struct thunderstrike { |
207 | struct shield_device base; |
208 | |
209 | int id; |
210 | |
211 | /* Sub-devices */ |
212 | struct input_dev *haptics_dev; |
213 | struct led_classdev led_dev; |
214 | |
215 | /* Resources */ |
216 | void *req_report_dmabuf; |
217 | unsigned long update_flags; |
218 | struct thunderstrike_hostcmd_haptics haptics_val; |
219 | spinlock_t haptics_update_lock; |
220 | u8 led_state : 1; |
221 | enum thunderstrike_led_state led_value; |
222 | struct thunderstrike_psy_prop_values psy_stats; |
223 | spinlock_t psy_stats_lock; |
224 | struct timer_list psy_stats_timer; |
225 | struct work_struct hostcmd_req_work; |
226 | }; |
227 | |
228 | static inline void thunderstrike_hostcmd_req_report_init( |
229 | struct thunderstrike_hostcmd_req_report *report, u8 cmd_id) |
230 | { |
231 | memset(report, 0, sizeof(*report)); |
232 | report->report_id = THUNDERSTRIKE_HOSTCMD_REQ_REPORT_ID; |
233 | report->cmd_id = cmd_id; |
234 | } |
235 | |
236 | static inline void shield_strrev(char *dest, size_t len, u16 rev) |
237 | { |
238 | dest[0] = ('A' - 1) + (rev >> 8); |
239 | snprintf(buf: &dest[1], size: len - 1, fmt: "%02X" , 0xff & rev); |
240 | } |
241 | |
242 | static struct input_dev *shield_allocate_input_dev(struct hid_device *hdev, |
243 | const char *name_suffix) |
244 | { |
245 | struct input_dev *idev; |
246 | |
247 | idev = input_allocate_device(); |
248 | if (!idev) |
249 | goto err_device; |
250 | |
251 | idev->id.bustype = hdev->bus; |
252 | idev->id.vendor = hdev->vendor; |
253 | idev->id.product = hdev->product; |
254 | idev->id.version = hdev->version; |
255 | idev->uniq = hdev->uniq; |
256 | idev->name = devm_kasprintf(dev: &hdev->dev, GFP_KERNEL, fmt: "%s %s" , hdev->name, |
257 | name_suffix); |
258 | if (!idev->name) |
259 | goto err_name; |
260 | |
261 | input_set_drvdata(dev: idev, data: hdev); |
262 | |
263 | return idev; |
264 | |
265 | err_name: |
266 | input_free_device(dev: idev); |
267 | err_device: |
268 | return ERR_PTR(error: -ENOMEM); |
269 | } |
270 | |
271 | static struct input_dev *shield_haptics_create( |
272 | struct shield_device *dev, |
273 | int (*play_effect)(struct input_dev *, void *, struct ff_effect *)) |
274 | { |
275 | struct input_dev *haptics; |
276 | int ret; |
277 | |
278 | if (!IS_ENABLED(CONFIG_NVIDIA_SHIELD_FF)) |
279 | return NULL; |
280 | |
281 | haptics = shield_allocate_input_dev(hdev: dev->hdev, name_suffix: "Haptics" ); |
282 | if (IS_ERR(ptr: haptics)) |
283 | return haptics; |
284 | |
285 | input_set_capability(dev: haptics, EV_FF, FF_RUMBLE); |
286 | input_ff_create_memless(dev: haptics, NULL, play_effect); |
287 | |
288 | ret = input_register_device(haptics); |
289 | if (ret) |
290 | goto err; |
291 | |
292 | return haptics; |
293 | |
294 | err: |
295 | input_free_device(dev: haptics); |
296 | return ERR_PTR(error: ret); |
297 | } |
298 | |
299 | static inline void thunderstrike_send_hostcmd_request(struct thunderstrike *ts) |
300 | { |
301 | struct thunderstrike_hostcmd_req_report *report = ts->req_report_dmabuf; |
302 | struct shield_device *shield_dev = &ts->base; |
303 | int ret; |
304 | |
305 | ret = hid_hw_raw_request(hdev: shield_dev->hdev, reportnum: report->report_id, |
306 | buf: ts->req_report_dmabuf, |
307 | len: THUNDERSTRIKE_HOSTCMD_REPORT_SIZE, |
308 | rtype: HID_OUTPUT_REPORT, reqtype: HID_REQ_SET_REPORT); |
309 | |
310 | if (ret < 0) { |
311 | hid_err(shield_dev->hdev, |
312 | "Failed to output Thunderstrike HOSTCMD request HID report due to %pe\n" , |
313 | ERR_PTR(ret)); |
314 | } |
315 | } |
316 | |
317 | static void thunderstrike_hostcmd_req_work_handler(struct work_struct *work) |
318 | { |
319 | struct thunderstrike *ts = |
320 | container_of(work, struct thunderstrike, hostcmd_req_work); |
321 | struct thunderstrike_hostcmd_req_report *report; |
322 | unsigned long flags; |
323 | |
324 | report = ts->req_report_dmabuf; |
325 | |
326 | if (test_and_clear_bit(nr: THUNDERSTRIKE_FW_VERSION_UPDATE, addr: &ts->update_flags)) { |
327 | thunderstrike_hostcmd_req_report_init( |
328 | report, cmd_id: THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION); |
329 | thunderstrike_send_hostcmd_request(ts); |
330 | } |
331 | |
332 | if (test_and_clear_bit(nr: THUNDERSTRIKE_LED_UPDATE, addr: &ts->update_flags)) { |
333 | thunderstrike_hostcmd_req_report_init(report, cmd_id: THUNDERSTRIKE_HOSTCMD_ID_LED); |
334 | report->led.update = 1; |
335 | report->led.state = ts->led_value; |
336 | thunderstrike_send_hostcmd_request(ts); |
337 | } |
338 | |
339 | if (test_and_clear_bit(nr: THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE, addr: &ts->update_flags)) { |
340 | thunderstrike_hostcmd_req_report_init( |
341 | report, cmd_id: THUNDERSTRIKE_HOSTCMD_ID_BATTERY); |
342 | thunderstrike_send_hostcmd_request(ts); |
343 | |
344 | thunderstrike_hostcmd_req_report_init( |
345 | report, cmd_id: THUNDERSTRIKE_HOSTCMD_ID_CHARGER); |
346 | thunderstrike_send_hostcmd_request(ts); |
347 | } |
348 | |
349 | if (test_and_clear_bit(nr: THUNDERSTRIKE_BOARD_INFO_UPDATE, addr: &ts->update_flags)) { |
350 | thunderstrike_hostcmd_req_report_init( |
351 | report, cmd_id: THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO); |
352 | thunderstrike_send_hostcmd_request(ts); |
353 | } |
354 | |
355 | if (test_and_clear_bit(nr: THUNDERSTRIKE_HAPTICS_UPDATE, addr: &ts->update_flags)) { |
356 | thunderstrike_hostcmd_req_report_init( |
357 | report, cmd_id: THUNDERSTRIKE_HOSTCMD_ID_HAPTICS); |
358 | |
359 | report->haptics.update = 1; |
360 | spin_lock_irqsave(&ts->haptics_update_lock, flags); |
361 | report->haptics.motors = ts->haptics_val; |
362 | spin_unlock_irqrestore(lock: &ts->haptics_update_lock, flags); |
363 | |
364 | thunderstrike_send_hostcmd_request(ts); |
365 | } |
366 | } |
367 | |
368 | static inline void thunderstrike_request_firmware_version(struct thunderstrike *ts) |
369 | { |
370 | set_bit(nr: THUNDERSTRIKE_FW_VERSION_UPDATE, addr: &ts->update_flags); |
371 | schedule_work(work: &ts->hostcmd_req_work); |
372 | } |
373 | |
374 | static inline void thunderstrike_request_board_info(struct thunderstrike *ts) |
375 | { |
376 | set_bit(nr: THUNDERSTRIKE_BOARD_INFO_UPDATE, addr: &ts->update_flags); |
377 | schedule_work(work: &ts->hostcmd_req_work); |
378 | } |
379 | |
380 | static inline int |
381 | thunderstrike_update_haptics(struct thunderstrike *ts, |
382 | struct thunderstrike_hostcmd_haptics *motors) |
383 | { |
384 | unsigned long flags; |
385 | |
386 | spin_lock_irqsave(&ts->haptics_update_lock, flags); |
387 | ts->haptics_val = *motors; |
388 | spin_unlock_irqrestore(lock: &ts->haptics_update_lock, flags); |
389 | |
390 | set_bit(nr: THUNDERSTRIKE_HAPTICS_UPDATE, addr: &ts->update_flags); |
391 | schedule_work(work: &ts->hostcmd_req_work); |
392 | |
393 | return 0; |
394 | } |
395 | |
396 | static int thunderstrike_play_effect(struct input_dev *idev, void *data, |
397 | struct ff_effect *effect) |
398 | { |
399 | struct hid_device *hdev = input_get_drvdata(dev: idev); |
400 | struct thunderstrike_hostcmd_haptics motors; |
401 | struct shield_device *shield_dev; |
402 | struct thunderstrike *ts; |
403 | |
404 | if (effect->type != FF_RUMBLE) |
405 | return 0; |
406 | |
407 | shield_dev = hid_get_drvdata(hdev); |
408 | ts = container_of(shield_dev, struct thunderstrike, base); |
409 | |
410 | /* Thunderstrike motor values range from 0 to 32 inclusively */ |
411 | motors.motor_left = effect->u.rumble.strong_magnitude / 2047; |
412 | motors.motor_right = effect->u.rumble.weak_magnitude / 2047; |
413 | |
414 | hid_dbg(hdev, "Thunderstrike FF_RUMBLE request, left: %u right: %u\n" , |
415 | motors.motor_left, motors.motor_right); |
416 | |
417 | return thunderstrike_update_haptics(ts, motors: &motors); |
418 | } |
419 | |
420 | static enum led_brightness |
421 | thunderstrike_led_get_brightness(struct led_classdev *led) |
422 | { |
423 | struct hid_device *hdev = to_hid_device(led->dev->parent); |
424 | struct shield_device *shield_dev = hid_get_drvdata(hdev); |
425 | struct thunderstrike *ts; |
426 | |
427 | ts = container_of(shield_dev, struct thunderstrike, base); |
428 | |
429 | return ts->led_state; |
430 | } |
431 | |
432 | static void thunderstrike_led_set_brightness(struct led_classdev *led, |
433 | enum led_brightness value) |
434 | { |
435 | struct hid_device *hdev = to_hid_device(led->dev->parent); |
436 | struct shield_device *shield_dev = hid_get_drvdata(hdev); |
437 | struct thunderstrike *ts; |
438 | |
439 | ts = container_of(shield_dev, struct thunderstrike, base); |
440 | |
441 | switch (value) { |
442 | case LED_OFF: |
443 | ts->led_value = THUNDERSTRIKE_LED_OFF; |
444 | break; |
445 | default: |
446 | ts->led_value = THUNDERSTRIKE_LED_ON; |
447 | break; |
448 | } |
449 | |
450 | set_bit(nr: THUNDERSTRIKE_LED_UPDATE, addr: &ts->update_flags); |
451 | schedule_work(work: &ts->hostcmd_req_work); |
452 | } |
453 | |
454 | static int thunderstrike_battery_get_property(struct power_supply *psy, |
455 | enum power_supply_property psp, |
456 | union power_supply_propval *val) |
457 | { |
458 | struct shield_device *shield_dev = power_supply_get_drvdata(psy); |
459 | struct thunderstrike_psy_prop_values prop_values; |
460 | struct thunderstrike *ts; |
461 | int ret = 0; |
462 | |
463 | ts = container_of(shield_dev, struct thunderstrike, base); |
464 | spin_lock(lock: &ts->psy_stats_lock); |
465 | prop_values = ts->psy_stats; |
466 | spin_unlock(lock: &ts->psy_stats_lock); |
467 | |
468 | switch (psp) { |
469 | case POWER_SUPPLY_PROP_STATUS: |
470 | val->intval = prop_values.status; |
471 | break; |
472 | case POWER_SUPPLY_PROP_CHARGE_TYPE: |
473 | val->intval = prop_values.charge_type; |
474 | break; |
475 | case POWER_SUPPLY_PROP_PRESENT: |
476 | val->intval = 1; |
477 | break; |
478 | case POWER_SUPPLY_PROP_VOLTAGE_MIN: |
479 | val->intval = prop_values.voltage_min; |
480 | break; |
481 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: |
482 | val->intval = 2900000; /* 2.9 V */ |
483 | break; |
484 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: |
485 | val->intval = 2200000; /* 2.2 V */ |
486 | break; |
487 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
488 | val->intval = prop_values.voltage_now; |
489 | break; |
490 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: |
491 | val->intval = prop_values.voltage_avg; |
492 | break; |
493 | case POWER_SUPPLY_PROP_VOLTAGE_BOOT: |
494 | val->intval = prop_values.voltage_boot; |
495 | break; |
496 | case POWER_SUPPLY_PROP_CAPACITY: |
497 | val->intval = prop_values.capacity; |
498 | break; |
499 | case POWER_SUPPLY_PROP_SCOPE: |
500 | val->intval = POWER_SUPPLY_SCOPE_DEVICE; |
501 | break; |
502 | case POWER_SUPPLY_PROP_TEMP: |
503 | val->intval = prop_values.temp; |
504 | break; |
505 | case POWER_SUPPLY_PROP_TEMP_MIN: |
506 | val->intval = 0; /* 0 C */ |
507 | break; |
508 | case POWER_SUPPLY_PROP_TEMP_MAX: |
509 | val->intval = 400; /* 40 C */ |
510 | break; |
511 | case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: |
512 | val->intval = 15; /* 1.5 C */ |
513 | break; |
514 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: |
515 | val->intval = 380; /* 38 C */ |
516 | break; |
517 | default: |
518 | ret = -EINVAL; |
519 | break; |
520 | } |
521 | |
522 | return ret; |
523 | } |
524 | |
525 | static inline void thunderstrike_request_psy_stats(struct thunderstrike *ts) |
526 | { |
527 | set_bit(nr: THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE, addr: &ts->update_flags); |
528 | schedule_work(work: &ts->hostcmd_req_work); |
529 | } |
530 | |
531 | static void thunderstrike_psy_stats_timer_handler(struct timer_list *timer) |
532 | { |
533 | struct thunderstrike *ts = |
534 | container_of(timer, struct thunderstrike, psy_stats_timer); |
535 | |
536 | thunderstrike_request_psy_stats(ts); |
537 | /* Query battery statistics from device every five minutes */ |
538 | mod_timer(timer, expires: jiffies + 300 * HZ); |
539 | } |
540 | |
541 | static void |
542 | thunderstrike_parse_fw_version_payload(struct shield_device *shield_dev, |
543 | __le16 fw_version) |
544 | { |
545 | shield_dev->fw_version = le16_to_cpu(fw_version); |
546 | |
547 | set_bit(nr: SHIELD_FW_VERSION_INITIALIZED, addr: &shield_dev->initialized_flags); |
548 | |
549 | hid_dbg(shield_dev->hdev, "Thunderstrike firmware version 0x%04X\n" , |
550 | shield_dev->fw_version); |
551 | } |
552 | |
553 | static void |
554 | thunderstrike_parse_board_info_payload(struct shield_device *shield_dev, |
555 | struct thunderstrike_hostcmd_board_info *board_info) |
556 | { |
557 | char board_revision_str[4]; |
558 | int i; |
559 | |
560 | shield_dev->board_info.revision = le16_to_cpu(board_info->revision); |
561 | for (i = 0; i < 7; ++i) { |
562 | u16 val = le16_to_cpu(board_info->serial[i]); |
563 | |
564 | shield_dev->board_info.serial_number[2 * i] = val & 0xFF; |
565 | shield_dev->board_info.serial_number[2 * i + 1] = val >> 8; |
566 | } |
567 | shield_dev->board_info.serial_number[14] = '\0'; |
568 | |
569 | set_bit(nr: SHIELD_BOARD_INFO_INITIALIZED, addr: &shield_dev->initialized_flags); |
570 | |
571 | shield_strrev(dest: board_revision_str, len: 4, rev: shield_dev->board_info.revision); |
572 | hid_dbg(shield_dev->hdev, |
573 | "Thunderstrike BOARD_REVISION_%s (0x%04X) S/N: %s\n" , |
574 | board_revision_str, shield_dev->board_info.revision, |
575 | shield_dev->board_info.serial_number); |
576 | } |
577 | |
578 | static inline void |
579 | thunderstrike_parse_haptics_payload(struct shield_device *shield_dev, |
580 | struct thunderstrike_hostcmd_haptics *haptics) |
581 | { |
582 | hid_dbg(shield_dev->hdev, |
583 | "Thunderstrike haptics HOSTCMD response, left: %u right: %u\n" , |
584 | haptics->motor_left, haptics->motor_right); |
585 | } |
586 | |
587 | static void |
588 | thunderstrike_parse_led_payload(struct shield_device *shield_dev, |
589 | enum thunderstrike_led_state led_state) |
590 | { |
591 | struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base); |
592 | |
593 | switch (led_state) { |
594 | case THUNDERSTRIKE_LED_OFF: |
595 | ts->led_state = 0; |
596 | break; |
597 | case THUNDERSTRIKE_LED_ON: |
598 | ts->led_state = 1; |
599 | break; |
600 | } |
601 | |
602 | hid_dbg(shield_dev->hdev, "Thunderstrike led HOSTCMD response, 0x%02X\n" , led_state); |
603 | } |
604 | |
605 | static void thunderstrike_parse_battery_payload( |
606 | struct shield_device *shield_dev, |
607 | struct thunderstrike_hostcmd_battery *battery) |
608 | { |
609 | struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base); |
610 | u16 hostcmd_voltage_boot = le16_to_cpu(battery->voltage_boot); |
611 | u16 hostcmd_voltage_avg = le16_to_cpu(battery->voltage_avg); |
612 | u16 hostcmd_voltage_min = le16_to_cpu(battery->voltage_min); |
613 | u16 hostcmd_voltage_now = le16_to_cpu(battery->voltage_now); |
614 | u16 hostcmd_thermistor = le16_to_cpu(battery->thermistor); |
615 | int voltage_boot, voltage_avg, voltage_min, voltage_now; |
616 | struct hid_device *hdev = shield_dev->hdev; |
617 | u8 capacity = battery->capacity; |
618 | int temp; |
619 | |
620 | /* Convert thunderstrike device values to µV and tenths of degree Celsius */ |
621 | voltage_boot = hostcmd_voltage_boot * 1000; |
622 | voltage_avg = hostcmd_voltage_avg * 1000; |
623 | voltage_min = hostcmd_voltage_min * 1000; |
624 | voltage_now = hostcmd_voltage_now * 1000; |
625 | temp = (1378 - (int)hostcmd_thermistor) * 10 / 19; |
626 | |
627 | /* Copy converted values */ |
628 | spin_lock(lock: &ts->psy_stats_lock); |
629 | ts->psy_stats.voltage_boot = voltage_boot; |
630 | ts->psy_stats.voltage_avg = voltage_avg; |
631 | ts->psy_stats.voltage_min = voltage_min; |
632 | ts->psy_stats.voltage_now = voltage_now; |
633 | ts->psy_stats.capacity = capacity; |
634 | ts->psy_stats.temp = temp; |
635 | spin_unlock(lock: &ts->psy_stats_lock); |
636 | |
637 | set_bit(nr: SHIELD_BATTERY_STATS_INITIALIZED, addr: &shield_dev->initialized_flags); |
638 | |
639 | hid_dbg(hdev, |
640 | "Thunderstrike battery HOSTCMD response, voltage_avg: %u voltage_now: %u\n" , |
641 | hostcmd_voltage_avg, hostcmd_voltage_now); |
642 | hid_dbg(hdev, |
643 | "Thunderstrike battery HOSTCMD response, voltage_boot: %u voltage_min: %u\n" , |
644 | hostcmd_voltage_boot, hostcmd_voltage_min); |
645 | hid_dbg(hdev, |
646 | "Thunderstrike battery HOSTCMD response, thermistor: %u\n" , |
647 | hostcmd_thermistor); |
648 | hid_dbg(hdev, |
649 | "Thunderstrike battery HOSTCMD response, capacity: %u%%\n" , |
650 | capacity); |
651 | } |
652 | |
653 | static void thunderstrike_parse_charger_payload( |
654 | struct shield_device *shield_dev, |
655 | struct thunderstrike_hostcmd_charger *charger) |
656 | { |
657 | struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base); |
658 | int charge_type = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; |
659 | struct hid_device *hdev = shield_dev->hdev; |
660 | int status = POWER_SUPPLY_STATUS_UNKNOWN; |
661 | |
662 | switch (charger->type) { |
663 | case THUNDERSTRIKE_CHARGER_TYPE_NONE: |
664 | charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE; |
665 | break; |
666 | case THUNDERSTRIKE_CHARGER_TYPE_TRICKLE: |
667 | charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; |
668 | break; |
669 | case THUNDERSTRIKE_CHARGER_TYPE_NORMAL: |
670 | charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD; |
671 | break; |
672 | default: |
673 | hid_warn(hdev, "Unhandled Thunderstrike charger HOSTCMD type, %u\n" , |
674 | charger->type); |
675 | break; |
676 | } |
677 | |
678 | switch (charger->state) { |
679 | case THUNDERSTRIKE_CHARGER_STATE_UNKNOWN: |
680 | status = POWER_SUPPLY_STATUS_UNKNOWN; |
681 | break; |
682 | case THUNDERSTRIKE_CHARGER_STATE_DISABLED: |
683 | /* Indicates charger is disconnected */ |
684 | break; |
685 | case THUNDERSTRIKE_CHARGER_STATE_CHARGING: |
686 | status = POWER_SUPPLY_STATUS_CHARGING; |
687 | break; |
688 | case THUNDERSTRIKE_CHARGER_STATE_FULL: |
689 | status = POWER_SUPPLY_STATUS_FULL; |
690 | break; |
691 | case THUNDERSTRIKE_CHARGER_STATE_FAILED: |
692 | status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
693 | hid_err(hdev, "Thunderstrike device failed to charge\n" ); |
694 | break; |
695 | default: |
696 | hid_warn(hdev, "Unhandled Thunderstrike charger HOSTCMD state, %u\n" , |
697 | charger->state); |
698 | break; |
699 | } |
700 | |
701 | if (!charger->connected) |
702 | status = POWER_SUPPLY_STATUS_DISCHARGING; |
703 | |
704 | spin_lock(lock: &ts->psy_stats_lock); |
705 | ts->psy_stats.charge_type = charge_type; |
706 | ts->psy_stats.status = status; |
707 | spin_unlock(lock: &ts->psy_stats_lock); |
708 | |
709 | set_bit(nr: SHIELD_CHARGER_STATE_INITIALIZED, addr: &shield_dev->initialized_flags); |
710 | |
711 | hid_dbg(hdev, |
712 | "Thunderstrike charger HOSTCMD response, connected: %u, type: %u, state: %u\n" , |
713 | charger->connected, charger->type, charger->state); |
714 | } |
715 | |
716 | static inline void thunderstrike_device_init_info(struct shield_device *shield_dev) |
717 | { |
718 | struct thunderstrike *ts = |
719 | container_of(shield_dev, struct thunderstrike, base); |
720 | |
721 | if (!test_bit(SHIELD_FW_VERSION_INITIALIZED, &shield_dev->initialized_flags)) |
722 | thunderstrike_request_firmware_version(ts); |
723 | |
724 | if (!test_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags)) |
725 | thunderstrike_request_board_info(ts); |
726 | |
727 | if (!test_bit(SHIELD_BATTERY_STATS_INITIALIZED, &shield_dev->initialized_flags) || |
728 | !test_bit(SHIELD_CHARGER_STATE_INITIALIZED, &shield_dev->initialized_flags)) |
729 | thunderstrike_psy_stats_timer_handler(timer: &ts->psy_stats_timer); |
730 | } |
731 | |
732 | static int thunderstrike_parse_report(struct shield_device *shield_dev, |
733 | struct hid_report *report, u8 *data, |
734 | int size) |
735 | { |
736 | struct thunderstrike_hostcmd_resp_report *hostcmd_resp_report; |
737 | struct hid_device *hdev = shield_dev->hdev; |
738 | |
739 | switch (report->id) { |
740 | case THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID: |
741 | if (size != THUNDERSTRIKE_HOSTCMD_REPORT_SIZE) { |
742 | hid_err(hdev, |
743 | "Encountered Thunderstrike HOSTCMD HID report with unexpected size %d\n" , |
744 | size); |
745 | return -EINVAL; |
746 | } |
747 | |
748 | hostcmd_resp_report = |
749 | (struct thunderstrike_hostcmd_resp_report *)data; |
750 | |
751 | switch (hostcmd_resp_report->cmd_id) { |
752 | case THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION: |
753 | thunderstrike_parse_fw_version_payload( |
754 | shield_dev, fw_version: hostcmd_resp_report->fw_version); |
755 | break; |
756 | case THUNDERSTRIKE_HOSTCMD_ID_LED: |
757 | thunderstrike_parse_led_payload(shield_dev, led_state: hostcmd_resp_report->led_state); |
758 | break; |
759 | case THUNDERSTRIKE_HOSTCMD_ID_BATTERY: |
760 | thunderstrike_parse_battery_payload(shield_dev, |
761 | battery: &hostcmd_resp_report->battery); |
762 | break; |
763 | case THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO: |
764 | thunderstrike_parse_board_info_payload( |
765 | shield_dev, board_info: &hostcmd_resp_report->board_info); |
766 | break; |
767 | case THUNDERSTRIKE_HOSTCMD_ID_HAPTICS: |
768 | thunderstrike_parse_haptics_payload( |
769 | shield_dev, haptics: &hostcmd_resp_report->motors); |
770 | break; |
771 | case THUNDERSTRIKE_HOSTCMD_ID_USB_INIT: |
772 | /* May block HOSTCMD requests till received initially */ |
773 | thunderstrike_device_init_info(shield_dev); |
774 | break; |
775 | case THUNDERSTRIKE_HOSTCMD_ID_CHARGER: |
776 | /* May block HOSTCMD requests till received initially */ |
777 | thunderstrike_device_init_info(shield_dev); |
778 | |
779 | thunderstrike_parse_charger_payload( |
780 | shield_dev, charger: &hostcmd_resp_report->charger); |
781 | break; |
782 | default: |
783 | hid_warn(hdev, |
784 | "Unhandled Thunderstrike HOSTCMD id %d\n" , |
785 | hostcmd_resp_report->cmd_id); |
786 | return -ENOENT; |
787 | } |
788 | |
789 | break; |
790 | default: |
791 | return 0; |
792 | } |
793 | |
794 | return 0; |
795 | } |
796 | |
797 | static inline int thunderstrike_led_create(struct thunderstrike *ts) |
798 | { |
799 | struct led_classdev *led = &ts->led_dev; |
800 | |
801 | led->name = devm_kasprintf(dev: &ts->base.hdev->dev, GFP_KERNEL, |
802 | fmt: "thunderstrike%d:blue:led" , ts->id); |
803 | if (!led->name) |
804 | return -ENOMEM; |
805 | led->max_brightness = 1; |
806 | led->flags = LED_CORE_SUSPENDRESUME | LED_RETAIN_AT_SHUTDOWN; |
807 | led->brightness_get = &thunderstrike_led_get_brightness; |
808 | led->brightness_set = &thunderstrike_led_set_brightness; |
809 | |
810 | return led_classdev_register(parent: &ts->base.hdev->dev, led_cdev: led); |
811 | } |
812 | |
813 | static inline int thunderstrike_psy_create(struct shield_device *shield_dev) |
814 | { |
815 | struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base); |
816 | struct power_supply_config psy_cfg = { .drv_data = shield_dev, }; |
817 | struct hid_device *hdev = shield_dev->hdev; |
818 | int ret; |
819 | |
820 | /* |
821 | * Set an initial capacity and temperature value to avoid prematurely |
822 | * triggering alerts. Will be replaced by values queried from initial |
823 | * HOSTCMD requests. |
824 | */ |
825 | ts->psy_stats.capacity = 100; |
826 | ts->psy_stats.temp = 182; |
827 | |
828 | shield_dev->battery_dev.desc.properties = thunderstrike_battery_props; |
829 | shield_dev->battery_dev.desc.num_properties = |
830 | ARRAY_SIZE(thunderstrike_battery_props); |
831 | shield_dev->battery_dev.desc.get_property = thunderstrike_battery_get_property; |
832 | shield_dev->battery_dev.desc.type = POWER_SUPPLY_TYPE_BATTERY; |
833 | shield_dev->battery_dev.desc.name = |
834 | devm_kasprintf(dev: &ts->base.hdev->dev, GFP_KERNEL, |
835 | fmt: "thunderstrike_%d" , ts->id); |
836 | if (!shield_dev->battery_dev.desc.name) |
837 | return -ENOMEM; |
838 | |
839 | shield_dev->battery_dev.psy = power_supply_register( |
840 | parent: &hdev->dev, desc: &shield_dev->battery_dev.desc, cfg: &psy_cfg); |
841 | if (IS_ERR(ptr: shield_dev->battery_dev.psy)) { |
842 | hid_err(hdev, "Failed to register Thunderstrike battery device\n" ); |
843 | return PTR_ERR(ptr: shield_dev->battery_dev.psy); |
844 | } |
845 | |
846 | ret = power_supply_powers(psy: shield_dev->battery_dev.psy, dev: &hdev->dev); |
847 | if (ret) { |
848 | hid_err(hdev, "Failed to associate battery device to Thunderstrike\n" ); |
849 | goto err; |
850 | } |
851 | |
852 | return 0; |
853 | |
854 | err: |
855 | power_supply_unregister(psy: shield_dev->battery_dev.psy); |
856 | return ret; |
857 | } |
858 | |
859 | static struct shield_device *thunderstrike_create(struct hid_device *hdev) |
860 | { |
861 | struct shield_device *shield_dev; |
862 | struct thunderstrike *ts; |
863 | int ret; |
864 | |
865 | ts = devm_kzalloc(dev: &hdev->dev, size: sizeof(*ts), GFP_KERNEL); |
866 | if (!ts) |
867 | return ERR_PTR(error: -ENOMEM); |
868 | |
869 | ts->req_report_dmabuf = devm_kzalloc( |
870 | dev: &hdev->dev, size: THUNDERSTRIKE_HOSTCMD_REPORT_SIZE, GFP_KERNEL); |
871 | if (!ts->req_report_dmabuf) |
872 | return ERR_PTR(error: -ENOMEM); |
873 | |
874 | shield_dev = &ts->base; |
875 | shield_dev->hdev = hdev; |
876 | shield_dev->codename = "Thunderstrike" ; |
877 | |
878 | spin_lock_init(&ts->haptics_update_lock); |
879 | spin_lock_init(&ts->psy_stats_lock); |
880 | INIT_WORK(&ts->hostcmd_req_work, thunderstrike_hostcmd_req_work_handler); |
881 | |
882 | hid_set_drvdata(hdev, data: shield_dev); |
883 | |
884 | ts->id = ida_alloc(ida: &thunderstrike_ida, GFP_KERNEL); |
885 | if (ts->id < 0) |
886 | return ERR_PTR(error: ts->id); |
887 | |
888 | ts->haptics_dev = shield_haptics_create(dev: shield_dev, play_effect: thunderstrike_play_effect); |
889 | if (IS_ERR(ptr: ts->haptics_dev)) { |
890 | hid_err(hdev, "Failed to create Thunderstrike haptics instance\n" ); |
891 | ret = PTR_ERR(ptr: ts->haptics_dev); |
892 | goto err_id; |
893 | } |
894 | |
895 | ret = thunderstrike_psy_create(shield_dev); |
896 | if (ret) { |
897 | hid_err(hdev, "Failed to create Thunderstrike power supply instance\n" ); |
898 | goto err_haptics; |
899 | } |
900 | |
901 | ret = thunderstrike_led_create(ts); |
902 | if (ret) { |
903 | hid_err(hdev, "Failed to create Thunderstrike LED instance\n" ); |
904 | goto err_psy; |
905 | } |
906 | |
907 | timer_setup(&ts->psy_stats_timer, thunderstrike_psy_stats_timer_handler, 0); |
908 | |
909 | hid_info(hdev, "Registered Thunderstrike controller\n" ); |
910 | return shield_dev; |
911 | |
912 | err_psy: |
913 | power_supply_unregister(psy: shield_dev->battery_dev.psy); |
914 | err_haptics: |
915 | if (ts->haptics_dev) |
916 | input_unregister_device(ts->haptics_dev); |
917 | err_id: |
918 | ida_free(&thunderstrike_ida, id: ts->id); |
919 | return ERR_PTR(error: ret); |
920 | } |
921 | |
922 | static void thunderstrike_destroy(struct thunderstrike *ts) |
923 | { |
924 | led_classdev_unregister(led_cdev: &ts->led_dev); |
925 | power_supply_unregister(psy: ts->base.battery_dev.psy); |
926 | if (ts->haptics_dev) |
927 | input_unregister_device(ts->haptics_dev); |
928 | ida_free(&thunderstrike_ida, id: ts->id); |
929 | } |
930 | |
931 | static int android_input_mapping(struct hid_device *hdev, struct hid_input *hi, |
932 | struct hid_field *field, |
933 | struct hid_usage *usage, unsigned long **bit, |
934 | int *max) |
935 | { |
936 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) |
937 | return 0; |
938 | |
939 | switch (usage->hid & HID_USAGE) { |
940 | case HID_USAGE_ANDROID_PLAYPAUSE_BTN: |
941 | android_map_key(KEY_PLAYPAUSE); |
942 | break; |
943 | case HID_USAGE_ANDROID_VOLUMEUP_BTN: |
944 | android_map_key(KEY_VOLUMEUP); |
945 | break; |
946 | case HID_USAGE_ANDROID_VOLUMEDOWN_BTN: |
947 | android_map_key(KEY_VOLUMEDOWN); |
948 | break; |
949 | case HID_USAGE_ANDROID_SEARCH_BTN: |
950 | android_map_key(BTN_Z); |
951 | break; |
952 | case HID_USAGE_ANDROID_HOME_BTN: |
953 | android_map_key(BTN_MODE); |
954 | break; |
955 | case HID_USAGE_ANDROID_BACK_BTN: |
956 | android_map_key(BTN_SELECT); |
957 | break; |
958 | default: |
959 | return 0; |
960 | } |
961 | |
962 | return 1; |
963 | } |
964 | |
965 | static ssize_t firmware_version_show(struct device *dev, |
966 | struct device_attribute *attr, char *buf) |
967 | { |
968 | struct hid_device *hdev = to_hid_device(dev); |
969 | struct shield_device *shield_dev; |
970 | int ret; |
971 | |
972 | shield_dev = hid_get_drvdata(hdev); |
973 | |
974 | if (test_bit(SHIELD_FW_VERSION_INITIALIZED, &shield_dev->initialized_flags)) |
975 | ret = sysfs_emit(buf, fmt: "0x%04X\n" , shield_dev->fw_version); |
976 | else |
977 | ret = sysfs_emit(buf, NOT_INIT_STR "\n" ); |
978 | |
979 | return ret; |
980 | } |
981 | |
982 | static DEVICE_ATTR_RO(firmware_version); |
983 | |
984 | static ssize_t hardware_version_show(struct device *dev, |
985 | struct device_attribute *attr, char *buf) |
986 | { |
987 | struct hid_device *hdev = to_hid_device(dev); |
988 | struct shield_device *shield_dev; |
989 | char board_revision_str[4]; |
990 | int ret; |
991 | |
992 | shield_dev = hid_get_drvdata(hdev); |
993 | |
994 | if (test_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags)) { |
995 | shield_strrev(dest: board_revision_str, len: 4, rev: shield_dev->board_info.revision); |
996 | ret = sysfs_emit(buf, fmt: "%s BOARD_REVISION_%s (0x%04X)\n" , |
997 | shield_dev->codename, board_revision_str, |
998 | shield_dev->board_info.revision); |
999 | } else |
1000 | ret = sysfs_emit(buf, NOT_INIT_STR "\n" ); |
1001 | |
1002 | return ret; |
1003 | } |
1004 | |
1005 | static DEVICE_ATTR_RO(hardware_version); |
1006 | |
1007 | static ssize_t serial_number_show(struct device *dev, |
1008 | struct device_attribute *attr, char *buf) |
1009 | { |
1010 | struct hid_device *hdev = to_hid_device(dev); |
1011 | struct shield_device *shield_dev; |
1012 | int ret; |
1013 | |
1014 | shield_dev = hid_get_drvdata(hdev); |
1015 | |
1016 | if (test_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags)) |
1017 | ret = sysfs_emit(buf, fmt: "%s\n" , shield_dev->board_info.serial_number); |
1018 | else |
1019 | ret = sysfs_emit(buf, NOT_INIT_STR "\n" ); |
1020 | |
1021 | return ret; |
1022 | } |
1023 | |
1024 | static DEVICE_ATTR_RO(serial_number); |
1025 | |
1026 | static struct attribute *shield_device_attrs[] = { |
1027 | &dev_attr_firmware_version.attr, |
1028 | &dev_attr_hardware_version.attr, |
1029 | &dev_attr_serial_number.attr, |
1030 | NULL, |
1031 | }; |
1032 | ATTRIBUTE_GROUPS(shield_device); |
1033 | |
1034 | static int shield_raw_event(struct hid_device *hdev, struct hid_report *report, |
1035 | u8 *data, int size) |
1036 | { |
1037 | struct shield_device *dev = hid_get_drvdata(hdev); |
1038 | |
1039 | return thunderstrike_parse_report(shield_dev: dev, report, data, size); |
1040 | } |
1041 | |
1042 | static int shield_probe(struct hid_device *hdev, const struct hid_device_id *id) |
1043 | { |
1044 | struct shield_device *shield_dev = NULL; |
1045 | struct thunderstrike *ts; |
1046 | int ret; |
1047 | |
1048 | ret = hid_parse(hdev); |
1049 | if (ret) { |
1050 | hid_err(hdev, "Parse failed\n" ); |
1051 | return ret; |
1052 | } |
1053 | |
1054 | switch (id->product) { |
1055 | case USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE_CONTROLLER: |
1056 | shield_dev = thunderstrike_create(hdev); |
1057 | break; |
1058 | } |
1059 | |
1060 | if (unlikely(!shield_dev)) { |
1061 | hid_err(hdev, "Failed to identify SHIELD device\n" ); |
1062 | return -ENODEV; |
1063 | } |
1064 | if (IS_ERR(ptr: shield_dev)) { |
1065 | hid_err(hdev, "Failed to create SHIELD device\n" ); |
1066 | return PTR_ERR(ptr: shield_dev); |
1067 | } |
1068 | |
1069 | ts = container_of(shield_dev, struct thunderstrike, base); |
1070 | |
1071 | ret = hid_hw_start(hdev, HID_CONNECT_HIDINPUT); |
1072 | if (ret) { |
1073 | hid_err(hdev, "Failed to start HID device\n" ); |
1074 | goto err_ts_create; |
1075 | } |
1076 | |
1077 | ret = hid_hw_open(hdev); |
1078 | if (ret) { |
1079 | hid_err(hdev, "Failed to open HID device\n" ); |
1080 | goto err_stop; |
1081 | } |
1082 | |
1083 | thunderstrike_device_init_info(shield_dev); |
1084 | |
1085 | return ret; |
1086 | |
1087 | err_stop: |
1088 | hid_hw_stop(hdev); |
1089 | err_ts_create: |
1090 | thunderstrike_destroy(ts); |
1091 | return ret; |
1092 | } |
1093 | |
1094 | static void shield_remove(struct hid_device *hdev) |
1095 | { |
1096 | struct shield_device *dev = hid_get_drvdata(hdev); |
1097 | struct thunderstrike *ts; |
1098 | |
1099 | ts = container_of(dev, struct thunderstrike, base); |
1100 | |
1101 | hid_hw_close(hdev); |
1102 | thunderstrike_destroy(ts); |
1103 | del_timer_sync(timer: &ts->psy_stats_timer); |
1104 | cancel_work_sync(work: &ts->hostcmd_req_work); |
1105 | hid_hw_stop(hdev); |
1106 | } |
1107 | |
1108 | static const struct hid_device_id shield_devices[] = { |
1109 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NVIDIA, |
1110 | USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE_CONTROLLER) }, |
1111 | { HID_USB_DEVICE(USB_VENDOR_ID_NVIDIA, |
1112 | USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE_CONTROLLER) }, |
1113 | { } |
1114 | }; |
1115 | MODULE_DEVICE_TABLE(hid, shield_devices); |
1116 | |
1117 | static struct hid_driver shield_driver = { |
1118 | .name = "shield" , |
1119 | .id_table = shield_devices, |
1120 | .input_mapping = android_input_mapping, |
1121 | .probe = shield_probe, |
1122 | .remove = shield_remove, |
1123 | .raw_event = shield_raw_event, |
1124 | .driver = { |
1125 | .dev_groups = shield_device_groups, |
1126 | }, |
1127 | }; |
1128 | module_hid_driver(shield_driver); |
1129 | |
1130 | MODULE_AUTHOR("Rahul Rameshbabu <rrameshbabu@nvidia.com>" ); |
1131 | MODULE_DESCRIPTION("HID Driver for NVIDIA SHIELD peripherals." ); |
1132 | MODULE_LICENSE("GPL" ); |
1133 | |