Commit | Line | Data |
---|---|---|
2025cf9e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
0b28cb4b SP |
2 | /* |
3 | * ISHTP-HID glue driver. | |
4 | * | |
5 | * Copyright (c) 2012-2016, Intel Corporation. | |
0b28cb4b SP |
6 | */ |
7 | ||
8 | #include <linux/hid.h> | |
7ab21842 | 9 | #include <linux/intel-ish-client-if.h> |
0b28cb4b | 10 | #include <uapi/linux/input.h> |
0b28cb4b SP |
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, client_data->report_descr[hid_data->index], | |
28 | 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 | ||
e19595fc HY |
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) | |
0b28cb4b | 57 | { |
e19595fc HY |
58 | struct ishtp_hid_data *hid_data = hid->driver_data; |
59 | char *ishtp_buf = NULL; | |
60 | size_t ishtp_buf_len; | |
61 | unsigned int header_size = 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, reportnum, 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(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, ishtp_buf, ishtp_buf_len, reportnum); | |
87 | kfree(ishtp_buf); | |
88 | break; | |
89 | } | |
90 | ||
91 | hid_hw_wait(hid); | |
92 | ||
93 | return len; | |
0b28cb4b SP |
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 header_size = 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: | |
e19595fc | 118 | hid_data->raw_get_req = false; |
0b28cb4b SP |
119 | hid_ishtp_get_report(hid, rep->id, 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(len + 7, GFP_KERNEL); | |
127 | if (!buf) | |
128 | return; | |
129 | ||
130 | hid_output_report(rep, buf + header_size); | |
131 | hid_ishtp_set_feature(hid, buf, len, rep->id); | |
132 | kfree(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; | |
0b28cb4b SP |
148 | int rv; |
149 | ||
150 | hid_ishtp_trace(client_data, "%s hid %p\n", __func__, hid); | |
151 | ||
152 | rv = ishtp_hid_link_ready_wait(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 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(hid)) { | |
215 | rv = PTR_ERR(hid); | |
216 | return -ENOMEM; | |
217 | } | |
218 | ||
219 | hid_data = kzalloc(sizeof(*hid_data), GFP_KERNEL); | |
220 | if (!hid_data) { | |
221 | rv = -ENOMEM; | |
222 | goto err_hid_data; | |
223 | } | |
224 | ||
225 | hid_data->index = cur_hid_dev; | |
226 | hid_data->client_data = client_data; | |
227 | init_waitqueue_head(&hid_data->hid_wait); | |
228 | ||
229 | hid->driver_data = hid_data; | |
230 | ||
231 | client_data->hid_sensor_hubs[cur_hid_dev] = hid; | |
232 | ||
233 | hid->ll_driver = &ishtp_hid_ll_driver; | |
234 | hid->bus = BUS_INTEL_ISHTP; | |
7ab21842 SP |
235 | hid->dev.parent = ishtp_device(client_data->cl_device); |
236 | ||
0b28cb4b | 237 | hid->version = le16_to_cpu(ISH_HID_VERSION); |
1578461a SP |
238 | hid->vendor = le16_to_cpu(client_data->hid_devices[cur_hid_dev].vid); |
239 | hid->product = le16_to_cpu(client_data->hid_devices[cur_hid_dev].pid); | |
5299a92a | 240 | snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-ishtp", |
0b28cb4b SP |
241 | hid->vendor, hid->product); |
242 | ||
243 | rv = hid_add_device(hid); | |
244 | if (rv) | |
245 | goto err_hid_device; | |
246 | ||
247 | hid_ishtp_trace(client_data, "%s allocated hid %p\n", __func__, hid); | |
248 | ||
249 | return 0; | |
250 | ||
251 | err_hid_device: | |
252 | kfree(hid_data); | |
253 | err_hid_data: | |
6e0856d3 | 254 | hid_destroy_device(hid); |
0b28cb4b SP |
255 | return rv; |
256 | } | |
257 | ||
258 | /** | |
259 | * ishtp_hid_probe() - Remove registered hid device | |
260 | * @client_data: client data pointer | |
261 | * | |
262 | * This function is used to destroy allocatd HID device. | |
263 | */ | |
264 | void ishtp_hid_remove(struct ishtp_cl_data *client_data) | |
265 | { | |
266 | int i; | |
267 | ||
268 | for (i = 0; i < client_data->num_hid_devices; ++i) { | |
269 | if (client_data->hid_sensor_hubs[i]) { | |
270 | kfree(client_data->hid_sensor_hubs[i]->driver_data); | |
271 | hid_destroy_device(client_data->hid_sensor_hubs[i]); | |
272 | client_data->hid_sensor_hubs[i] = NULL; | |
273 | } | |
274 | } | |
275 | } |