1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * HID driver for Google Fiber TV Box remote controls |
4 | * |
5 | * Copyright (c) 2014-2015 Google Inc. |
6 | * |
7 | * Author: Petri Gynther <pgynther@google.com> |
8 | */ |
9 | #include <linux/device.h> |
10 | #include <linux/hid.h> |
11 | #include <linux/input.h> |
12 | #include <linux/module.h> |
13 | |
14 | #include "hid-ids.h" |
15 | |
16 | #define GFRM100 1 /* Google Fiber GFRM100 (Bluetooth classic) */ |
17 | #define GFRM200 2 /* Google Fiber GFRM200 (Bluetooth LE) */ |
18 | |
19 | #define GFRM100_SEARCH_KEY_REPORT_ID 0xF7 |
20 | #define GFRM100_SEARCH_KEY_DOWN 0x0 |
21 | #define GFRM100_SEARCH_KEY_AUDIO_DATA 0x1 |
22 | #define GFRM100_SEARCH_KEY_UP 0x2 |
23 | |
24 | static u8 search_key_dn[3] = {0x40, 0x21, 0x02}; |
25 | static u8 search_key_up[3] = {0x40, 0x00, 0x00}; |
26 | |
27 | static int gfrm_input_mapping(struct hid_device *hdev, struct hid_input *hi, |
28 | struct hid_field *field, struct hid_usage *usage, |
29 | unsigned long **bit, int *max) |
30 | { |
31 | unsigned long hdev_type = (unsigned long) hid_get_drvdata(hdev); |
32 | |
33 | if (hdev_type == GFRM100) { |
34 | if (usage->hid == (HID_UP_CONSUMER | 0x4)) { |
35 | /* Consumer.0004 -> KEY_INFO */ |
36 | hid_map_usage_clear(hidinput: hi, usage, bit, max, EV_KEY, KEY_INFO); |
37 | return 1; |
38 | } |
39 | |
40 | if (usage->hid == (HID_UP_CONSUMER | 0x41)) { |
41 | /* Consumer.0041 -> KEY_OK */ |
42 | hid_map_usage_clear(hidinput: hi, usage, bit, max, EV_KEY, KEY_OK); |
43 | return 1; |
44 | } |
45 | } |
46 | |
47 | return 0; |
48 | } |
49 | |
50 | static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report, |
51 | u8 *data, int size) |
52 | { |
53 | unsigned long hdev_type = (unsigned long) hid_get_drvdata(hdev); |
54 | int ret = 0; |
55 | |
56 | if (hdev_type != GFRM100) |
57 | return 0; |
58 | |
59 | if (size < 2 || data[0] != GFRM100_SEARCH_KEY_REPORT_ID) |
60 | return 0; |
61 | |
62 | /* |
63 | * Convert GFRM100 Search key reports into Consumer.0221 (Key.Search) |
64 | * reports. Ignore audio data. |
65 | */ |
66 | switch (data[1]) { |
67 | case GFRM100_SEARCH_KEY_DOWN: |
68 | ret = hid_report_raw_event(hid: hdev, type: HID_INPUT_REPORT, data: search_key_dn, |
69 | size: sizeof(search_key_dn), interrupt: 1); |
70 | break; |
71 | |
72 | case GFRM100_SEARCH_KEY_AUDIO_DATA: |
73 | break; |
74 | |
75 | case GFRM100_SEARCH_KEY_UP: |
76 | ret = hid_report_raw_event(hid: hdev, type: HID_INPUT_REPORT, data: search_key_up, |
77 | size: sizeof(search_key_up), interrupt: 1); |
78 | break; |
79 | |
80 | default: |
81 | break; |
82 | } |
83 | |
84 | return (ret < 0) ? ret : -1; |
85 | } |
86 | |
87 | static int gfrm_input_configured(struct hid_device *hid, struct hid_input *hidinput) |
88 | { |
89 | /* |
90 | * Enable software autorepeat with: |
91 | * - repeat delay: 400 msec |
92 | * - repeat period: 100 msec |
93 | */ |
94 | input_enable_softrepeat(dev: hidinput->input, delay: 400, period: 100); |
95 | return 0; |
96 | } |
97 | |
98 | static int gfrm_probe(struct hid_device *hdev, const struct hid_device_id *id) |
99 | { |
100 | int ret; |
101 | |
102 | hid_set_drvdata(hdev, data: (void *) id->driver_data); |
103 | |
104 | ret = hid_parse(hdev); |
105 | if (ret) |
106 | goto done; |
107 | |
108 | if (id->driver_data == GFRM100) { |
109 | /* |
110 | * GFRM100 HID Report Descriptor does not describe the Search |
111 | * key reports. Thus, we need to add it manually here, so that |
112 | * those reports reach gfrm_raw_event() from hid_input_report(). |
113 | */ |
114 | if (!hid_register_report(device: hdev, type: HID_INPUT_REPORT, |
115 | GFRM100_SEARCH_KEY_REPORT_ID, application: 0)) { |
116 | ret = -ENOMEM; |
117 | goto done; |
118 | } |
119 | } |
120 | |
121 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
122 | done: |
123 | return ret; |
124 | } |
125 | |
126 | static const struct hid_device_id gfrm_devices[] = { |
127 | { HID_BLUETOOTH_DEVICE(0x58, 0x2000), |
128 | .driver_data = GFRM100 }, |
129 | { HID_BLUETOOTH_DEVICE(0x471, 0x2210), |
130 | .driver_data = GFRM200 }, |
131 | { } |
132 | }; |
133 | MODULE_DEVICE_TABLE(hid, gfrm_devices); |
134 | |
135 | static struct hid_driver gfrm_driver = { |
136 | .name = "gfrm" , |
137 | .id_table = gfrm_devices, |
138 | .probe = gfrm_probe, |
139 | .input_mapping = gfrm_input_mapping, |
140 | .raw_event = gfrm_raw_event, |
141 | .input_configured = gfrm_input_configured, |
142 | }; |
143 | |
144 | module_hid_driver(gfrm_driver); |
145 | |
146 | MODULE_AUTHOR("Petri Gynther <pgynther@google.com>" ); |
147 | MODULE_DESCRIPTION("Google Fiber TV Box remote control driver" ); |
148 | MODULE_LICENSE("GPL" ); |
149 | |