blob: 4db337cf7acc2138b699c8e1a6a34fe6c6f50e24 [file] [log] [blame]
Viresh Kumar96eab772015-03-16 16:49:37 +05301/*
2 * HID class driver for the Greybus.
3 *
4 * Copyright 2014 Google Inc.
5 * Copyright 2014 Linaro Ltd.
6 *
7 * Released under the GPLv2 only.
8 */
9
10#include <linux/bitops.h>
11#include <linux/hid.h>
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/mutex.h>
15#include <linux/slab.h>
16
17#include "greybus.h"
18
Viresh Kumar96eab772015-03-16 16:49:37 +053019/* Greybus HID device's structure */
20struct gb_hid {
21 struct gb_connection *connection;
Viresh Kumar96eab772015-03-16 16:49:37 +053022
23 struct hid_device *hid;
24 struct gb_hid_desc_response hdesc;
25
26 unsigned long flags;
27#define GB_HID_STARTED 0x01
28#define GB_HID_READ_PENDING 0x04
29
30 unsigned int bufsize;
31 char *inbuf;
32};
33
34static DEFINE_MUTEX(gb_hid_open_mutex);
35
Viresh Kumard79ae492015-08-08 10:25:32 +053036/* Routines to get controller's information over greybus */
Viresh Kumar96eab772015-03-16 16:49:37 +053037
Viresh Kumar96eab772015-03-16 16:49:37 +053038/* Operations performed on greybus */
39static int gb_hid_get_desc(struct gb_hid *ghid)
40{
41 return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_DESC, NULL,
42 0, &ghid->hdesc, sizeof(ghid->hdesc));
43}
44
45static int gb_hid_get_report_desc(struct gb_hid *ghid, char *rdesc)
46{
47 return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT_DESC,
48 NULL, 0, rdesc,
49 le16_to_cpu(ghid->hdesc.wReportDescLength));
50}
51
52static int gb_hid_set_power(struct gb_hid *ghid, int type)
53{
54 return gb_operation_sync(ghid->connection, type, NULL, 0, NULL, 0);
55}
56
57static int gb_hid_get_report(struct gb_hid *ghid, u8 report_type, u8 report_id,
58 unsigned char *buf, int len)
59{
60 struct gb_hid_get_report_request request;
61
62 request.report_type = report_type;
63 request.report_id = report_id;
64
65 return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT,
66 &request, sizeof(request), buf, len);
67}
68
Viresh Kumar96eab772015-03-16 16:49:37 +053069static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id,
Viresh Kumar292cca92015-03-18 11:41:39 +053070 unsigned char *buf, int len)
Viresh Kumar96eab772015-03-16 16:49:37 +053071{
72 struct gb_hid_set_report_request *request;
73 struct gb_operation *operation;
74 int ret, size = sizeof(*request) + len - 1;
Viresh Kumar96eab772015-03-16 16:49:37 +053075
Viresh Kumar292cca92015-03-18 11:41:39 +053076 operation = gb_operation_create(ghid->connection,
Johan Hovolde4207212015-07-01 12:37:22 +020077 GB_HID_TYPE_SET_REPORT, size, 0,
78 GFP_KERNEL);
Viresh Kumar96eab772015-03-16 16:49:37 +053079 if (!operation)
80 return -ENOMEM;
81
82 request = operation->request->payload;
83 request->report_type = report_type;
84 request->report_id = report_id;
85 memcpy(request->report, buf, len);
86
87 ret = gb_operation_request_send_sync(operation);
Johan Hovoldb67f2d12015-03-27 12:45:44 +010088 if (ret) {
Greg Kroah-Hartmanb2a637d2015-10-14 11:16:24 -070089 dev_err(&operation->connection->bundle->dev,
Johan Hovoldb67f2d12015-03-27 12:45:44 +010090 "failed to set report: %d\n", ret);
91 } else {
Viresh Kumar96eab772015-03-16 16:49:37 +053092 ret = len;
Johan Hovoldb67f2d12015-03-27 12:45:44 +010093 }
Viresh Kumar96eab772015-03-16 16:49:37 +053094
Johan Hovold6ab1ce42015-09-26 17:59:15 -070095 gb_operation_put(operation);
96
Viresh Kumar96eab772015-03-16 16:49:37 +053097 return ret;
98}
99
Johan Hovold4324282d2016-01-21 17:34:23 +0100100static int gb_hid_request_handler(struct gb_operation *op)
Viresh Kumar96eab772015-03-16 16:49:37 +0530101{
102 struct gb_connection *connection = op->connection;
103 struct gb_hid *ghid = connection->private;
104 struct gb_hid_input_report_request *request = op->request->payload;
Viresh Kumar96eab772015-03-16 16:49:37 +0530105
Johan Hovold4324282d2016-01-21 17:34:23 +0100106 if (op->type != GB_HID_TYPE_IRQ_EVENT) {
Greg Kroah-Hartmanb2a637d2015-10-14 11:16:24 -0700107 dev_err(&connection->bundle->dev,
Johan Hovoldb67f2d12015-03-27 12:45:44 +0100108 "unsupported unsolicited request\n");
Johan Hovold973ccfd2015-03-27 12:45:49 +0100109 return -EINVAL;
Viresh Kumar96eab772015-03-16 16:49:37 +0530110 }
111
Viresh Kumar96eab772015-03-16 16:49:37 +0530112 if (test_bit(GB_HID_STARTED, &ghid->flags))
113 hid_input_report(ghid->hid, HID_INPUT_REPORT,
Viresh Kumar581baac2015-04-03 12:00:48 +0530114 request->report, op->request->payload_size, 1);
Johan Hovoldecf47ab2015-03-27 12:45:45 +0100115
Johan Hovold973ccfd2015-03-27 12:45:49 +0100116 return 0;
Viresh Kumar96eab772015-03-16 16:49:37 +0530117}
118
119
120static int gb_hid_report_len(struct hid_report *report)
121{
122 return ((report->size - 1) >> 3) + 1 +
123 report->device->report_enum[report->type].numbered;
124}
125
126static void gb_hid_find_max_report(struct hid_device *hid, unsigned int type,
127 unsigned int *max)
128{
129 struct hid_report *report;
130 unsigned int size;
131
132 list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
133 size = gb_hid_report_len(report);
134 if (*max < size)
135 *max = size;
136 }
137}
138
139static void gb_hid_free_buffers(struct gb_hid *ghid)
140{
141 kfree(ghid->inbuf);
142 ghid->inbuf = NULL;
143 ghid->bufsize = 0;
144}
145
146static int gb_hid_alloc_buffers(struct gb_hid *ghid, size_t bufsize)
147{
148 ghid->inbuf = kzalloc(bufsize, GFP_KERNEL);
149 if (!ghid->inbuf)
150 return -ENOMEM;
151
152 ghid->bufsize = bufsize;
153
154 return 0;
155}
156
157/* Routines dealing with reports */
158static void gb_hid_init_report(struct gb_hid *ghid, struct hid_report *report)
159{
160 unsigned int size;
161
162 size = gb_hid_report_len(report);
163 if (gb_hid_get_report(ghid, report->type, report->id, ghid->inbuf,
164 size))
165 return;
166
167 /*
168 * hid->driver_lock is held as we are in probe function,
169 * we just need to setup the input fields, so using
170 * hid_report_raw_event is safe.
171 */
172 hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, size, 1);
173}
174
175static void gb_hid_init_reports(struct gb_hid *ghid)
176{
177 struct hid_device *hid = ghid->hid;
178 struct hid_report *report;
179
180 list_for_each_entry(report,
181 &hid->report_enum[HID_INPUT_REPORT].report_list, list)
182 gb_hid_init_report(ghid, report);
183
184 list_for_each_entry(report,
185 &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
186 gb_hid_init_report(ghid, report);
187}
188
189static int __gb_hid_get_raw_report(struct hid_device *hid,
190 unsigned char report_number, __u8 *buf, size_t count,
191 unsigned char report_type)
192{
193 struct gb_hid *ghid = hid->driver_data;
194 int ret;
195
196 if (report_type == HID_OUTPUT_REPORT)
197 return -EINVAL;
198
199 ret = gb_hid_get_report(ghid, report_type, report_number, buf, count);
200 if (!ret)
201 ret = count;
202
203 return ret;
204}
205
206static int __gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
Viresh Kumar292cca92015-03-18 11:41:39 +0530207 size_t len, unsigned char report_type)
Viresh Kumar96eab772015-03-16 16:49:37 +0530208{
209 struct gb_hid *ghid = hid->driver_data;
210 int report_id = buf[0];
211 int ret;
212
213 if (report_type == HID_INPUT_REPORT)
214 return -EINVAL;
215
216 if (report_id) {
217 buf++;
218 len--;
219 }
220
Viresh Kumar292cca92015-03-18 11:41:39 +0530221 ret = gb_hid_set_report(ghid, report_type, report_id, buf, len);
Viresh Kumar96eab772015-03-16 16:49:37 +0530222 if (report_id && ret >= 0)
223 ret++; /* add report_id to the number of transfered bytes */
224
225 return 0;
226}
227
228static int gb_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
229 __u8 *buf, size_t len, unsigned char rtype,
230 int reqtype)
231{
232 switch (reqtype) {
233 case HID_REQ_GET_REPORT:
234 return __gb_hid_get_raw_report(hid, reportnum, buf, len, rtype);
235 case HID_REQ_SET_REPORT:
236 if (buf[0] != reportnum)
237 return -EINVAL;
Viresh Kumar292cca92015-03-18 11:41:39 +0530238 return __gb_hid_output_raw_report(hid, buf, len, rtype);
Viresh Kumar96eab772015-03-16 16:49:37 +0530239 default:
240 return -EIO;
241 }
242}
243
Viresh Kumar292cca92015-03-18 11:41:39 +0530244#if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)
Viresh Kumar96eab772015-03-16 16:49:37 +0530245static int gb_hid_get_raw_report(struct hid_device *hid,
246 unsigned char reportnum, __u8 *buf,
247 size_t len, unsigned char rtype)
248{
249 return gb_hid_raw_request(hid, reportnum, buf, len, rtype,
250 HID_REQ_GET_REPORT);
251}
252
253static int gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
254 size_t len, unsigned char rtype)
255{
256 return gb_hid_raw_request(hid, buf[0], buf, len, rtype,
257 HID_REQ_SET_REPORT);
258}
259#endif
260
261/* HID Callbacks */
262static int gb_hid_parse(struct hid_device *hid)
263{
264 struct gb_hid *ghid = hid->driver_data;
265 unsigned int rsize;
266 char *rdesc;
267 int ret;
268
269 rsize = le16_to_cpu(ghid->hdesc.wReportDescLength);
270 if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
271 dbg_hid("weird size of report descriptor (%u)\n", rsize);
272 return -EINVAL;
273 }
274
275 rdesc = kzalloc(rsize, GFP_KERNEL);
276 if (!rdesc) {
277 dbg_hid("couldn't allocate rdesc memory\n");
278 return -ENOMEM;
279 }
280
281 ret = gb_hid_get_report_desc(ghid, rdesc);
282 if (ret) {
283 hid_err(hid, "reading report descriptor failed\n");
284 goto free_rdesc;
285 }
286
287 ret = hid_parse_report(hid, rdesc, rsize);
288 if (ret)
289 dbg_hid("parsing report descriptor failed\n");
290
291free_rdesc:
292 kfree(rdesc);
293
294 return ret;
295}
296
297static int gb_hid_start(struct hid_device *hid)
298{
299 struct gb_hid *ghid = hid->driver_data;
300 unsigned int bufsize = HID_MIN_BUFFER_SIZE;
301 int ret;
302
303 gb_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize);
304 gb_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize);
305 gb_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize);
306
307 if (bufsize > HID_MAX_BUFFER_SIZE)
308 bufsize = HID_MAX_BUFFER_SIZE;
309
310 ret = gb_hid_alloc_buffers(ghid, bufsize);
311 if (ret)
312 return ret;
313
314 if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
315 gb_hid_init_reports(ghid);
316
317 return 0;
318}
319
320static void gb_hid_stop(struct hid_device *hid)
321{
322 struct gb_hid *ghid = hid->driver_data;
323
324 gb_hid_free_buffers(ghid);
325}
326
327static int gb_hid_open(struct hid_device *hid)
328{
329 struct gb_hid *ghid = hid->driver_data;
330 int ret = 0;
331
332 mutex_lock(&gb_hid_open_mutex);
333 if (!hid->open++) {
334 ret = gb_hid_set_power(ghid, GB_HID_TYPE_PWR_ON);
335 if (ret < 0)
336 hid->open--;
337 else
338 set_bit(GB_HID_STARTED, &ghid->flags);
339 }
340 mutex_unlock(&gb_hid_open_mutex);
341
342 return ret;
343}
344
345static void gb_hid_close(struct hid_device *hid)
346{
347 struct gb_hid *ghid = hid->driver_data;
348
349 /*
350 * Protecting hid->open to make sure we don't restart data acquistion
351 * due to a resumption we no longer care about..
352 */
353 mutex_lock(&gb_hid_open_mutex);
354 if (!--hid->open) {
355 clear_bit(GB_HID_STARTED, &ghid->flags);
356
357 /* Save some power */
358 WARN_ON(gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF));
359 }
360 mutex_unlock(&gb_hid_open_mutex);
361}
362
363static int gb_hid_power(struct hid_device *hid, int lvl)
364{
365 struct gb_hid *ghid = hid->driver_data;
366
367 switch (lvl) {
368 case PM_HINT_FULLON:
369 return gb_hid_set_power(ghid, GB_HID_TYPE_PWR_ON);
370 case PM_HINT_NORMAL:
371 return gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF);
372 }
373
374 return 0;
375}
376
377/* HID structure to pass callbacks */
378static struct hid_ll_driver gb_hid_ll_driver = {
379 .parse = gb_hid_parse,
380 .start = gb_hid_start,
381 .stop = gb_hid_stop,
382 .open = gb_hid_open,
383 .close = gb_hid_close,
384 .power = gb_hid_power,
385#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
Viresh Kumar96eab772015-03-16 16:49:37 +0530386 .raw_request = gb_hid_raw_request,
387#endif
388};
389
390static int gb_hid_init(struct gb_hid *ghid)
391{
392 struct hid_device *hid = ghid->hid;
393 int ret;
394
Viresh Kumar96eab772015-03-16 16:49:37 +0530395 ret = gb_hid_get_desc(ghid);
396 if (ret)
397 return ret;
398
399 hid->version = le16_to_cpu(ghid->hdesc.bcdHID);
400 hid->vendor = le16_to_cpu(ghid->hdesc.wVendorID);
401 hid->product = le16_to_cpu(ghid->hdesc.wProductID);
402 hid->country = ghid->hdesc.bCountryCode;
403
404 hid->driver_data = ghid;
405 hid->ll_driver = &gb_hid_ll_driver;
Greg Kroah-Hartmanb2a637d2015-10-14 11:16:24 -0700406 hid->dev.parent = &ghid->connection->bundle->dev;
Viresh Kumar96eab772015-03-16 16:49:37 +0530407#if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)
408 hid->hid_get_raw_report = gb_hid_get_raw_report;
409 hid->hid_output_raw_report = gb_hid_output_raw_report;
410#endif
411// hid->bus = BUS_GREYBUS; /* Need a bustype for GREYBUS in <linux/input.h> */
412
413 /* Set HID device's name */
Viresh Kumar2f3db922015-12-04 21:30:09 +0530414 snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X",
Greg Kroah-Hartmanb2a637d2015-10-14 11:16:24 -0700415 dev_name(&ghid->connection->bundle->dev),
416 hid->vendor, hid->product);
Viresh Kumar96eab772015-03-16 16:49:37 +0530417
418 return 0;
419}
420
Johan Hovold4324282d2016-01-21 17:34:23 +0100421static int gb_hid_probe(struct gb_bundle *bundle,
422 const struct greybus_bundle_id *id)
Viresh Kumar96eab772015-03-16 16:49:37 +0530423{
Johan Hovold4324282d2016-01-21 17:34:23 +0100424 struct greybus_descriptor_cport *cport_desc;
425 struct gb_connection *connection;
Viresh Kumar96eab772015-03-16 16:49:37 +0530426 struct hid_device *hid;
427 struct gb_hid *ghid;
428 int ret;
429
Johan Hovold4324282d2016-01-21 17:34:23 +0100430 if (bundle->num_cports != 1)
431 return -ENODEV;
432
433 cport_desc = &bundle->cport_desc[0];
434 if (cport_desc->protocol_id != GREYBUS_PROTOCOL_HID)
435 return -ENODEV;
436
Viresh Kumar96eab772015-03-16 16:49:37 +0530437 ghid = kzalloc(sizeof(*ghid), GFP_KERNEL);
438 if (!ghid)
439 return -ENOMEM;
440
Johan Hovold4324282d2016-01-21 17:34:23 +0100441 connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
442 gb_hid_request_handler);
443 if (IS_ERR(connection)) {
444 ret = PTR_ERR(connection);
Johan Hovold5dd8cc52016-01-21 17:34:22 +0100445 goto err_free_ghid;
Viresh Kumar96eab772015-03-16 16:49:37 +0530446 }
447
448 connection->private = ghid;
449 ghid->connection = connection;
Johan Hovold4324282d2016-01-21 17:34:23 +0100450
451 hid = hid_allocate_device();
452 if (IS_ERR(hid)) {
453 ret = PTR_ERR(hid);
454 goto err_connection_destroy;
455 }
456
Viresh Kumar96eab772015-03-16 16:49:37 +0530457 ghid->hid = hid;
458
Johan Hovold4324282d2016-01-21 17:34:23 +0100459 greybus_set_drvdata(bundle, ghid);
460
461 ret = gb_connection_enable(connection);
462 if (ret)
463 goto err_destroy_hid;
464
Viresh Kumar96eab772015-03-16 16:49:37 +0530465 ret = gb_hid_init(ghid);
466 if (ret)
Johan Hovold4324282d2016-01-21 17:34:23 +0100467 goto err_connection_disable;
Viresh Kumar96eab772015-03-16 16:49:37 +0530468
469 ret = hid_add_device(hid);
Johan Hovold5dd8cc52016-01-21 17:34:22 +0100470 if (ret) {
471 hid_err(hid, "can't add hid device: %d\n", ret);
Johan Hovold4324282d2016-01-21 17:34:23 +0100472 goto err_connection_disable;
Johan Hovold5dd8cc52016-01-21 17:34:22 +0100473 }
Viresh Kumar96eab772015-03-16 16:49:37 +0530474
Johan Hovold5dd8cc52016-01-21 17:34:22 +0100475 return 0;
Viresh Kumar96eab772015-03-16 16:49:37 +0530476
Johan Hovold4324282d2016-01-21 17:34:23 +0100477err_connection_disable:
478 gb_connection_disable(connection);
Johan Hovold5dd8cc52016-01-21 17:34:22 +0100479err_destroy_hid:
Viresh Kumar96eab772015-03-16 16:49:37 +0530480 hid_destroy_device(hid);
Johan Hovold4324282d2016-01-21 17:34:23 +0100481err_connection_destroy:
482 gb_connection_destroy(connection);
Johan Hovold5dd8cc52016-01-21 17:34:22 +0100483err_free_ghid:
Viresh Kumar96eab772015-03-16 16:49:37 +0530484 kfree(ghid);
485
486 return ret;
487}
488
Johan Hovold4324282d2016-01-21 17:34:23 +0100489static void gb_hid_disconnect(struct gb_bundle *bundle)
Viresh Kumar96eab772015-03-16 16:49:37 +0530490{
Johan Hovold4324282d2016-01-21 17:34:23 +0100491 struct gb_hid *ghid = greybus_get_drvdata(bundle);
Viresh Kumar96eab772015-03-16 16:49:37 +0530492
Johan Hovold4324282d2016-01-21 17:34:23 +0100493 gb_connection_disable(ghid->connection);
Viresh Kumar96eab772015-03-16 16:49:37 +0530494 hid_destroy_device(ghid->hid);
Johan Hovold4324282d2016-01-21 17:34:23 +0100495 gb_connection_destroy(ghid->connection);
Viresh Kumar96eab772015-03-16 16:49:37 +0530496 kfree(ghid);
497}
498
Johan Hovold4324282d2016-01-21 17:34:23 +0100499static const struct greybus_bundle_id gb_hid_id_table[] = {
500 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_HID) },
501 { }
Viresh Kumar96eab772015-03-16 16:49:37 +0530502};
Johan Hovold4324282d2016-01-21 17:34:23 +0100503MODULE_DEVICE_TABLE(greybus, gb_hid_id_table);
Viresh Kumar96eab772015-03-16 16:49:37 +0530504
Johan Hovold4324282d2016-01-21 17:34:23 +0100505static struct greybus_driver gb_hid_driver = {
506 .name = "hid",
507 .probe = gb_hid_probe,
508 .disconnect = gb_hid_disconnect,
509 .id_table = gb_hid_id_table,
510};
511module_greybus_driver(gb_hid_driver);
Greg Kroah-Hartmanc9e9de22015-11-04 20:46:14 -0800512
513MODULE_LICENSE("GPL v2");