aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-hyperv.c
diff options
context:
space:
mode:
authorK. Y. Srinivasan <kys@microsoft.com>2011-11-17 11:47:29 -0500
committerJiri Kosina <jkosina@suse.cz>2011-11-22 16:48:41 -0500
commitb95f5bcb811e3905b5376f87789da8d097fee682 (patch)
tree03b7082f18cd99db0f2eebb3c1b4ee9e55d9b5d8 /drivers/hid/hid-hyperv.c
parent30307c69d59b14723fbf8a524847b302388c702d (diff)
HID: Move the hid-hyperv driver out of staging
The file hid-hyperv.c implements a hid compliant mouse driver for use on a Hyper-V based system. This driver is currently in the staging area and as part of the effort to move this driver out of staging, I had posted the driver code for community review a few weeks ago. This current patch addresses all the review comments I have gotten to date. All the relevant patches have already been submitted to the staging tree as well. As per Greg's suggestion, this patch does not get rid of the code from the staging area. Once the mouse driver lands under the hid directory, we will cleanup the staging directory. Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-hyperv.c')
-rw-r--r--drivers/hid/hid-hyperv.c582
1 files changed, 582 insertions, 0 deletions
diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c
new file mode 100644
index 000000000000..d503cbbe1e04
--- /dev/null
+++ b/drivers/hid/hid-hyperv.c
@@ -0,0 +1,582 @@
1/*
2 * Copyright (c) 2009, Citrix Systems, Inc.
3 * Copyright (c) 2010, Microsoft Corporation.
4 * Copyright (c) 2011, Novell Inc.
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#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/device.h>
18#include <linux/completion.h>
19#include <linux/input.h>
20#include <linux/hid.h>
21#include <linux/hiddev.h>
22#include <linux/hyperv.h>
23
24
25struct hv_input_dev_info {
26 unsigned int size;
27 unsigned short vendor;
28 unsigned short product;
29 unsigned short version;
30 unsigned short reserved[11];
31};
32
33/* The maximum size of a synthetic input message. */
34#define SYNTHHID_MAX_INPUT_REPORT_SIZE 16
35
36/*
37 * Current version
38 *
39 * History:
40 * Beta, RC < 2008/1/22 1,0
41 * RC > 2008/1/22 2,0
42 */
43#define SYNTHHID_INPUT_VERSION_MAJOR 2
44#define SYNTHHID_INPUT_VERSION_MINOR 0
45#define SYNTHHID_INPUT_VERSION (SYNTHHID_INPUT_VERSION_MINOR | \
46 (SYNTHHID_INPUT_VERSION_MAJOR << 16))
47
48
49#pragma pack(push, 1)
50/*
51 * Message types in the synthetic input protocol
52 */
53enum synthhid_msg_type {
54 SYNTH_HID_PROTOCOL_REQUEST,
55 SYNTH_HID_PROTOCOL_RESPONSE,
56 SYNTH_HID_INITIAL_DEVICE_INFO,
57 SYNTH_HID_INITIAL_DEVICE_INFO_ACK,
58 SYNTH_HID_INPUT_REPORT,
59 SYNTH_HID_MAX
60};
61
62/*
63 * Basic message structures.
64 */
65struct synthhid_msg_hdr {
66 enum synthhid_msg_type type;
67 u32 size;
68};
69
70struct synthhid_msg {
71 struct synthhid_msg_hdr header;
72 char data[1]; /* Enclosed message */
73};
74
75union synthhid_version {
76 struct {
77 u16 minor_version;
78 u16 major_version;
79 };
80 u32 version;
81};
82
83/*
84 * Protocol messages
85 */
86struct synthhid_protocol_request {
87 struct synthhid_msg_hdr header;
88 union synthhid_version version_requested;
89};
90
91struct synthhid_protocol_response {
92 struct synthhid_msg_hdr header;
93 union synthhid_version version_requested;
94 unsigned char approved;
95};
96
97struct synthhid_device_info {
98 struct synthhid_msg_hdr header;
99 struct hv_input_dev_info hid_dev_info;
100 struct hid_descriptor hid_descriptor;
101};
102
103struct synthhid_device_info_ack {
104 struct synthhid_msg_hdr header;
105 unsigned char reserved;
106};
107
108struct synthhid_input_report {
109 struct synthhid_msg_hdr header;
110 char buffer[1];
111};
112
113#pragma pack(pop)
114
115#define INPUTVSC_SEND_RING_BUFFER_SIZE (10*PAGE_SIZE)
116#define INPUTVSC_RECV_RING_BUFFER_SIZE (10*PAGE_SIZE)
117
118
119enum pipe_prot_msg_type {
120 PIPE_MESSAGE_INVALID,
121 PIPE_MESSAGE_DATA,
122 PIPE_MESSAGE_MAXIMUM
123};
124
125
126struct pipe_prt_msg {
127 enum pipe_prot_msg_type type;
128 u32 size;
129 char data[1];
130};
131
132struct mousevsc_prt_msg {
133 enum pipe_prot_msg_type type;
134 u32 size;
135 union {
136 struct synthhid_protocol_request request;
137 struct synthhid_protocol_response response;
138 struct synthhid_device_info_ack ack;
139 };
140};
141
142/*
143 * Represents an mousevsc device
144 */
145struct mousevsc_dev {
146 struct hv_device *device;
147 bool init_complete;
148 bool connected;
149 struct mousevsc_prt_msg protocol_req;
150 struct mousevsc_prt_msg protocol_resp;
151 /* Synchronize the request/response if needed */
152 struct completion wait_event;
153 int dev_info_status;
154
155 struct hid_descriptor *hid_desc;
156 unsigned char *report_desc;
157 u32 report_desc_size;
158 struct hv_input_dev_info hid_dev_info;
159 struct hid_device *hid_device;
160};
161
162
163static struct mousevsc_dev *mousevsc_alloc_device(struct hv_device *device)
164{
165 struct mousevsc_dev *input_dev;
166
167 input_dev = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL);
168
169 if (!input_dev)
170 return NULL;
171
172 input_dev->device = device;
173 hv_set_drvdata(device, input_dev);
174 init_completion(&input_dev->wait_event);
175 input_dev->init_complete = false;
176
177 return input_dev;
178}
179
180static void mousevsc_free_device(struct mousevsc_dev *device)
181{
182 kfree(device->hid_desc);
183 kfree(device->report_desc);
184 hv_set_drvdata(device->device, NULL);
185 kfree(device);
186}
187
188static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device,
189 struct synthhid_device_info *device_info)
190{
191 int ret = 0;
192 struct hid_descriptor *desc;
193 struct mousevsc_prt_msg ack;
194
195 input_device->dev_info_status = -ENOMEM;
196
197 input_device->hid_dev_info = device_info->hid_dev_info;
198 desc = &device_info->hid_descriptor;
199 if (desc->bLength == 0)
200 goto cleanup;
201
202 input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC);
203
204 if (!input_device->hid_desc)
205 goto cleanup;
206
207 memcpy(input_device->hid_desc, desc, desc->bLength);
208
209 input_device->report_desc_size = desc->desc[0].wDescriptorLength;
210 if (input_device->report_desc_size == 0) {
211 input_device->dev_info_status = -EINVAL;
212 goto cleanup;
213 }
214
215 input_device->report_desc = kzalloc(input_device->report_desc_size,
216 GFP_ATOMIC);
217
218 if (!input_device->report_desc) {
219 input_device->dev_info_status = -ENOMEM;
220 goto cleanup;
221 }
222
223 memcpy(input_device->report_desc,
224 ((unsigned char *)desc) + desc->bLength,
225 desc->desc[0].wDescriptorLength);
226
227 /* Send the ack */
228 memset(&ack, 0, sizeof(struct mousevsc_prt_msg));
229
230 ack.type = PIPE_MESSAGE_DATA;
231 ack.size = sizeof(struct synthhid_device_info_ack);
232
233 ack.ack.header.type = SYNTH_HID_INITIAL_DEVICE_INFO_ACK;
234 ack.ack.header.size = 1;
235 ack.ack.reserved = 0;
236
237 ret = vmbus_sendpacket(input_device->device->channel,
238 &ack,
239 sizeof(struct pipe_prt_msg) - sizeof(unsigned char) +
240 sizeof(struct synthhid_device_info_ack),
241 (unsigned long)&ack,
242 VM_PKT_DATA_INBAND,
243 VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
244
245 if (!ret)
246 input_device->dev_info_status = 0;
247
248cleanup:
249 complete(&input_device->wait_event);
250
251 return;
252}
253
254static void mousevsc_on_receive(struct hv_device *device,
255 struct vmpacket_descriptor *packet)
256{
257 struct pipe_prt_msg *pipe_msg;
258 struct synthhid_msg *hid_msg;
259 struct mousevsc_dev *input_dev = hv_get_drvdata(device);
260 struct synthhid_input_report *input_report;
261
262 pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet +
263 (packet->offset8 << 3));
264
265 if (pipe_msg->type != PIPE_MESSAGE_DATA)
266 return;
267
268 hid_msg = (struct synthhid_msg *)pipe_msg->data;
269
270 switch (hid_msg->header.type) {
271 case SYNTH_HID_PROTOCOL_RESPONSE:
272 /*
273 * While it will be impossible for us to protect against
274 * malicious/buggy hypervisor/host, add a check here to
275 * ensure we don't corrupt memory.
276 */
277 if ((pipe_msg->size + sizeof(struct pipe_prt_msg)
278 - sizeof(unsigned char))
279 > sizeof(struct mousevsc_prt_msg)) {
280 WARN_ON(1);
281 break;
282 }
283
284 memcpy(&input_dev->protocol_resp, pipe_msg,
285 pipe_msg->size + sizeof(struct pipe_prt_msg) -
286 sizeof(unsigned char));
287 complete(&input_dev->wait_event);
288 break;
289
290 case SYNTH_HID_INITIAL_DEVICE_INFO:
291 WARN_ON(pipe_msg->size < sizeof(struct hv_input_dev_info));
292
293 /*
294 * Parse out the device info into device attr,
295 * hid desc and report desc
296 */
297 mousevsc_on_receive_device_info(input_dev,
298 (struct synthhid_device_info *)pipe_msg->data);
299 break;
300 case SYNTH_HID_INPUT_REPORT:
301 input_report =
302 (struct synthhid_input_report *)pipe_msg->data;
303 if (!input_dev->init_complete)
304 break;
305 hid_input_report(input_dev->hid_device,
306 HID_INPUT_REPORT, input_report->buffer,
307 input_report->header.size, 1);
308 break;
309 default:
310 pr_err("unsupported hid msg type - type %d len %d",
311 hid_msg->header.type, hid_msg->header.size);
312 break;
313 }
314
315}
316
317static void mousevsc_on_channel_callback(void *context)
318{
319 const int packet_size = 0x100;
320 int ret;
321 struct hv_device *device = context;
322 u32 bytes_recvd;
323 u64 req_id;
324 struct vmpacket_descriptor *desc;
325 unsigned char *buffer;
326 int bufferlen = packet_size;
327
328 buffer = kmalloc(bufferlen, GFP_ATOMIC);
329 if (!buffer)
330 return;
331
332 do {
333 ret = vmbus_recvpacket_raw(device->channel, buffer,
334 bufferlen, &bytes_recvd, &req_id);
335
336 switch (ret) {
337 case 0:
338 if (bytes_recvd <= 0) {
339 kfree(buffer);
340 return;
341 }
342 desc = (struct vmpacket_descriptor *)buffer;
343
344 switch (desc->type) {
345 case VM_PKT_COMP:
346 break;
347
348 case VM_PKT_DATA_INBAND:
349 mousevsc_on_receive(device, desc);
350 break;
351
352 default:
353 pr_err("unhandled packet type %d, tid %llx len %d\n",
354 desc->type, req_id, bytes_recvd);
355 break;
356 }
357
358 break;
359
360 case -ENOBUFS:
361 kfree(buffer);
362 /* Handle large packet */
363 bufferlen = bytes_recvd;
364 buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
365
366 if (!buffer)
367 return;
368
369 break;
370 }
371 } while (1);
372
373}
374
375static int mousevsc_connect_to_vsp(struct hv_device *device)
376{
377 int ret = 0;
378 int t;
379 struct mousevsc_dev *input_dev = hv_get_drvdata(device);
380 struct mousevsc_prt_msg *request;
381 struct mousevsc_prt_msg *response;
382
383 request = &input_dev->protocol_req;
384 memset(request, 0, sizeof(struct mousevsc_prt_msg));
385
386 request->type = PIPE_MESSAGE_DATA;
387 request->size = sizeof(struct synthhid_protocol_request);
388 request->request.header.type = SYNTH_HID_PROTOCOL_REQUEST;
389 request->request.header.size = sizeof(unsigned int);
390 request->request.version_requested.version = SYNTHHID_INPUT_VERSION;
391
392 ret = vmbus_sendpacket(device->channel, request,
393 sizeof(struct pipe_prt_msg) -
394 sizeof(unsigned char) +
395 sizeof(struct synthhid_protocol_request),
396 (unsigned long)request,
397 VM_PKT_DATA_INBAND,
398 VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
399 if (ret)
400 goto cleanup;
401
402 t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ);
403 if (!t) {
404 ret = -ETIMEDOUT;
405 goto cleanup;
406 }
407
408 response = &input_dev->protocol_resp;
409
410 if (!response->response.approved) {
411 pr_err("synthhid protocol request failed (version %d)\n",
412 SYNTHHID_INPUT_VERSION);
413 ret = -ENODEV;
414 goto cleanup;
415 }
416
417 t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ);
418 if (!t) {
419 ret = -ETIMEDOUT;
420 goto cleanup;
421 }
422
423 /*
424 * We should have gotten the device attr, hid desc and report
425 * desc at this point
426 */
427 ret = input_dev->dev_info_status;
428
429cleanup:
430 return ret;
431}
432
433static int mousevsc_hid_open(struct hid_device *hid)
434{
435 return 0;
436}
437
438static int mousevsc_hid_start(struct hid_device *hid)
439{
440 return 0;
441}
442
443static void mousevsc_hid_close(struct hid_device *hid)
444{
445}
446
447static void mousevsc_hid_stop(struct hid_device *hid)
448{
449}
450
451static struct hid_ll_driver mousevsc_ll_driver = {
452 .open = mousevsc_hid_open,
453 .close = mousevsc_hid_close,
454 .start = mousevsc_hid_start,
455 .stop = mousevsc_hid_stop,
456};
457
458static struct hid_driver mousevsc_hid_driver;
459
460static int mousevsc_probe(struct hv_device *device,
461 const struct hv_vmbus_device_id *dev_id)
462{
463 int ret;
464 struct mousevsc_dev *input_dev;
465 struct hid_device *hid_dev;
466
467 input_dev = mousevsc_alloc_device(device);
468
469 if (!input_dev)
470 return -ENOMEM;
471
472 ret = vmbus_open(device->channel,
473 INPUTVSC_SEND_RING_BUFFER_SIZE,
474 INPUTVSC_RECV_RING_BUFFER_SIZE,
475 NULL,
476 0,
477 mousevsc_on_channel_callback,
478 device
479 );
480
481 if (ret)
482 goto probe_err0;
483
484 ret = mousevsc_connect_to_vsp(device);
485
486 if (ret)
487 goto probe_err1;
488
489 /* workaround SA-167 */
490 if (input_dev->report_desc[14] == 0x25)
491 input_dev->report_desc[14] = 0x29;
492
493 hid_dev = hid_allocate_device();
494 if (IS_ERR(hid_dev)) {
495 ret = PTR_ERR(hid_dev);
496 goto probe_err1;
497 }
498
499 hid_dev->ll_driver = &mousevsc_ll_driver;
500 hid_dev->driver = &mousevsc_hid_driver;
501 hid_dev->bus = BUS_VIRTUAL;
502 hid_dev->vendor = input_dev->hid_dev_info.vendor;
503 hid_dev->product = input_dev->hid_dev_info.product;
504 hid_dev->version = input_dev->hid_dev_info.version;
505 input_dev->hid_device = hid_dev;
506
507 sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse");
508
509 ret = hid_parse_report(hid_dev, input_dev->report_desc,
510 input_dev->report_desc_size);
511
512 if (ret) {
513 hid_err(hid_dev, "parse failed\n");
514 goto probe_err2;
515 }
516
517 ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV);
518
519 if (ret) {
520 hid_err(hid_dev, "hw start failed\n");
521 goto probe_err2;
522 }
523
524 input_dev->connected = true;
525 input_dev->init_complete = true;
526
527 return ret;
528
529probe_err2:
530 hid_destroy_device(hid_dev);
531
532probe_err1:
533 vmbus_close(device->channel);
534
535probe_err0:
536 mousevsc_free_device(input_dev);
537
538 return ret;
539}
540
541
542static int mousevsc_remove(struct hv_device *dev)
543{
544 struct mousevsc_dev *input_dev = hv_get_drvdata(dev);
545
546 vmbus_close(dev->channel);
547 hid_destroy_device(input_dev->hid_device);
548 mousevsc_free_device(input_dev);
549
550 return 0;
551}
552
553static const struct hv_vmbus_device_id id_table[] = {
554 /* Mouse guid */
555 { VMBUS_DEVICE(0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
556 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A) },
557 { },
558};
559
560MODULE_DEVICE_TABLE(vmbus, id_table);
561
562static struct hv_driver mousevsc_drv = {
563 .name = KBUILD_MODNAME,
564 .id_table = id_table,
565 .probe = mousevsc_probe,
566 .remove = mousevsc_remove,
567};
568
569static int __init mousevsc_init(void)
570{
571 return vmbus_driver_register(&mousevsc_drv);
572}
573
574static void __exit mousevsc_exit(void)
575{
576 vmbus_driver_unregister(&mousevsc_drv);
577}
578
579MODULE_LICENSE("GPL");
580MODULE_VERSION(HV_DRV_VERSION);
581module_init(mousevsc_init);
582module_exit(mousevsc_exit);