Root/
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 | |
25 | struct 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 | */ |
53 | enum 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 | */ |
65 | struct synthhid_msg_hdr { |
66 | enum synthhid_msg_type type; |
67 | u32 size; |
68 | }; |
69 | |
70 | struct synthhid_msg { |
71 | struct synthhid_msg_hdr header; |
72 | char data[1]; /* Enclosed message */ |
73 | }; |
74 | |
75 | union synthhid_version { |
76 | struct { |
77 | u16 minor_version; |
78 | u16 major_version; |
79 | }; |
80 | u32 version; |
81 | }; |
82 | |
83 | /* |
84 | * Protocol messages |
85 | */ |
86 | struct synthhid_protocol_request { |
87 | struct synthhid_msg_hdr header; |
88 | union synthhid_version version_requested; |
89 | }; |
90 | |
91 | struct synthhid_protocol_response { |
92 | struct synthhid_msg_hdr header; |
93 | union synthhid_version version_requested; |
94 | unsigned char approved; |
95 | }; |
96 | |
97 | struct 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 | |
103 | struct synthhid_device_info_ack { |
104 | struct synthhid_msg_hdr header; |
105 | unsigned char reserved; |
106 | }; |
107 | |
108 | struct 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 | |
119 | enum pipe_prot_msg_type { |
120 | PIPE_MESSAGE_INVALID, |
121 | PIPE_MESSAGE_DATA, |
122 | PIPE_MESSAGE_MAXIMUM |
123 | }; |
124 | |
125 | |
126 | struct pipe_prt_msg { |
127 | enum pipe_prot_msg_type type; |
128 | u32 size; |
129 | char data[1]; |
130 | }; |
131 | |
132 | struct 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 | */ |
145 | struct 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 | |
163 | static 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 | |
180 | static 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 | |
188 | static 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 | |
248 | cleanup: |
249 | complete(&input_device->wait_event); |
250 | |
251 | return; |
252 | } |
253 | |
254 | static 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 | |
317 | static 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 | |
375 | static 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 | |
429 | cleanup: |
430 | return ret; |
431 | } |
432 | |
433 | static int mousevsc_hid_parse(struct hid_device *hid) |
434 | { |
435 | struct hv_device *dev = hid_get_drvdata(hid); |
436 | struct mousevsc_dev *input_dev = hv_get_drvdata(dev); |
437 | |
438 | return hid_parse_report(hid, input_dev->report_desc, |
439 | input_dev->report_desc_size); |
440 | } |
441 | |
442 | static int mousevsc_hid_open(struct hid_device *hid) |
443 | { |
444 | return 0; |
445 | } |
446 | |
447 | static int mousevsc_hid_start(struct hid_device *hid) |
448 | { |
449 | return 0; |
450 | } |
451 | |
452 | static void mousevsc_hid_close(struct hid_device *hid) |
453 | { |
454 | } |
455 | |
456 | static void mousevsc_hid_stop(struct hid_device *hid) |
457 | { |
458 | } |
459 | |
460 | static struct hid_ll_driver mousevsc_ll_driver = { |
461 | .parse = mousevsc_hid_parse, |
462 | .open = mousevsc_hid_open, |
463 | .close = mousevsc_hid_close, |
464 | .start = mousevsc_hid_start, |
465 | .stop = mousevsc_hid_stop, |
466 | }; |
467 | |
468 | static struct hid_driver mousevsc_hid_driver; |
469 | |
470 | static int mousevsc_probe(struct hv_device *device, |
471 | const struct hv_vmbus_device_id *dev_id) |
472 | { |
473 | int ret; |
474 | struct mousevsc_dev *input_dev; |
475 | struct hid_device *hid_dev; |
476 | |
477 | input_dev = mousevsc_alloc_device(device); |
478 | |
479 | if (!input_dev) |
480 | return -ENOMEM; |
481 | |
482 | ret = vmbus_open(device->channel, |
483 | INPUTVSC_SEND_RING_BUFFER_SIZE, |
484 | INPUTVSC_RECV_RING_BUFFER_SIZE, |
485 | NULL, |
486 | 0, |
487 | mousevsc_on_channel_callback, |
488 | device |
489 | ); |
490 | |
491 | if (ret) |
492 | goto probe_err0; |
493 | |
494 | ret = mousevsc_connect_to_vsp(device); |
495 | |
496 | if (ret) |
497 | goto probe_err1; |
498 | |
499 | /* workaround SA-167 */ |
500 | if (input_dev->report_desc[14] == 0x25) |
501 | input_dev->report_desc[14] = 0x29; |
502 | |
503 | hid_dev = hid_allocate_device(); |
504 | if (IS_ERR(hid_dev)) { |
505 | ret = PTR_ERR(hid_dev); |
506 | goto probe_err1; |
507 | } |
508 | |
509 | hid_dev->ll_driver = &mousevsc_ll_driver; |
510 | hid_dev->driver = &mousevsc_hid_driver; |
511 | hid_dev->bus = BUS_VIRTUAL; |
512 | hid_dev->vendor = input_dev->hid_dev_info.vendor; |
513 | hid_dev->product = input_dev->hid_dev_info.product; |
514 | hid_dev->version = input_dev->hid_dev_info.version; |
515 | input_dev->hid_device = hid_dev; |
516 | |
517 | sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); |
518 | |
519 | hid_set_drvdata(hid_dev, device); |
520 | |
521 | ret = hid_add_device(hid_dev); |
522 | if (ret) |
523 | goto probe_err1; |
524 | |
525 | |
526 | ret = hid_parse(hid_dev); |
527 | if (ret) { |
528 | hid_err(hid_dev, "parse failed\n"); |
529 | goto probe_err2; |
530 | } |
531 | |
532 | ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV); |
533 | |
534 | if (ret) { |
535 | hid_err(hid_dev, "hw start failed\n"); |
536 | goto probe_err2; |
537 | } |
538 | |
539 | input_dev->connected = true; |
540 | input_dev->init_complete = true; |
541 | |
542 | return ret; |
543 | |
544 | probe_err2: |
545 | hid_destroy_device(hid_dev); |
546 | |
547 | probe_err1: |
548 | vmbus_close(device->channel); |
549 | |
550 | probe_err0: |
551 | mousevsc_free_device(input_dev); |
552 | |
553 | return ret; |
554 | } |
555 | |
556 | |
557 | static int mousevsc_remove(struct hv_device *dev) |
558 | { |
559 | struct mousevsc_dev *input_dev = hv_get_drvdata(dev); |
560 | |
561 | vmbus_close(dev->channel); |
562 | hid_hw_stop(input_dev->hid_device); |
563 | hid_destroy_device(input_dev->hid_device); |
564 | mousevsc_free_device(input_dev); |
565 | |
566 | return 0; |
567 | } |
568 | |
569 | static const struct hv_vmbus_device_id id_table[] = { |
570 | /* Mouse guid */ |
571 | { HV_MOUSE_GUID, }, |
572 | { }, |
573 | }; |
574 | |
575 | MODULE_DEVICE_TABLE(vmbus, id_table); |
576 | |
577 | static struct hv_driver mousevsc_drv = { |
578 | .name = KBUILD_MODNAME, |
579 | .id_table = id_table, |
580 | .probe = mousevsc_probe, |
581 | .remove = mousevsc_remove, |
582 | }; |
583 | |
584 | static int __init mousevsc_init(void) |
585 | { |
586 | return vmbus_driver_register(&mousevsc_drv); |
587 | } |
588 | |
589 | static void __exit mousevsc_exit(void) |
590 | { |
591 | vmbus_driver_unregister(&mousevsc_drv); |
592 | } |
593 | |
594 | MODULE_LICENSE("GPL"); |
595 | MODULE_VERSION(HV_DRV_VERSION); |
596 | module_init(mousevsc_init); |
597 | module_exit(mousevsc_exit); |
598 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9