1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ISHTP-HID glue driver. |
4 | * |
5 | * Copyright (c) 2012-2016, Intel Corporation. |
6 | */ |
7 | |
8 | #include <linux/hid.h> |
9 | #include <linux/intel-ish-client-if.h> |
10 | #include <uapi/linux/input.h> |
11 | #include "ishtp-hid.h" |
12 | |
13 | /** |
14 | * ishtp_hid_parse() - hid-core .parse() callback |
15 | * @hid: hid device instance |
16 | * |
17 | * This function gets called during call to hid_add_device |
18 | * |
19 | * Return: 0 on success and non zero on error |
20 | */ |
21 | static int ishtp_hid_parse(struct hid_device *hid) |
22 | { |
23 | struct ishtp_hid_data *hid_data = hid->driver_data; |
24 | struct ishtp_cl_data *client_data = hid_data->client_data; |
25 | int rv; |
26 | |
27 | rv = hid_parse_report(hid, start: client_data->report_descr[hid_data->index], |
28 | size: client_data->report_descr_size[hid_data->index]); |
29 | if (rv) |
30 | return rv; |
31 | |
32 | return 0; |
33 | } |
34 | |
35 | /* Empty callbacks with success return code */ |
36 | static int ishtp_hid_start(struct hid_device *hid) |
37 | { |
38 | return 0; |
39 | } |
40 | |
41 | static void ishtp_hid_stop(struct hid_device *hid) |
42 | { |
43 | } |
44 | |
45 | static int ishtp_hid_open(struct hid_device *hid) |
46 | { |
47 | return 0; |
48 | } |
49 | |
50 | static void ishtp_hid_close(struct hid_device *hid) |
51 | { |
52 | } |
53 | |
54 | static int ishtp_raw_request(struct hid_device *hid, unsigned char reportnum, |
55 | __u8 *buf, size_t len, unsigned char rtype, |
56 | int reqtype) |
57 | { |
58 | struct ishtp_hid_data *hid_data = hid->driver_data; |
59 | char *ishtp_buf = NULL; |
60 | size_t ishtp_buf_len; |
61 | unsigned int = sizeof(struct hostif_msg); |
62 | |
63 | if (rtype == HID_OUTPUT_REPORT) |
64 | return -EINVAL; |
65 | |
66 | hid_data->request_done = false; |
67 | switch (reqtype) { |
68 | case HID_REQ_GET_REPORT: |
69 | hid_data->raw_buf = buf; |
70 | hid_data->raw_buf_size = len; |
71 | hid_data->raw_get_req = true; |
72 | |
73 | hid_ishtp_get_report(hid, report_id: reportnum, report_type: rtype); |
74 | break; |
75 | case HID_REQ_SET_REPORT: |
76 | /* |
77 | * Spare 7 bytes for 64b accesses through |
78 | * get/put_unaligned_le64() |
79 | */ |
80 | ishtp_buf_len = len + header_size; |
81 | ishtp_buf = kzalloc(size: ishtp_buf_len + 7, GFP_KERNEL); |
82 | if (!ishtp_buf) |
83 | return -ENOMEM; |
84 | |
85 | memcpy(ishtp_buf + header_size, buf, len); |
86 | hid_ishtp_set_feature(hid, buf: ishtp_buf, len: ishtp_buf_len, report_id: reportnum); |
87 | kfree(objp: ishtp_buf); |
88 | break; |
89 | } |
90 | |
91 | hid_hw_wait(hdev: hid); |
92 | |
93 | return len; |
94 | } |
95 | |
96 | /** |
97 | * ishtp_hid_request() - hid-core .request() callback |
98 | * @hid: hid device instance |
99 | * @rep: pointer to hid_report |
100 | * @reqtype: type of req. [GET|SET]_REPORT |
101 | * |
102 | * This function is used to set/get feaure/input report. |
103 | */ |
104 | static void ishtp_hid_request(struct hid_device *hid, struct hid_report *rep, |
105 | int reqtype) |
106 | { |
107 | struct ishtp_hid_data *hid_data = hid->driver_data; |
108 | /* the specific report length, just HID part of it */ |
109 | unsigned int len = ((rep->size - 1) >> 3) + 1 + (rep->id > 0); |
110 | char *buf; |
111 | unsigned int = sizeof(struct hostif_msg); |
112 | |
113 | len += header_size; |
114 | |
115 | hid_data->request_done = false; |
116 | switch (reqtype) { |
117 | case HID_REQ_GET_REPORT: |
118 | hid_data->raw_get_req = false; |
119 | hid_ishtp_get_report(hid, report_id: rep->id, report_type: rep->type); |
120 | break; |
121 | case HID_REQ_SET_REPORT: |
122 | /* |
123 | * Spare 7 bytes for 64b accesses through |
124 | * get/put_unaligned_le64() |
125 | */ |
126 | buf = kzalloc(size: len + 7, GFP_KERNEL); |
127 | if (!buf) |
128 | return; |
129 | |
130 | hid_output_report(report: rep, data: buf + header_size); |
131 | hid_ishtp_set_feature(hid, buf, len, report_id: rep->id); |
132 | kfree(objp: buf); |
133 | break; |
134 | } |
135 | } |
136 | |
137 | /** |
138 | * ishtp_wait_for_response() - hid-core .wait() callback |
139 | * @hid: hid device instance |
140 | * |
141 | * This function is used to wait after get feaure/input report. |
142 | * |
143 | * Return: 0 on success and non zero on error |
144 | */ |
145 | static int ishtp_wait_for_response(struct hid_device *hid) |
146 | { |
147 | struct ishtp_hid_data *hid_data = hid->driver_data; |
148 | int rv; |
149 | |
150 | hid_ishtp_trace(client_data, "%s hid %p\n" , __func__, hid); |
151 | |
152 | rv = ishtp_hid_link_ready_wait(client_data: hid_data->client_data); |
153 | if (rv) |
154 | return rv; |
155 | |
156 | if (!hid_data->request_done) |
157 | wait_event_interruptible_timeout(hid_data->hid_wait, |
158 | hid_data->request_done, 3 * HZ); |
159 | |
160 | if (!hid_data->request_done) { |
161 | hid_err(hid, |
162 | "timeout waiting for response from ISHTP device\n" ); |
163 | return -ETIMEDOUT; |
164 | } |
165 | hid_ishtp_trace(client_data, "%s hid %p done\n" , __func__, hid); |
166 | |
167 | hid_data->request_done = false; |
168 | |
169 | return 0; |
170 | } |
171 | |
172 | /** |
173 | * ishtp_hid_wakeup() - Wakeup caller |
174 | * @hid: hid device instance |
175 | * |
176 | * This function will wakeup caller waiting for Get/Set feature report |
177 | */ |
178 | void ishtp_hid_wakeup(struct hid_device *hid) |
179 | { |
180 | struct ishtp_hid_data *hid_data = hid->driver_data; |
181 | |
182 | hid_data->request_done = true; |
183 | wake_up_interruptible(&hid_data->hid_wait); |
184 | } |
185 | |
186 | static const struct hid_ll_driver ishtp_hid_ll_driver = { |
187 | .parse = ishtp_hid_parse, |
188 | .start = ishtp_hid_start, |
189 | .stop = ishtp_hid_stop, |
190 | .open = ishtp_hid_open, |
191 | .close = ishtp_hid_close, |
192 | .request = ishtp_hid_request, |
193 | .wait = ishtp_wait_for_response, |
194 | .raw_request = ishtp_raw_request |
195 | }; |
196 | |
197 | /** |
198 | * ishtp_hid_probe() - hid register ll driver |
199 | * @cur_hid_dev: Index of hid device calling to register |
200 | * @client_data: Client data pointer |
201 | * |
202 | * This function is used to allocate and add HID device. |
203 | * |
204 | * Return: 0 on success, non zero on error |
205 | */ |
206 | int ishtp_hid_probe(unsigned int cur_hid_dev, |
207 | struct ishtp_cl_data *client_data) |
208 | { |
209 | int rv; |
210 | struct hid_device *hid; |
211 | struct ishtp_hid_data *hid_data; |
212 | |
213 | hid = hid_allocate_device(); |
214 | if (IS_ERR(ptr: hid)) |
215 | return PTR_ERR(ptr: hid); |
216 | |
217 | hid_data = kzalloc(size: sizeof(*hid_data), GFP_KERNEL); |
218 | if (!hid_data) { |
219 | rv = -ENOMEM; |
220 | goto err_hid_data; |
221 | } |
222 | |
223 | hid_data->index = cur_hid_dev; |
224 | hid_data->client_data = client_data; |
225 | init_waitqueue_head(&hid_data->hid_wait); |
226 | |
227 | hid->driver_data = hid_data; |
228 | |
229 | client_data->hid_sensor_hubs[cur_hid_dev] = hid; |
230 | |
231 | hid->ll_driver = &ishtp_hid_ll_driver; |
232 | hid->bus = BUS_INTEL_ISHTP; |
233 | hid->dev.parent = ishtp_device(cl_device: client_data->cl_device); |
234 | |
235 | hid->version = le16_to_cpu(ISH_HID_VERSION); |
236 | hid->vendor = le16_to_cpu(client_data->hid_devices[cur_hid_dev].vid); |
237 | hid->product = le16_to_cpu(client_data->hid_devices[cur_hid_dev].pid); |
238 | snprintf(buf: hid->name, size: sizeof(hid->name), fmt: "%s %04X:%04X" , "hid-ishtp" , |
239 | hid->vendor, hid->product); |
240 | |
241 | rv = hid_add_device(hid); |
242 | if (rv) |
243 | goto err_hid_device; |
244 | |
245 | hid_ishtp_trace(client_data, "%s allocated hid %p\n" , __func__, hid); |
246 | |
247 | return 0; |
248 | |
249 | err_hid_device: |
250 | kfree(objp: hid_data); |
251 | err_hid_data: |
252 | hid_destroy_device(hid); |
253 | return rv; |
254 | } |
255 | |
256 | /** |
257 | * ishtp_hid_remove() - Remove registered hid device |
258 | * @client_data: client data pointer |
259 | * |
260 | * This function is used to destroy allocatd HID device. |
261 | */ |
262 | void ishtp_hid_remove(struct ishtp_cl_data *client_data) |
263 | { |
264 | int i; |
265 | |
266 | for (i = 0; i < client_data->num_hid_devices; ++i) { |
267 | if (client_data->hid_sensor_hubs[i]) { |
268 | kfree(objp: client_data->hid_sensor_hubs[i]->driver_data); |
269 | hid_destroy_device(client_data->hid_sensor_hubs[i]); |
270 | client_data->hid_sensor_hubs[i] = NULL; |
271 | } |
272 | } |
273 | } |
274 | |