diff options
author | Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> | 2016-08-07 05:25:36 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2016-08-17 05:13:08 -0400 |
commit | 0b28cb4bcb17dcb5fe0763fc3e1a94398b8f6cf6 (patch) | |
tree | b5a9cd3967b27babea93f58a86d7450db71b84fc | |
parent | ae02e5d40d5f829c589412c6253f925e35cf7a22 (diff) |
HID: intel-ish-hid: ISH HID client driver
This driver is responsible for implementing ISH HID client, which
gets HID description and report. Once it has completely gets
report descriptors, it registers as a HID LL drivers. This implements
necessary callbacks so that it can be used by HID sensor hub driver.
Original-author: Daniel Drubin <daniel.drubin@intel.com>
Reviewed-and-tested-by: Ooi, Joyce <joyce.ooi@intel.com>
Tested-by: Grant Likely <grant.likely@secretlab.ca>
Tested-by: Rann Bar-On <rb6@duke.edu>
Tested-by: Atri Bhattacharya <badshah400@aim.com>
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/intel-ish-hid/Makefile | 4 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp-hid-client.c | 978 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp-hid.c | 246 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp-hid.h | 182 | ||||
-rw-r--r-- | include/uapi/linux/input.h | 1 |
5 files changed, 1411 insertions, 0 deletions
diff --git a/drivers/hid/intel-ish-hid/Makefile b/drivers/hid/intel-ish-hid/Makefile index ab626d8f7bb8..8c08b0b358b1 100644 --- a/drivers/hid/intel-ish-hid/Makefile +++ b/drivers/hid/intel-ish-hid/Makefile | |||
@@ -15,4 +15,8 @@ obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-ipc.o | |||
15 | intel-ish-ipc-objs := ipc/ipc.o | 15 | intel-ish-ipc-objs := ipc/ipc.o |
16 | intel-ish-ipc-objs += ipc/pci-ish.o | 16 | intel-ish-ipc-objs += ipc/pci-ish.o |
17 | 17 | ||
18 | obj-$(CONFIG_INTEL_ISH_HID) += intel-ishtp-hid.o | ||
19 | intel-ishtp-hid-objs := ishtp-hid.o | ||
20 | intel-ishtp-hid-objs += ishtp-hid-client.o | ||
21 | |||
18 | ccflags-y += -Idrivers/hid/intel-ish-hid/ishtp | 22 | ccflags-y += -Idrivers/hid/intel-ish-hid/ishtp |
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c new file mode 100644 index 000000000000..5c643d7a07b2 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c | |||
@@ -0,0 +1,978 @@ | |||
1 | /* | ||
2 | * ISHTP client driver for HID (ISH) | ||
3 | * | ||
4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/hid.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include "ishtp/ishtp-dev.h" | ||
20 | #include "ishtp/client.h" | ||
21 | #include "ishtp-hid.h" | ||
22 | |||
23 | /* Rx ring buffer pool size */ | ||
24 | #define HID_CL_RX_RING_SIZE 32 | ||
25 | #define HID_CL_TX_RING_SIZE 16 | ||
26 | |||
27 | /** | ||
28 | * report_bad_packets() - Report bad packets | ||
29 | * @hid_ishtp_cl: Client instance to get stats | ||
30 | * @recv_buf: Raw received host interface message | ||
31 | * @cur_pos: Current position index in payload | ||
32 | * @payload_len: Length of payload expected | ||
33 | * | ||
34 | * Dumps error in case bad packet is received | ||
35 | */ | ||
36 | static void report_bad_packet(struct ishtp_cl *hid_ishtp_cl, void *recv_buf, | ||
37 | size_t cur_pos, size_t payload_len) | ||
38 | { | ||
39 | struct hostif_msg *recv_msg = recv_buf; | ||
40 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
41 | |||
42 | dev_err(&client_data->cl_device->dev, "[hid-ish]: BAD packet %02X\n" | ||
43 | "total_bad=%u cur_pos=%u\n" | ||
44 | "[%02X %02X %02X %02X]\n" | ||
45 | "payload_len=%u\n" | ||
46 | "multi_packet_cnt=%u\n" | ||
47 | "is_response=%02X\n", | ||
48 | recv_msg->hdr.command, client_data->bad_recv_cnt, | ||
49 | (unsigned int)cur_pos, | ||
50 | ((unsigned char *)recv_msg)[0], ((unsigned char *)recv_msg)[1], | ||
51 | ((unsigned char *)recv_msg)[2], ((unsigned char *)recv_msg)[3], | ||
52 | (unsigned int)payload_len, client_data->multi_packet_cnt, | ||
53 | recv_msg->hdr.command & ~CMD_MASK); | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * process_recv() - Received and parse incoming packet | ||
58 | * @hid_ishtp_cl: Client instance to get stats | ||
59 | * @recv_buf: Raw received host interface message | ||
60 | * @data_len: length of the message | ||
61 | * | ||
62 | * Parse the incoming packet. If it is a response packet then it will update | ||
63 | * per instance flags and wake up the caller waiting to for the response. | ||
64 | */ | ||
65 | static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf, | ||
66 | size_t data_len) | ||
67 | { | ||
68 | struct hostif_msg *recv_msg; | ||
69 | unsigned char *payload; | ||
70 | struct device_info *dev_info; | ||
71 | int i, j; | ||
72 | size_t payload_len, total_len, cur_pos; | ||
73 | int report_type; | ||
74 | struct report_list *reports_list; | ||
75 | char *reports; | ||
76 | size_t report_len; | ||
77 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
78 | int curr_hid_dev = client_data->cur_hid_dev; | ||
79 | |||
80 | if (data_len < sizeof(struct hostif_msg_hdr)) { | ||
81 | dev_err(&client_data->cl_device->dev, | ||
82 | "[hid-ish]: error, received %u which is less than data header %u\n", | ||
83 | (unsigned int)data_len, | ||
84 | (unsigned int)sizeof(struct hostif_msg_hdr)); | ||
85 | ++client_data->bad_recv_cnt; | ||
86 | ish_hw_reset(hid_ishtp_cl->dev); | ||
87 | return; | ||
88 | } | ||
89 | |||
90 | payload = recv_buf + sizeof(struct hostif_msg_hdr); | ||
91 | total_len = data_len; | ||
92 | cur_pos = 0; | ||
93 | |||
94 | do { | ||
95 | recv_msg = (struct hostif_msg *)(recv_buf + cur_pos); | ||
96 | payload_len = recv_msg->hdr.size; | ||
97 | |||
98 | /* Sanity checks */ | ||
99 | if (cur_pos + payload_len + sizeof(struct hostif_msg) > | ||
100 | total_len) { | ||
101 | ++client_data->bad_recv_cnt; | ||
102 | report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos, | ||
103 | payload_len); | ||
104 | ish_hw_reset(hid_ishtp_cl->dev); | ||
105 | break; | ||
106 | } | ||
107 | |||
108 | hid_ishtp_trace(client_data, "%s %d\n", | ||
109 | __func__, recv_msg->hdr.command & CMD_MASK); | ||
110 | |||
111 | switch (recv_msg->hdr.command & CMD_MASK) { | ||
112 | case HOSTIF_DM_ENUM_DEVICES: | ||
113 | if ((!(recv_msg->hdr.command & ~CMD_MASK) || | ||
114 | client_data->init_done)) { | ||
115 | ++client_data->bad_recv_cnt; | ||
116 | report_bad_packet(hid_ishtp_cl, recv_msg, | ||
117 | cur_pos, | ||
118 | payload_len); | ||
119 | ish_hw_reset(hid_ishtp_cl->dev); | ||
120 | break; | ||
121 | } | ||
122 | client_data->hid_dev_count = (unsigned int)*payload; | ||
123 | if (!client_data->hid_devices) | ||
124 | client_data->hid_devices = devm_kzalloc( | ||
125 | &client_data->cl_device->dev, | ||
126 | client_data->hid_dev_count * | ||
127 | sizeof(struct device_info), | ||
128 | GFP_KERNEL); | ||
129 | if (!client_data->hid_devices) { | ||
130 | dev_err(&client_data->cl_device->dev, | ||
131 | "Mem alloc failed for hid device info\n"); | ||
132 | wake_up_interruptible(&client_data->init_wait); | ||
133 | break; | ||
134 | } | ||
135 | for (i = 0; i < client_data->hid_dev_count; ++i) { | ||
136 | if (1 + sizeof(struct device_info) * i >= | ||
137 | payload_len) { | ||
138 | dev_err(&client_data->cl_device->dev, | ||
139 | "[hid-ish]: [ENUM_DEVICES]: content size %lu is bigger than payload_len %u\n", | ||
140 | 1 + sizeof(struct device_info) | ||
141 | * i, | ||
142 | (unsigned int)payload_len); | ||
143 | } | ||
144 | |||
145 | if (1 + sizeof(struct device_info) * i >= | ||
146 | data_len) | ||
147 | break; | ||
148 | |||
149 | dev_info = (struct device_info *)(payload + 1 + | ||
150 | sizeof(struct device_info) * i); | ||
151 | if (client_data->hid_devices) | ||
152 | memcpy(client_data->hid_devices + i, | ||
153 | dev_info, | ||
154 | sizeof(struct device_info)); | ||
155 | } | ||
156 | |||
157 | client_data->enum_devices_done = true; | ||
158 | wake_up_interruptible(&client_data->init_wait); | ||
159 | |||
160 | break; | ||
161 | |||
162 | case HOSTIF_GET_HID_DESCRIPTOR: | ||
163 | if ((!(recv_msg->hdr.command & ~CMD_MASK) || | ||
164 | client_data->init_done)) { | ||
165 | ++client_data->bad_recv_cnt; | ||
166 | report_bad_packet(hid_ishtp_cl, recv_msg, | ||
167 | cur_pos, | ||
168 | payload_len); | ||
169 | ish_hw_reset(hid_ishtp_cl->dev); | ||
170 | break; | ||
171 | } | ||
172 | if (!client_data->hid_descr[curr_hid_dev]) | ||
173 | client_data->hid_descr[curr_hid_dev] = | ||
174 | devm_kmalloc(&client_data->cl_device->dev, | ||
175 | payload_len, GFP_KERNEL); | ||
176 | if (client_data->hid_descr[curr_hid_dev]) { | ||
177 | memcpy(client_data->hid_descr[curr_hid_dev], | ||
178 | payload, payload_len); | ||
179 | client_data->hid_descr_size[curr_hid_dev] = | ||
180 | payload_len; | ||
181 | client_data->hid_descr_done = true; | ||
182 | } | ||
183 | wake_up_interruptible(&client_data->init_wait); | ||
184 | |||
185 | break; | ||
186 | |||
187 | case HOSTIF_GET_REPORT_DESCRIPTOR: | ||
188 | if ((!(recv_msg->hdr.command & ~CMD_MASK) || | ||
189 | client_data->init_done)) { | ||
190 | ++client_data->bad_recv_cnt; | ||
191 | report_bad_packet(hid_ishtp_cl, recv_msg, | ||
192 | cur_pos, | ||
193 | payload_len); | ||
194 | ish_hw_reset(hid_ishtp_cl->dev); | ||
195 | break; | ||
196 | } | ||
197 | if (!client_data->report_descr[curr_hid_dev]) | ||
198 | client_data->report_descr[curr_hid_dev] = | ||
199 | devm_kmalloc(&client_data->cl_device->dev, | ||
200 | payload_len, GFP_KERNEL); | ||
201 | if (client_data->report_descr[curr_hid_dev]) { | ||
202 | memcpy(client_data->report_descr[curr_hid_dev], | ||
203 | payload, | ||
204 | payload_len); | ||
205 | client_data->report_descr_size[curr_hid_dev] = | ||
206 | payload_len; | ||
207 | client_data->report_descr_done = true; | ||
208 | } | ||
209 | wake_up_interruptible(&client_data->init_wait); | ||
210 | |||
211 | break; | ||
212 | |||
213 | case HOSTIF_GET_FEATURE_REPORT: | ||
214 | report_type = HID_FEATURE_REPORT; | ||
215 | goto do_get_report; | ||
216 | |||
217 | case HOSTIF_GET_INPUT_REPORT: | ||
218 | report_type = HID_INPUT_REPORT; | ||
219 | do_get_report: | ||
220 | /* Get index of device that matches this id */ | ||
221 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
222 | if (recv_msg->hdr.device_id == | ||
223 | client_data->hid_devices[i].dev_id) | ||
224 | if (client_data->hid_sensor_hubs[i]) { | ||
225 | hid_input_report( | ||
226 | client_data->hid_sensor_hubs[ | ||
227 | i], | ||
228 | report_type, payload, | ||
229 | payload_len, 0); | ||
230 | ishtp_hid_wakeup( | ||
231 | client_data->hid_sensor_hubs[ | ||
232 | i]); | ||
233 | break; | ||
234 | } | ||
235 | } | ||
236 | break; | ||
237 | |||
238 | case HOSTIF_SET_FEATURE_REPORT: | ||
239 | /* Get index of device that matches this id */ | ||
240 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
241 | if (recv_msg->hdr.device_id == | ||
242 | client_data->hid_devices[i].dev_id) | ||
243 | if (client_data->hid_sensor_hubs[i]) { | ||
244 | ishtp_hid_wakeup( | ||
245 | client_data->hid_sensor_hubs[ | ||
246 | i]); | ||
247 | break; | ||
248 | } | ||
249 | } | ||
250 | break; | ||
251 | |||
252 | case HOSTIF_PUBLISH_INPUT_REPORT: | ||
253 | report_type = HID_INPUT_REPORT; | ||
254 | for (i = 0; i < client_data->num_hid_devices; ++i) | ||
255 | if (recv_msg->hdr.device_id == | ||
256 | client_data->hid_devices[i].dev_id) | ||
257 | if (client_data->hid_sensor_hubs[i]) | ||
258 | hid_input_report( | ||
259 | client_data->hid_sensor_hubs[ | ||
260 | i], | ||
261 | report_type, payload, | ||
262 | payload_len, 0); | ||
263 | break; | ||
264 | |||
265 | case HOSTIF_PUBLISH_INPUT_REPORT_LIST: | ||
266 | report_type = HID_INPUT_REPORT; | ||
267 | reports_list = (struct report_list *)payload; | ||
268 | reports = (char *)reports_list->reports; | ||
269 | |||
270 | for (j = 0; j < reports_list->num_of_reports; j++) { | ||
271 | recv_msg = (struct hostif_msg *)(reports + | ||
272 | sizeof(uint16_t)); | ||
273 | report_len = *(uint16_t *)reports; | ||
274 | payload = reports + sizeof(uint16_t) + | ||
275 | sizeof(struct hostif_msg_hdr); | ||
276 | payload_len = report_len - | ||
277 | sizeof(struct hostif_msg_hdr); | ||
278 | |||
279 | for (i = 0; i < client_data->num_hid_devices; | ||
280 | ++i) | ||
281 | if (recv_msg->hdr.device_id == | ||
282 | client_data->hid_devices[i].dev_id && | ||
283 | client_data->hid_sensor_hubs[i]) { | ||
284 | hid_input_report( | ||
285 | client_data->hid_sensor_hubs[ | ||
286 | i], | ||
287 | report_type, | ||
288 | payload, payload_len, | ||
289 | 0); | ||
290 | } | ||
291 | |||
292 | reports += sizeof(uint16_t) + report_len; | ||
293 | } | ||
294 | break; | ||
295 | default: | ||
296 | ++client_data->bad_recv_cnt; | ||
297 | report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos, | ||
298 | payload_len); | ||
299 | ish_hw_reset(hid_ishtp_cl->dev); | ||
300 | break; | ||
301 | |||
302 | } | ||
303 | |||
304 | if (!cur_pos && cur_pos + payload_len + | ||
305 | sizeof(struct hostif_msg) < total_len) | ||
306 | ++client_data->multi_packet_cnt; | ||
307 | |||
308 | cur_pos += payload_len + sizeof(struct hostif_msg); | ||
309 | payload += payload_len + sizeof(struct hostif_msg); | ||
310 | |||
311 | } while (cur_pos < total_len); | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * ish_cl_event_cb() - bus driver callback for incoming message/packet | ||
316 | * @device: Pointer to the the ishtp client device for which this message | ||
317 | * is targeted | ||
318 | * | ||
319 | * Remove the packet from the list and process the message by calling | ||
320 | * process_recv | ||
321 | */ | ||
322 | static void ish_cl_event_cb(struct ishtp_cl_device *device) | ||
323 | { | ||
324 | struct ishtp_cl *hid_ishtp_cl = device->driver_data; | ||
325 | struct ishtp_cl_rb *rb_in_proc; | ||
326 | size_t r_length; | ||
327 | unsigned long flags; | ||
328 | |||
329 | if (!hid_ishtp_cl) | ||
330 | return; | ||
331 | |||
332 | spin_lock_irqsave(&hid_ishtp_cl->in_process_spinlock, flags); | ||
333 | while (!list_empty(&hid_ishtp_cl->in_process_list.list)) { | ||
334 | rb_in_proc = list_entry( | ||
335 | hid_ishtp_cl->in_process_list.list.next, | ||
336 | struct ishtp_cl_rb, list); | ||
337 | list_del_init(&rb_in_proc->list); | ||
338 | spin_unlock_irqrestore(&hid_ishtp_cl->in_process_spinlock, | ||
339 | flags); | ||
340 | |||
341 | if (!rb_in_proc->buffer.data) | ||
342 | return; | ||
343 | |||
344 | r_length = rb_in_proc->buf_idx; | ||
345 | |||
346 | /* decide what to do with received data */ | ||
347 | process_recv(hid_ishtp_cl, rb_in_proc->buffer.data, r_length); | ||
348 | |||
349 | ishtp_cl_io_rb_recycle(rb_in_proc); | ||
350 | spin_lock_irqsave(&hid_ishtp_cl->in_process_spinlock, flags); | ||
351 | } | ||
352 | spin_unlock_irqrestore(&hid_ishtp_cl->in_process_spinlock, flags); | ||
353 | } | ||
354 | |||
355 | /** | ||
356 | * hid_ishtp_set_feature() - send request to ISH FW to set a feature request | ||
357 | * @hid: hid device instance for this request | ||
358 | * @buf: feature buffer | ||
359 | * @len: Length of feature buffer | ||
360 | * @report_id: Report id for the feature set request | ||
361 | * | ||
362 | * This is called from hid core .request() callback. This function doesn't wait | ||
363 | * for response. | ||
364 | */ | ||
365 | void hid_ishtp_set_feature(struct hid_device *hid, char *buf, unsigned int len, | ||
366 | int report_id) | ||
367 | { | ||
368 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
369 | struct ishtp_cl_data *client_data = hid_data->client_data; | ||
370 | struct hostif_msg *msg = (struct hostif_msg *)buf; | ||
371 | int rv; | ||
372 | int i; | ||
373 | |||
374 | hid_ishtp_trace(client_data, "%s hid %p\n", __func__, hid); | ||
375 | |||
376 | rv = ishtp_hid_link_ready_wait(client_data); | ||
377 | if (rv) { | ||
378 | hid_ishtp_trace(client_data, "%s hid %p link not ready\n", | ||
379 | __func__, hid); | ||
380 | return; | ||
381 | } | ||
382 | |||
383 | memset(msg, 0, sizeof(struct hostif_msg)); | ||
384 | msg->hdr.command = HOSTIF_SET_FEATURE_REPORT; | ||
385 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
386 | if (hid == client_data->hid_sensor_hubs[i]) { | ||
387 | msg->hdr.device_id = | ||
388 | client_data->hid_devices[i].dev_id; | ||
389 | break; | ||
390 | } | ||
391 | } | ||
392 | |||
393 | if (i == client_data->num_hid_devices) | ||
394 | return; | ||
395 | |||
396 | rv = ishtp_cl_send(client_data->hid_ishtp_cl, buf, len); | ||
397 | if (rv) | ||
398 | hid_ishtp_trace(client_data, "%s hid %p send failed\n", | ||
399 | __func__, hid); | ||
400 | } | ||
401 | |||
402 | /** | ||
403 | * hid_ishtp_get_report() - request to get feature/input report | ||
404 | * @hid: hid device instance for this request | ||
405 | * @report_id: Report id for the get request | ||
406 | * @report_type: Report type for the this request | ||
407 | * | ||
408 | * This is called from hid core .request() callback. This function will send | ||
409 | * request to FW and return without waiting for response. | ||
410 | */ | ||
411 | void hid_ishtp_get_report(struct hid_device *hid, int report_id, | ||
412 | int report_type) | ||
413 | { | ||
414 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
415 | struct ishtp_cl_data *client_data = hid_data->client_data; | ||
416 | static unsigned char buf[10]; | ||
417 | unsigned int len; | ||
418 | struct hostif_msg_to_sensor *msg = (struct hostif_msg_to_sensor *)buf; | ||
419 | int rv; | ||
420 | int i; | ||
421 | |||
422 | hid_ishtp_trace(client_data, "%s hid %p\n", __func__, hid); | ||
423 | rv = ishtp_hid_link_ready_wait(client_data); | ||
424 | if (rv) { | ||
425 | hid_ishtp_trace(client_data, "%s hid %p link not ready\n", | ||
426 | __func__, hid); | ||
427 | return; | ||
428 | } | ||
429 | |||
430 | len = sizeof(struct hostif_msg_to_sensor); | ||
431 | |||
432 | memset(msg, 0, sizeof(struct hostif_msg_to_sensor)); | ||
433 | msg->hdr.command = (report_type == HID_FEATURE_REPORT) ? | ||
434 | HOSTIF_GET_FEATURE_REPORT : HOSTIF_GET_INPUT_REPORT; | ||
435 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
436 | if (hid == client_data->hid_sensor_hubs[i]) { | ||
437 | msg->hdr.device_id = | ||
438 | client_data->hid_devices[i].dev_id; | ||
439 | break; | ||
440 | } | ||
441 | } | ||
442 | |||
443 | if (i == client_data->num_hid_devices) | ||
444 | return; | ||
445 | |||
446 | msg->report_id = report_id; | ||
447 | rv = ishtp_cl_send(client_data->hid_ishtp_cl, buf, len); | ||
448 | if (rv) | ||
449 | hid_ishtp_trace(client_data, "%s hid %p send failed\n", | ||
450 | __func__, hid); | ||
451 | } | ||
452 | |||
453 | /** | ||
454 | * ishtp_hid_link_ready_wait() - Wait for link ready | ||
455 | * @client_data: client data instance | ||
456 | * | ||
457 | * If the transport link started suspend process, then wait, till either | ||
458 | * resumed or timeout | ||
459 | * | ||
460 | * Return: 0 on success, non zero on error | ||
461 | */ | ||
462 | int ishtp_hid_link_ready_wait(struct ishtp_cl_data *client_data) | ||
463 | { | ||
464 | int rc; | ||
465 | |||
466 | if (client_data->suspended) { | ||
467 | hid_ishtp_trace(client_data, "wait for link ready\n"); | ||
468 | rc = wait_event_interruptible_timeout( | ||
469 | client_data->ishtp_resume_wait, | ||
470 | !client_data->suspended, | ||
471 | 5 * HZ); | ||
472 | |||
473 | if (rc == 0) { | ||
474 | hid_ishtp_trace(client_data, "link not ready\n"); | ||
475 | return -EIO; | ||
476 | } | ||
477 | hid_ishtp_trace(client_data, "link ready\n"); | ||
478 | } | ||
479 | |||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | /** | ||
484 | * ishtp_enum_enum_devices() - Enumerate hid devices | ||
485 | * @hid_ishtp_cl: client instance | ||
486 | * | ||
487 | * Helper function to send request to firmware to enumerate HID devices | ||
488 | * | ||
489 | * Return: 0 on success, non zero on error | ||
490 | */ | ||
491 | static int ishtp_enum_enum_devices(struct ishtp_cl *hid_ishtp_cl) | ||
492 | { | ||
493 | struct hostif_msg msg; | ||
494 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
495 | int retry_count; | ||
496 | int rv; | ||
497 | |||
498 | /* Send HOSTIF_DM_ENUM_DEVICES */ | ||
499 | memset(&msg, 0, sizeof(struct hostif_msg)); | ||
500 | msg.hdr.command = HOSTIF_DM_ENUM_DEVICES; | ||
501 | rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *)&msg, | ||
502 | sizeof(struct hostif_msg)); | ||
503 | if (rv) | ||
504 | return rv; | ||
505 | |||
506 | retry_count = 0; | ||
507 | while (!client_data->enum_devices_done && | ||
508 | retry_count < 10) { | ||
509 | wait_event_interruptible_timeout(client_data->init_wait, | ||
510 | client_data->enum_devices_done, | ||
511 | 3 * HZ); | ||
512 | ++retry_count; | ||
513 | if (!client_data->enum_devices_done) | ||
514 | /* Send HOSTIF_DM_ENUM_DEVICES */ | ||
515 | rv = ishtp_cl_send(hid_ishtp_cl, | ||
516 | (unsigned char *) &msg, | ||
517 | sizeof(struct hostif_msg)); | ||
518 | } | ||
519 | if (!client_data->enum_devices_done) { | ||
520 | dev_err(&client_data->cl_device->dev, | ||
521 | "[hid-ish]: timed out waiting for enum_devices\n"); | ||
522 | return -ETIMEDOUT; | ||
523 | } | ||
524 | if (!client_data->hid_devices) { | ||
525 | dev_err(&client_data->cl_device->dev, | ||
526 | "[hid-ish]: failed to allocate HID dev structures\n"); | ||
527 | return -ENOMEM; | ||
528 | } | ||
529 | |||
530 | client_data->num_hid_devices = client_data->hid_dev_count; | ||
531 | dev_info(&hid_ishtp_cl->device->dev, | ||
532 | "[hid-ish]: enum_devices_done OK, num_hid_devices=%d\n", | ||
533 | client_data->num_hid_devices); | ||
534 | |||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | /** | ||
539 | * ishtp_get_hid_descriptor() - Get hid descriptor | ||
540 | * @hid_ishtp_cl: client instance | ||
541 | * @index: Index into the hid_descr array | ||
542 | * | ||
543 | * Helper function to send request to firmware get HID descriptor of a device | ||
544 | * | ||
545 | * Return: 0 on success, non zero on error | ||
546 | */ | ||
547 | static int ishtp_get_hid_descriptor(struct ishtp_cl *hid_ishtp_cl, int index) | ||
548 | { | ||
549 | struct hostif_msg msg; | ||
550 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
551 | int rv; | ||
552 | |||
553 | /* Get HID descriptor */ | ||
554 | client_data->hid_descr_done = false; | ||
555 | memset(&msg, 0, sizeof(struct hostif_msg)); | ||
556 | msg.hdr.command = HOSTIF_GET_HID_DESCRIPTOR; | ||
557 | msg.hdr.device_id = client_data->hid_devices[index].dev_id; | ||
558 | rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *) &msg, | ||
559 | sizeof(struct hostif_msg)); | ||
560 | if (rv) | ||
561 | return rv; | ||
562 | |||
563 | if (!client_data->hid_descr_done) { | ||
564 | wait_event_interruptible_timeout(client_data->init_wait, | ||
565 | client_data->hid_descr_done, | ||
566 | 3 * HZ); | ||
567 | if (!client_data->hid_descr_done) { | ||
568 | dev_err(&client_data->cl_device->dev, | ||
569 | "[hid-ish]: timed out for hid_descr_done\n"); | ||
570 | return -EIO; | ||
571 | } | ||
572 | |||
573 | if (!client_data->hid_descr[index]) { | ||
574 | dev_err(&client_data->cl_device->dev, | ||
575 | "[hid-ish]: allocation HID desc fail\n"); | ||
576 | return -ENOMEM; | ||
577 | } | ||
578 | } | ||
579 | |||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | /** | ||
584 | * ishtp_get_report_descriptor() - Get report descriptor | ||
585 | * @hid_ishtp_cl: client instance | ||
586 | * @index: Index into the hid_descr array | ||
587 | * | ||
588 | * Helper function to send request to firmware get HID report descriptor of | ||
589 | * a device | ||
590 | * | ||
591 | * Return: 0 on success, non zero on error | ||
592 | */ | ||
593 | static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl, | ||
594 | int index) | ||
595 | { | ||
596 | struct hostif_msg msg; | ||
597 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
598 | int rv; | ||
599 | |||
600 | /* Get report descriptor */ | ||
601 | client_data->report_descr_done = false; | ||
602 | memset(&msg, 0, sizeof(struct hostif_msg)); | ||
603 | msg.hdr.command = HOSTIF_GET_REPORT_DESCRIPTOR; | ||
604 | msg.hdr.device_id = client_data->hid_devices[index].dev_id; | ||
605 | rv = ishtp_cl_send(hid_ishtp_cl, (unsigned char *) &msg, | ||
606 | sizeof(struct hostif_msg)); | ||
607 | if (rv) | ||
608 | return rv; | ||
609 | |||
610 | if (!client_data->report_descr_done) | ||
611 | wait_event_interruptible_timeout(client_data->init_wait, | ||
612 | client_data->report_descr_done, | ||
613 | 3 * HZ); | ||
614 | if (!client_data->report_descr_done) { | ||
615 | dev_err(&client_data->cl_device->dev, | ||
616 | "[hid-ish]: timed out for report descr\n"); | ||
617 | return -EIO; | ||
618 | } | ||
619 | if (!client_data->report_descr[index]) { | ||
620 | dev_err(&client_data->cl_device->dev, | ||
621 | "[hid-ish]: failed to alloc report descr\n"); | ||
622 | return -ENOMEM; | ||
623 | } | ||
624 | |||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | /** | ||
629 | * hid_ishtp_cl_init() - Init function for ISHTP client | ||
630 | * @hid_ishtp_cl: ISHTP client instance | ||
631 | * @reset: true if called for init after reset | ||
632 | * | ||
633 | * This function complete the initializtion of the client. The summary of | ||
634 | * processing: | ||
635 | * - Send request to enumerate the hid clients | ||
636 | * Get the HID descriptor for each enumearated device | ||
637 | * Get report description of each device | ||
638 | * Register each device wik hid core by calling ishtp_hid_probe | ||
639 | * | ||
640 | * Return: 0 on success, non zero on error | ||
641 | */ | ||
642 | static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset) | ||
643 | { | ||
644 | struct ishtp_device *dev; | ||
645 | unsigned long flags; | ||
646 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
647 | int i; | ||
648 | int rv; | ||
649 | |||
650 | dev_dbg(&client_data->cl_device->dev, "%s\n", __func__); | ||
651 | hid_ishtp_trace(client_data, "%s reset flag: %d\n", __func__, reset); | ||
652 | |||
653 | rv = ishtp_cl_link(hid_ishtp_cl, ISHTP_HOST_CLIENT_ID_ANY); | ||
654 | if (rv) { | ||
655 | dev_err(&client_data->cl_device->dev, | ||
656 | "ishtp_cl_link failed\n"); | ||
657 | return -ENOMEM; | ||
658 | } | ||
659 | |||
660 | client_data->init_done = 0; | ||
661 | |||
662 | dev = hid_ishtp_cl->dev; | ||
663 | |||
664 | /* Connect to FW client */ | ||
665 | hid_ishtp_cl->rx_ring_size = HID_CL_RX_RING_SIZE; | ||
666 | hid_ishtp_cl->tx_ring_size = HID_CL_TX_RING_SIZE; | ||
667 | |||
668 | spin_lock_irqsave(&dev->fw_clients_lock, flags); | ||
669 | i = ishtp_fw_cl_by_uuid(dev, &hid_ishtp_guid); | ||
670 | if (i < 0) { | ||
671 | spin_unlock_irqrestore(&dev->fw_clients_lock, flags); | ||
672 | dev_err(&client_data->cl_device->dev, | ||
673 | "ish client uuid not found\n"); | ||
674 | return i; | ||
675 | } | ||
676 | hid_ishtp_cl->fw_client_id = dev->fw_clients[i].client_id; | ||
677 | spin_unlock_irqrestore(&dev->fw_clients_lock, flags); | ||
678 | hid_ishtp_cl->state = ISHTP_CL_CONNECTING; | ||
679 | |||
680 | rv = ishtp_cl_connect(hid_ishtp_cl); | ||
681 | if (rv) { | ||
682 | dev_err(&client_data->cl_device->dev, | ||
683 | "client connect fail\n"); | ||
684 | goto err_cl_unlink; | ||
685 | } | ||
686 | |||
687 | hid_ishtp_trace(client_data, "%s client connected\n", __func__); | ||
688 | |||
689 | /* Register read callback */ | ||
690 | ishtp_register_event_cb(hid_ishtp_cl->device, ish_cl_event_cb); | ||
691 | |||
692 | rv = ishtp_enum_enum_devices(hid_ishtp_cl); | ||
693 | if (rv) | ||
694 | goto err_cl_disconnect; | ||
695 | |||
696 | hid_ishtp_trace(client_data, "%s enumerated device count %d\n", | ||
697 | __func__, client_data->num_hid_devices); | ||
698 | |||
699 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
700 | client_data->cur_hid_dev = i; | ||
701 | |||
702 | rv = ishtp_get_hid_descriptor(hid_ishtp_cl, i); | ||
703 | if (rv) | ||
704 | goto err_cl_disconnect; | ||
705 | |||
706 | rv = ishtp_get_report_descriptor(hid_ishtp_cl, i); | ||
707 | if (rv) | ||
708 | goto err_cl_disconnect; | ||
709 | |||
710 | if (!reset) { | ||
711 | rv = ishtp_hid_probe(i, client_data); | ||
712 | if (rv) { | ||
713 | dev_err(&client_data->cl_device->dev, | ||
714 | "[hid-ish]: HID probe for #%u failed: %d\n", | ||
715 | i, rv); | ||
716 | goto err_cl_disconnect; | ||
717 | } | ||
718 | } | ||
719 | } /* for() on all hid devices */ | ||
720 | |||
721 | client_data->init_done = 1; | ||
722 | client_data->suspended = false; | ||
723 | wake_up_interruptible(&client_data->ishtp_resume_wait); | ||
724 | hid_ishtp_trace(client_data, "%s successful init\n", __func__); | ||
725 | return 0; | ||
726 | |||
727 | err_cl_disconnect: | ||
728 | hid_ishtp_cl->state = ISHTP_CL_DISCONNECTING; | ||
729 | ishtp_cl_disconnect(hid_ishtp_cl); | ||
730 | err_cl_unlink: | ||
731 | ishtp_cl_unlink(hid_ishtp_cl); | ||
732 | return rv; | ||
733 | } | ||
734 | |||
735 | /** | ||
736 | * hid_ishtp_cl_deinit() - Deinit function for ISHTP client | ||
737 | * @hid_ishtp_cl: ISHTP client instance | ||
738 | * | ||
739 | * Unlink and free hid client | ||
740 | */ | ||
741 | static void hid_ishtp_cl_deinit(struct ishtp_cl *hid_ishtp_cl) | ||
742 | { | ||
743 | ishtp_cl_unlink(hid_ishtp_cl); | ||
744 | ishtp_cl_flush_queues(hid_ishtp_cl); | ||
745 | |||
746 | /* disband and free all Tx and Rx client-level rings */ | ||
747 | ishtp_cl_free(hid_ishtp_cl); | ||
748 | } | ||
749 | |||
750 | static void hid_ishtp_cl_reset_handler(struct work_struct *work) | ||
751 | { | ||
752 | struct ishtp_cl_data *client_data; | ||
753 | struct ishtp_cl *hid_ishtp_cl; | ||
754 | struct ishtp_cl_device *cl_device; | ||
755 | int retry; | ||
756 | int rv; | ||
757 | |||
758 | client_data = container_of(work, struct ishtp_cl_data, work); | ||
759 | |||
760 | hid_ishtp_cl = client_data->hid_ishtp_cl; | ||
761 | cl_device = client_data->cl_device; | ||
762 | |||
763 | hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__, | ||
764 | hid_ishtp_cl); | ||
765 | dev_dbg(&cl_device->dev, "%s\n", __func__); | ||
766 | |||
767 | hid_ishtp_cl_deinit(hid_ishtp_cl); | ||
768 | |||
769 | hid_ishtp_cl = ishtp_cl_allocate(cl_device->ishtp_dev); | ||
770 | if (!hid_ishtp_cl) | ||
771 | return; | ||
772 | |||
773 | cl_device->driver_data = hid_ishtp_cl; | ||
774 | hid_ishtp_cl->client_data = client_data; | ||
775 | client_data->hid_ishtp_cl = hid_ishtp_cl; | ||
776 | |||
777 | client_data->num_hid_devices = 0; | ||
778 | |||
779 | for (retry = 0; retry < 3; ++retry) { | ||
780 | rv = hid_ishtp_cl_init(hid_ishtp_cl, 1); | ||
781 | if (!rv) | ||
782 | break; | ||
783 | dev_err(&client_data->cl_device->dev, "Retry reset init\n"); | ||
784 | } | ||
785 | if (rv) { | ||
786 | dev_err(&client_data->cl_device->dev, "Reset Failed\n"); | ||
787 | hid_ishtp_trace(client_data, "%s Failed hid_ishtp_cl %p\n", | ||
788 | __func__, hid_ishtp_cl); | ||
789 | } | ||
790 | } | ||
791 | |||
792 | /** | ||
793 | * hid_ishtp_cl_probe() - ISHTP client driver probe | ||
794 | * @cl_device: ISHTP client device instance | ||
795 | * | ||
796 | * This function gets called on device create on ISHTP bus | ||
797 | * | ||
798 | * Return: 0 on success, non zero on error | ||
799 | */ | ||
800 | static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device) | ||
801 | { | ||
802 | struct ishtp_cl *hid_ishtp_cl; | ||
803 | struct ishtp_cl_data *client_data; | ||
804 | int rv; | ||
805 | |||
806 | if (!cl_device) | ||
807 | return -ENODEV; | ||
808 | |||
809 | if (uuid_le_cmp(hid_ishtp_guid, | ||
810 | cl_device->fw_client->props.protocol_name) != 0) | ||
811 | return -ENODEV; | ||
812 | |||
813 | client_data = devm_kzalloc(&cl_device->dev, sizeof(*client_data), | ||
814 | GFP_KERNEL); | ||
815 | if (!client_data) | ||
816 | return -ENOMEM; | ||
817 | |||
818 | hid_ishtp_cl = ishtp_cl_allocate(cl_device->ishtp_dev); | ||
819 | if (!hid_ishtp_cl) | ||
820 | return -ENOMEM; | ||
821 | |||
822 | cl_device->driver_data = hid_ishtp_cl; | ||
823 | hid_ishtp_cl->client_data = client_data; | ||
824 | client_data->hid_ishtp_cl = hid_ishtp_cl; | ||
825 | client_data->cl_device = cl_device; | ||
826 | |||
827 | init_waitqueue_head(&client_data->init_wait); | ||
828 | init_waitqueue_head(&client_data->ishtp_resume_wait); | ||
829 | |||
830 | INIT_WORK(&client_data->work, hid_ishtp_cl_reset_handler); | ||
831 | |||
832 | rv = hid_ishtp_cl_init(hid_ishtp_cl, 0); | ||
833 | if (rv) { | ||
834 | ishtp_cl_free(hid_ishtp_cl); | ||
835 | return rv; | ||
836 | } | ||
837 | ishtp_get_device(cl_device); | ||
838 | |||
839 | return 0; | ||
840 | } | ||
841 | |||
842 | /** | ||
843 | * hid_ishtp_cl_remove() - ISHTP client driver remove | ||
844 | * @cl_device: ISHTP client device instance | ||
845 | * | ||
846 | * This function gets called on device remove on ISHTP bus | ||
847 | * | ||
848 | * Return: 0 | ||
849 | */ | ||
850 | static int hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device) | ||
851 | { | ||
852 | struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data; | ||
853 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
854 | |||
855 | hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__, | ||
856 | hid_ishtp_cl); | ||
857 | |||
858 | dev_dbg(&cl_device->dev, "%s\n", __func__); | ||
859 | hid_ishtp_cl->state = ISHTP_CL_DISCONNECTING; | ||
860 | ishtp_cl_disconnect(hid_ishtp_cl); | ||
861 | ishtp_put_device(cl_device); | ||
862 | ishtp_hid_remove(client_data); | ||
863 | hid_ishtp_cl_deinit(hid_ishtp_cl); | ||
864 | |||
865 | hid_ishtp_cl = NULL; | ||
866 | |||
867 | client_data->num_hid_devices = 0; | ||
868 | |||
869 | return 0; | ||
870 | } | ||
871 | |||
872 | /** | ||
873 | * hid_ishtp_cl_reset() - ISHTP client driver reset | ||
874 | * @cl_device: ISHTP client device instance | ||
875 | * | ||
876 | * This function gets called on device reset on ISHTP bus | ||
877 | * | ||
878 | * Return: 0 | ||
879 | */ | ||
880 | static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device) | ||
881 | { | ||
882 | struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data; | ||
883 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
884 | |||
885 | hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__, | ||
886 | hid_ishtp_cl); | ||
887 | |||
888 | schedule_work(&client_data->work); | ||
889 | |||
890 | return 0; | ||
891 | } | ||
892 | |||
893 | #define to_ishtp_cl_device(d) container_of(d, struct ishtp_cl_device, dev) | ||
894 | |||
895 | /** | ||
896 | * hid_ishtp_cl_suspend() - ISHTP client driver suspend | ||
897 | * @device: device instance | ||
898 | * | ||
899 | * This function gets called on system suspend | ||
900 | * | ||
901 | * Return: 0 | ||
902 | */ | ||
903 | static int hid_ishtp_cl_suspend(struct device *device) | ||
904 | { | ||
905 | struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device); | ||
906 | struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data; | ||
907 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
908 | |||
909 | hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__, | ||
910 | hid_ishtp_cl); | ||
911 | client_data->suspended = true; | ||
912 | |||
913 | return 0; | ||
914 | } | ||
915 | |||
916 | /** | ||
917 | * hid_ishtp_cl_resume() - ISHTP client driver resume | ||
918 | * @device: device instance | ||
919 | * | ||
920 | * This function gets called on system resume | ||
921 | * | ||
922 | * Return: 0 | ||
923 | */ | ||
924 | static int hid_ishtp_cl_resume(struct device *device) | ||
925 | { | ||
926 | struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device); | ||
927 | struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data; | ||
928 | struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data; | ||
929 | |||
930 | hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__, | ||
931 | hid_ishtp_cl); | ||
932 | client_data->suspended = false; | ||
933 | return 0; | ||
934 | } | ||
935 | |||
936 | static const struct dev_pm_ops hid_ishtp_pm_ops = { | ||
937 | .suspend = hid_ishtp_cl_suspend, | ||
938 | .resume = hid_ishtp_cl_resume, | ||
939 | }; | ||
940 | |||
941 | static struct ishtp_cl_driver hid_ishtp_cl_driver = { | ||
942 | .name = "ish-hid", | ||
943 | .probe = hid_ishtp_cl_probe, | ||
944 | .remove = hid_ishtp_cl_remove, | ||
945 | .reset = hid_ishtp_cl_reset, | ||
946 | .driver.pm = &hid_ishtp_pm_ops, | ||
947 | }; | ||
948 | |||
949 | static int __init ish_hid_init(void) | ||
950 | { | ||
951 | int rv; | ||
952 | |||
953 | /* Register ISHTP client device driver with ISHTP Bus */ | ||
954 | rv = ishtp_cl_driver_register(&hid_ishtp_cl_driver); | ||
955 | |||
956 | return rv; | ||
957 | |||
958 | } | ||
959 | |||
960 | static void __exit ish_hid_exit(void) | ||
961 | { | ||
962 | ishtp_cl_driver_unregister(&hid_ishtp_cl_driver); | ||
963 | } | ||
964 | |||
965 | late_initcall(ish_hid_init); | ||
966 | module_exit(ish_hid_exit); | ||
967 | |||
968 | MODULE_DESCRIPTION("ISH ISHTP HID client driver"); | ||
969 | /* Primary author */ | ||
970 | MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>"); | ||
971 | /* | ||
972 | * Several modification for multi instance support | ||
973 | * suspend/resume and clean up | ||
974 | */ | ||
975 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); | ||
976 | |||
977 | MODULE_LICENSE("GPL"); | ||
978 | MODULE_ALIAS("ishtp:*"); | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c new file mode 100644 index 000000000000..277983aa1d90 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp-hid.c | |||
@@ -0,0 +1,246 @@ | |||
1 | /* | ||
2 | * ISHTP-HID glue driver. | ||
3 | * | ||
4 | * Copyright (c) 2012-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/hid.h> | ||
17 | #include <uapi/linux/input.h> | ||
18 | #include "ishtp/client.h" | ||
19 | #include "ishtp-hid.h" | ||
20 | |||
21 | /** | ||
22 | * ishtp_hid_parse() - hid-core .parse() callback | ||
23 | * @hid: hid device instance | ||
24 | * | ||
25 | * This function gets called during call to hid_add_device | ||
26 | * | ||
27 | * Return: 0 on success and non zero on error | ||
28 | */ | ||
29 | static int ishtp_hid_parse(struct hid_device *hid) | ||
30 | { | ||
31 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
32 | struct ishtp_cl_data *client_data = hid_data->client_data; | ||
33 | int rv; | ||
34 | |||
35 | rv = hid_parse_report(hid, client_data->report_descr[hid_data->index], | ||
36 | client_data->report_descr_size[hid_data->index]); | ||
37 | if (rv) | ||
38 | return rv; | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | /* Empty callbacks with success return code */ | ||
44 | static int ishtp_hid_start(struct hid_device *hid) | ||
45 | { | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static void ishtp_hid_stop(struct hid_device *hid) | ||
50 | { | ||
51 | } | ||
52 | |||
53 | static int ishtp_hid_open(struct hid_device *hid) | ||
54 | { | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static void ishtp_hid_close(struct hid_device *hid) | ||
59 | { | ||
60 | } | ||
61 | |||
62 | static int ishtp_raw_request(struct hid_device *hdev, unsigned char reportnum, | ||
63 | __u8 *buf, size_t len, unsigned char rtype, int reqtype) | ||
64 | { | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * ishtp_hid_request() - hid-core .request() callback | ||
70 | * @hid: hid device instance | ||
71 | * @rep: pointer to hid_report | ||
72 | * @reqtype: type of req. [GET|SET]_REPORT | ||
73 | * | ||
74 | * This function is used to set/get feaure/input report. | ||
75 | */ | ||
76 | static void ishtp_hid_request(struct hid_device *hid, struct hid_report *rep, | ||
77 | int reqtype) | ||
78 | { | ||
79 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
80 | /* the specific report length, just HID part of it */ | ||
81 | unsigned int len = ((rep->size - 1) >> 3) + 1 + (rep->id > 0); | ||
82 | char *buf; | ||
83 | unsigned int header_size = sizeof(struct hostif_msg); | ||
84 | |||
85 | len += header_size; | ||
86 | |||
87 | hid_data->request_done = false; | ||
88 | switch (reqtype) { | ||
89 | case HID_REQ_GET_REPORT: | ||
90 | hid_ishtp_get_report(hid, rep->id, rep->type); | ||
91 | break; | ||
92 | case HID_REQ_SET_REPORT: | ||
93 | /* | ||
94 | * Spare 7 bytes for 64b accesses through | ||
95 | * get/put_unaligned_le64() | ||
96 | */ | ||
97 | buf = kzalloc(len + 7, GFP_KERNEL); | ||
98 | if (!buf) | ||
99 | return; | ||
100 | |||
101 | hid_output_report(rep, buf + header_size); | ||
102 | hid_ishtp_set_feature(hid, buf, len, rep->id); | ||
103 | kfree(buf); | ||
104 | break; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * ishtp_wait_for_response() - hid-core .wait() callback | ||
110 | * @hid: hid device instance | ||
111 | * | ||
112 | * This function is used to wait after get feaure/input report. | ||
113 | * | ||
114 | * Return: 0 on success and non zero on error | ||
115 | */ | ||
116 | static int ishtp_wait_for_response(struct hid_device *hid) | ||
117 | { | ||
118 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
119 | struct ishtp_cl_data *client_data = hid_data->client_data; | ||
120 | int rv; | ||
121 | |||
122 | hid_ishtp_trace(client_data, "%s hid %p\n", __func__, hid); | ||
123 | |||
124 | rv = ishtp_hid_link_ready_wait(hid_data->client_data); | ||
125 | if (rv) | ||
126 | return rv; | ||
127 | |||
128 | if (!hid_data->request_done) | ||
129 | wait_event_interruptible_timeout(hid_data->hid_wait, | ||
130 | hid_data->request_done, 3 * HZ); | ||
131 | |||
132 | if (!hid_data->request_done) { | ||
133 | hid_err(hid, | ||
134 | "timeout waiting for response from ISHTP device\n"); | ||
135 | return -ETIMEDOUT; | ||
136 | } | ||
137 | hid_ishtp_trace(client_data, "%s hid %p done\n", __func__, hid); | ||
138 | |||
139 | hid_data->request_done = false; | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | /** | ||
145 | * ishtp_hid_wakeup() - Wakeup caller | ||
146 | * @hid: hid device instance | ||
147 | * | ||
148 | * This function will wakeup caller waiting for Get/Set feature report | ||
149 | */ | ||
150 | void ishtp_hid_wakeup(struct hid_device *hid) | ||
151 | { | ||
152 | struct ishtp_hid_data *hid_data = hid->driver_data; | ||
153 | |||
154 | hid_data->request_done = true; | ||
155 | wake_up_interruptible(&hid_data->hid_wait); | ||
156 | } | ||
157 | |||
158 | static struct hid_ll_driver ishtp_hid_ll_driver = { | ||
159 | .parse = ishtp_hid_parse, | ||
160 | .start = ishtp_hid_start, | ||
161 | .stop = ishtp_hid_stop, | ||
162 | .open = ishtp_hid_open, | ||
163 | .close = ishtp_hid_close, | ||
164 | .request = ishtp_hid_request, | ||
165 | .wait = ishtp_wait_for_response, | ||
166 | .raw_request = ishtp_raw_request | ||
167 | }; | ||
168 | |||
169 | /** | ||
170 | * ishtp_hid_probe() - hid register ll driver | ||
171 | * @cur_hid_dev: Index of hid device calling to register | ||
172 | * @client_data: Client data pointer | ||
173 | * | ||
174 | * This function is used to allocate and add HID device. | ||
175 | * | ||
176 | * Return: 0 on success, non zero on error | ||
177 | */ | ||
178 | int ishtp_hid_probe(unsigned int cur_hid_dev, | ||
179 | struct ishtp_cl_data *client_data) | ||
180 | { | ||
181 | int rv; | ||
182 | struct hid_device *hid; | ||
183 | struct ishtp_hid_data *hid_data; | ||
184 | |||
185 | hid = hid_allocate_device(); | ||
186 | if (IS_ERR(hid)) { | ||
187 | rv = PTR_ERR(hid); | ||
188 | return -ENOMEM; | ||
189 | } | ||
190 | |||
191 | hid_data = kzalloc(sizeof(*hid_data), GFP_KERNEL); | ||
192 | if (!hid_data) { | ||
193 | rv = -ENOMEM; | ||
194 | goto err_hid_data; | ||
195 | } | ||
196 | |||
197 | hid_data->index = cur_hid_dev; | ||
198 | hid_data->client_data = client_data; | ||
199 | init_waitqueue_head(&hid_data->hid_wait); | ||
200 | |||
201 | hid->driver_data = hid_data; | ||
202 | |||
203 | client_data->hid_sensor_hubs[cur_hid_dev] = hid; | ||
204 | |||
205 | hid->ll_driver = &ishtp_hid_ll_driver; | ||
206 | hid->bus = BUS_INTEL_ISHTP; | ||
207 | hid->dev.parent = &client_data->cl_device->dev; | ||
208 | hid->version = le16_to_cpu(ISH_HID_VERSION); | ||
209 | hid->vendor = le16_to_cpu(ISH_HID_VENDOR); | ||
210 | hid->product = le16_to_cpu(ISH_HID_PRODUCT); | ||
211 | snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX", "hid-ishtp", | ||
212 | hid->vendor, hid->product); | ||
213 | |||
214 | rv = hid_add_device(hid); | ||
215 | if (rv) | ||
216 | goto err_hid_device; | ||
217 | |||
218 | hid_ishtp_trace(client_data, "%s allocated hid %p\n", __func__, hid); | ||
219 | |||
220 | return 0; | ||
221 | |||
222 | err_hid_device: | ||
223 | kfree(hid_data); | ||
224 | err_hid_data: | ||
225 | kfree(hid); | ||
226 | return rv; | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * ishtp_hid_probe() - Remove registered hid device | ||
231 | * @client_data: client data pointer | ||
232 | * | ||
233 | * This function is used to destroy allocatd HID device. | ||
234 | */ | ||
235 | void ishtp_hid_remove(struct ishtp_cl_data *client_data) | ||
236 | { | ||
237 | int i; | ||
238 | |||
239 | for (i = 0; i < client_data->num_hid_devices; ++i) { | ||
240 | if (client_data->hid_sensor_hubs[i]) { | ||
241 | kfree(client_data->hid_sensor_hubs[i]->driver_data); | ||
242 | hid_destroy_device(client_data->hid_sensor_hubs[i]); | ||
243 | client_data->hid_sensor_hubs[i] = NULL; | ||
244 | } | ||
245 | } | ||
246 | } | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.h b/drivers/hid/intel-ish-hid/ishtp-hid.h new file mode 100644 index 000000000000..f5c7eb79b7b5 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp-hid.h | |||
@@ -0,0 +1,182 @@ | |||
1 | /* | ||
2 | * ISHTP-HID glue driver's definitions. | ||
3 | * | ||
4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | #ifndef ISHTP_HID__H | ||
16 | #define ISHTP_HID__H | ||
17 | |||
18 | /* The fixed ISH product and vendor id */ | ||
19 | #define ISH_HID_VENDOR 0x8086 | ||
20 | #define ISH_HID_PRODUCT 0x22D8 | ||
21 | #define ISH_HID_VERSION 0x0200 | ||
22 | |||
23 | #define CMD_MASK 0x7F | ||
24 | #define IS_RESPONSE 0x80 | ||
25 | |||
26 | /* Used to dump to Linux trace buffer, if enabled */ | ||
27 | #define hid_ishtp_trace(client, ...) \ | ||
28 | client->cl_device->ishtp_dev->print_log(\ | ||
29 | client->cl_device->ishtp_dev, __VA_ARGS__) | ||
30 | |||
31 | /* ISH Transport protocol (ISHTP in short) GUID */ | ||
32 | static const uuid_le hid_ishtp_guid = UUID_LE(0x33AECD58, 0xB679, 0x4E54, | ||
33 | 0x9B, 0xD9, 0xA0, 0x4D, 0x34, | ||
34 | 0xF0, 0xC2, 0x26); | ||
35 | |||
36 | /* ISH HID message structure */ | ||
37 | struct hostif_msg_hdr { | ||
38 | uint8_t command; /* Bit 7: is_response */ | ||
39 | uint8_t device_id; | ||
40 | uint8_t status; | ||
41 | uint8_t flags; | ||
42 | uint16_t size; | ||
43 | } __packed; | ||
44 | |||
45 | struct hostif_msg { | ||
46 | struct hostif_msg_hdr hdr; | ||
47 | } __packed; | ||
48 | |||
49 | struct hostif_msg_to_sensor { | ||
50 | struct hostif_msg_hdr hdr; | ||
51 | uint8_t report_id; | ||
52 | } __packed; | ||
53 | |||
54 | struct device_info { | ||
55 | uint32_t dev_id; | ||
56 | uint8_t dev_class; | ||
57 | uint16_t pid; | ||
58 | uint16_t vid; | ||
59 | } __packed; | ||
60 | |||
61 | struct ishtp_version { | ||
62 | uint8_t major; | ||
63 | uint8_t minor; | ||
64 | uint8_t hotfix; | ||
65 | uint16_t build; | ||
66 | } __packed; | ||
67 | |||
68 | /* struct for ISHTP aggregated input data */ | ||
69 | struct report_list { | ||
70 | uint16_t total_size; | ||
71 | uint8_t num_of_reports; | ||
72 | uint8_t flags; | ||
73 | struct { | ||
74 | uint16_t size_of_report; | ||
75 | uint8_t report[1]; | ||
76 | } __packed reports[1]; | ||
77 | } __packed; | ||
78 | |||
79 | /* HOSTIF commands */ | ||
80 | #define HOSTIF_HID_COMMAND_BASE 0 | ||
81 | #define HOSTIF_GET_HID_DESCRIPTOR 0 | ||
82 | #define HOSTIF_GET_REPORT_DESCRIPTOR 1 | ||
83 | #define HOSTIF_GET_FEATURE_REPORT 2 | ||
84 | #define HOSTIF_SET_FEATURE_REPORT 3 | ||
85 | #define HOSTIF_GET_INPUT_REPORT 4 | ||
86 | #define HOSTIF_PUBLISH_INPUT_REPORT 5 | ||
87 | #define HOSTIF_PUBLISH_INPUT_REPORT_LIST 6 | ||
88 | #define HOSTIF_DM_COMMAND_BASE 32 | ||
89 | #define HOSTIF_DM_ENUM_DEVICES 33 | ||
90 | #define HOSTIF_DM_ADD_DEVICE 34 | ||
91 | |||
92 | #define MAX_HID_DEVICES 32 | ||
93 | |||
94 | /** | ||
95 | * struct ishtp_cl_data - Encapsulate per ISH TP HID Client | ||
96 | * @enum_device_done: Enum devices response complete flag | ||
97 | * @hid_descr_done: HID descriptor complete flag | ||
98 | * @report_descr_done: Get report descriptor complete flag | ||
99 | * @init_done: Init process completed successfully | ||
100 | * @suspended: System is under suspend state or in progress | ||
101 | * @num_hid_devices: Number of HID devices enumerated in this client | ||
102 | * @cur_hid_dev: This keeps track of the device index for which | ||
103 | * initialization and registration with HID core | ||
104 | * in progress. | ||
105 | * @hid_devices: Store vid/pid/devid for each enumerated HID device | ||
106 | * @report_descr: Stores the raw report descriptors for each HID device | ||
107 | * @report_descr_size: Report description of size of above repo_descr[] | ||
108 | * @hid_sensor_hubs: Pointer to hid_device for all HID device, so that | ||
109 | * when clients are removed, they can be freed | ||
110 | * @hid_descr: Pointer to hid descriptor for each enumerated hid | ||
111 | * device | ||
112 | * @hid_descr_size: Size of each above report descriptor | ||
113 | * @init_wait: Wait queue to wait during initialization, where the | ||
114 | * client send message to ISH FW and wait for response | ||
115 | * @ishtp_hid_wait: The wait for get report during wait callback from hid | ||
116 | * core | ||
117 | * @bad_recv_cnt: Running count of packets received with error | ||
118 | * @multi_packet_cnt: Count of fragmented packet count | ||
119 | * | ||
120 | * This structure is used to store completion flags and per client data like | ||
121 | * like report description, number of HID devices etc. | ||
122 | */ | ||
123 | struct ishtp_cl_data { | ||
124 | /* completion flags */ | ||
125 | bool enum_devices_done; | ||
126 | bool hid_descr_done; | ||
127 | bool report_descr_done; | ||
128 | bool init_done; | ||
129 | bool suspended; | ||
130 | |||
131 | unsigned int num_hid_devices; | ||
132 | unsigned int cur_hid_dev; | ||
133 | unsigned int hid_dev_count; | ||
134 | |||
135 | struct device_info *hid_devices; | ||
136 | unsigned char *report_descr[MAX_HID_DEVICES]; | ||
137 | int report_descr_size[MAX_HID_DEVICES]; | ||
138 | struct hid_device *hid_sensor_hubs[MAX_HID_DEVICES]; | ||
139 | unsigned char *hid_descr[MAX_HID_DEVICES]; | ||
140 | int hid_descr_size[MAX_HID_DEVICES]; | ||
141 | |||
142 | wait_queue_head_t init_wait; | ||
143 | wait_queue_head_t ishtp_resume_wait; | ||
144 | struct ishtp_cl *hid_ishtp_cl; | ||
145 | |||
146 | /* Statistics */ | ||
147 | unsigned int bad_recv_cnt; | ||
148 | int multi_packet_cnt; | ||
149 | |||
150 | struct work_struct work; | ||
151 | struct ishtp_cl_device *cl_device; | ||
152 | }; | ||
153 | |||
154 | /** | ||
155 | * struct ishtp_hid_data - Per instance HID data | ||
156 | * @index: Device index in the order of enumeration | ||
157 | * @request_done: Get Feature/Input report complete flag | ||
158 | * used during get/set request from hid core | ||
159 | * @client_data: Link to the client instance | ||
160 | * @hid_wait: Completion waitq | ||
161 | * | ||
162 | * Used to tie hid hid->driver data to driver client instance | ||
163 | */ | ||
164 | struct ishtp_hid_data { | ||
165 | int index; | ||
166 | bool request_done; | ||
167 | struct ishtp_cl_data *client_data; | ||
168 | wait_queue_head_t hid_wait; | ||
169 | }; | ||
170 | |||
171 | /* Interface functions between HID LL driver and ISH TP client */ | ||
172 | void hid_ishtp_set_feature(struct hid_device *hid, char *buf, unsigned int len, | ||
173 | int report_id); | ||
174 | void hid_ishtp_get_report(struct hid_device *hid, int report_id, | ||
175 | int report_type); | ||
176 | int ishtp_hid_probe(unsigned int cur_hid_dev, | ||
177 | struct ishtp_cl_data *client_data); | ||
178 | void ishtp_hid_remove(struct ishtp_cl_data *client_data); | ||
179 | int ishtp_hid_link_ready_wait(struct ishtp_cl_data *client_data); | ||
180 | void ishtp_hid_wakeup(struct hid_device *hid); | ||
181 | |||
182 | #endif /* ISHTP_HID__H */ | ||
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index c51494119817..e794f7bee22f 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h | |||
@@ -248,6 +248,7 @@ struct input_mask { | |||
248 | #define BUS_SPI 0x1C | 248 | #define BUS_SPI 0x1C |
249 | #define BUS_RMI 0x1D | 249 | #define BUS_RMI 0x1D |
250 | #define BUS_CEC 0x1E | 250 | #define BUS_CEC 0x1E |
251 | #define BUS_INTEL_ISHTP 0x1F | ||
251 | 252 | ||
252 | /* | 253 | /* |
253 | * MT_TOOL types | 254 | * MT_TOOL types |