Commit | Line | Data |
---|---|---|
0c3a6ede | 1 | /* |
9b9f93da GKH |
2 | * Copyright (c) 2009, Citrix Systems, Inc. |
3 | * Copyright (c) 2010, Microsoft Corporation. | |
4 | * Copyright (c) 2011, Novell Inc. | |
0c3a6ede | 5 | * |
9b9f93da GKH |
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. | |
0c3a6ede | 9 | * |
9b9f93da GKH |
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. | |
0c3a6ede GKH |
14 | */ |
15 | #include <linux/init.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/device.h> | |
18 | #include <linux/workqueue.h> | |
9dccaa63 GKH |
19 | #include <linux/sched.h> |
20 | #include <linux/wait.h> | |
0c3a6ede GKH |
21 | #include <linux/input.h> |
22 | #include <linux/hid.h> | |
23 | #include <linux/hiddev.h> | |
24 | #include <linux/pci.h> | |
25 | #include <linux/dmi.h> | |
26 | ||
0c3a6ede GKH |
27 | #include "hv_api.h" |
28 | #include "logging.h" | |
29 | #include "version_info.h" | |
30 | #include "vmbus.h" | |
9dccaa63 | 31 | #include "vmbus_api.h" |
0c3a6ede | 32 | #include "mousevsc_api.h" |
9dccaa63 GKH |
33 | #include "channel.h" |
34 | #include "vmbus_packet_format.h" | |
fa003500 GKH |
35 | |
36 | ||
37 | /* The maximum size of a synthetic input message. */ | |
38 | #define SYNTHHID_MAX_INPUT_REPORT_SIZE 16 | |
39 | ||
40 | /* | |
41 | * Current version | |
42 | * | |
43 | * History: | |
44 | * Beta, RC < 2008/1/22 1,0 | |
45 | * RC > 2008/1/22 2,0 | |
46 | */ | |
47 | #define SYNTHHID_INPUT_VERSION_MAJOR 2 | |
48 | #define SYNTHHID_INPUT_VERSION_MINOR 0 | |
49 | #define SYNTHHID_INPUT_VERSION_DWORD (SYNTHHID_INPUT_VERSION_MINOR | \ | |
50 | (SYNTHHID_INPUT_VERSION_MAJOR << 16)) | |
51 | ||
52 | ||
53 | #pragma pack(push,1) | |
54 | /* | |
55 | * Message types in the synthetic input protocol | |
56 | */ | |
57 | enum synthhid_msg_type { | |
58 | SynthHidProtocolRequest, | |
59 | SynthHidProtocolResponse, | |
60 | SynthHidInitialDeviceInfo, | |
61 | SynthHidInitialDeviceInfoAck, | |
62 | SynthHidInputReport, | |
63 | SynthHidMax | |
64 | }; | |
65 | ||
66 | /* | |
67 | * Basic message structures. | |
68 | */ | |
69 | typedef struct { | |
70 | enum synthhid_msg_type Type; /* Type of the enclosed message */ | |
71 | u32 Size; /* Size of the enclosed message | |
72 | * (size of the data payload) | |
73 | */ | |
74 | } SYNTHHID_MESSAGE_HEADER, *PSYNTHHID_MESSAGE_HEADER; | |
75 | ||
76 | typedef struct { | |
77 | SYNTHHID_MESSAGE_HEADER Header; | |
78 | char Data[1]; /* Enclosed message */ | |
79 | } SYNTHHID_MESSAGE, *PSYNTHHID_MESSAGE; | |
80 | ||
81 | typedef union { | |
82 | struct { | |
83 | u16 Minor; | |
84 | u16 Major; | |
85 | }; | |
86 | ||
87 | u32 AsDWord; | |
88 | } SYNTHHID_VERSION, *PSYNTHHID_VERSION; | |
89 | ||
90 | /* | |
91 | * Protocol messages | |
92 | */ | |
93 | typedef struct { | |
94 | SYNTHHID_MESSAGE_HEADER Header; | |
95 | SYNTHHID_VERSION VersionRequested; | |
96 | } SYNTHHID_PROTOCOL_REQUEST, *PSYNTHHID_PROTOCOL_REQUEST; | |
97 | ||
98 | typedef struct { | |
99 | SYNTHHID_MESSAGE_HEADER Header; | |
100 | SYNTHHID_VERSION VersionRequested; | |
101 | unsigned char Approved; | |
102 | } SYNTHHID_PROTOCOL_RESPONSE, *PSYNTHHID_PROTOCOL_RESPONSE; | |
103 | ||
104 | typedef struct { | |
105 | SYNTHHID_MESSAGE_HEADER Header; | |
106 | struct input_dev_info HidDeviceAttributes; | |
107 | unsigned char HidDescriptorInformation[1]; | |
108 | } SYNTHHID_DEVICE_INFO, *PSYNTHHID_DEVICE_INFO; | |
109 | ||
110 | typedef struct { | |
111 | SYNTHHID_MESSAGE_HEADER Header; | |
112 | unsigned char Reserved; | |
113 | } SYNTHHID_DEVICE_INFO_ACK, *PSYNTHHID_DEVICE_INFO_ACK; | |
114 | ||
115 | typedef struct { | |
116 | SYNTHHID_MESSAGE_HEADER Header; | |
117 | char ReportBuffer[1]; | |
118 | } SYNTHHID_INPUT_REPORT, *PSYNTHHID_INPUT_REPORT; | |
119 | ||
120 | #pragma pack(pop) | |
121 | ||
0c3a6ede GKH |
122 | |
123 | #define NBITS(x) (((x)/BITS_PER_LONG)+1) | |
124 | ||
9dccaa63 GKH |
125 | enum pipe_prot_msg_type { |
126 | PipeMessageInvalid = 0, | |
127 | PipeMessageData, | |
128 | PipeMessageMaximum | |
129 | }; | |
130 | ||
131 | ||
132 | struct pipe_prt_msg { | |
133 | enum pipe_prot_msg_type PacketType; | |
134 | u32 DataSize; | |
135 | char Data[1]; | |
136 | }; | |
137 | ||
138 | /* | |
139 | * Data types | |
140 | */ | |
141 | struct mousevsc_prt_msg { | |
142 | enum pipe_prot_msg_type PacketType; | |
143 | u32 DataSize; | |
144 | union { | |
145 | SYNTHHID_PROTOCOL_REQUEST Request; | |
146 | SYNTHHID_PROTOCOL_RESPONSE Response; | |
147 | SYNTHHID_DEVICE_INFO_ACK Ack; | |
148 | } u; | |
149 | }; | |
150 | ||
151 | /* | |
152 | * Represents an mousevsc device | |
153 | */ | |
154 | struct mousevsc_dev { | |
155 | struct hv_device *Device; | |
156 | /* 0 indicates the device is being destroyed */ | |
157 | atomic_t RefCount; | |
158 | int NumOutstandingRequests; | |
159 | unsigned char bInitializeComplete; | |
160 | struct mousevsc_prt_msg ProtocolReq; | |
161 | struct mousevsc_prt_msg ProtocolResp; | |
162 | /* Synchronize the request/response if needed */ | |
163 | wait_queue_head_t ProtocolWaitEvent; | |
164 | wait_queue_head_t DeviceInfoWaitEvent; | |
165 | int protocol_wait_condition; | |
166 | int device_wait_condition; | |
167 | int DeviceInfoStatus; | |
168 | ||
169 | struct hid_descriptor *HidDesc; | |
170 | unsigned char *ReportDesc; | |
171 | u32 ReportDescSize; | |
172 | struct input_dev_info DeviceAttr; | |
173 | }; | |
174 | ||
175 | ||
176 | /* | |
177 | * Globals | |
178 | */ | |
179 | static const char *gDriverName = "mousevsc"; | |
180 | ||
181 | /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */ | |
182 | static const struct hv_guid gMousevscDeviceType = { | |
183 | .data = {0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, | |
184 | 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A} | |
185 | }; | |
186 | ||
187 | /* | |
188 | * Internal routines | |
189 | */ | |
190 | static int MousevscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo); | |
191 | ||
192 | static int MousevscOnDeviceRemove(struct hv_device *Device); | |
193 | ||
194 | static void MousevscOnCleanup(struct hv_driver *Device); | |
195 | ||
196 | static void MousevscOnChannelCallback(void *Context); | |
197 | ||
198 | static int MousevscConnectToVsp(struct hv_device *Device); | |
199 | ||
200 | static void MousevscOnReceive(struct hv_device *Device, | |
201 | struct vmpacket_descriptor *Packet); | |
202 | ||
203 | static inline struct mousevsc_dev *AllocInputDevice(struct hv_device *Device) | |
204 | { | |
205 | struct mousevsc_dev *inputDevice; | |
206 | ||
207 | inputDevice = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL); | |
208 | ||
209 | if (!inputDevice) | |
210 | return NULL; | |
211 | ||
212 | /* | |
213 | * Set to 2 to allow both inbound and outbound traffics | |
214 | * (ie GetInputDevice() and MustGetInputDevice()) to proceed. | |
215 | */ | |
216 | atomic_cmpxchg(&inputDevice->RefCount, 0, 2); | |
217 | ||
218 | inputDevice->Device = Device; | |
219 | Device->ext = inputDevice; | |
220 | ||
221 | return inputDevice; | |
222 | } | |
223 | ||
224 | static inline void FreeInputDevice(struct mousevsc_dev *Device) | |
225 | { | |
226 | WARN_ON(atomic_read(&Device->RefCount) == 0); | |
227 | kfree(Device); | |
228 | } | |
229 | ||
230 | /* | |
231 | * Get the inputdevice object if exists and its refcount > 1 | |
232 | */ | |
233 | static inline struct mousevsc_dev *GetInputDevice(struct hv_device *Device) | |
234 | { | |
235 | struct mousevsc_dev *inputDevice; | |
236 | ||
237 | inputDevice = (struct mousevsc_dev *)Device->ext; | |
238 | ||
239 | /* | |
240 | * FIXME | |
241 | * This sure isn't a valid thing to print for debugging, no matter | |
242 | * what the intention is... | |
243 | * | |
244 | * printk(KERN_ERR "-------------------------> REFCOUNT = %d", | |
245 | * inputDevice->RefCount); | |
246 | */ | |
247 | ||
248 | if (inputDevice && atomic_read(&inputDevice->RefCount) > 1) | |
249 | atomic_inc(&inputDevice->RefCount); | |
250 | else | |
251 | inputDevice = NULL; | |
252 | ||
253 | return inputDevice; | |
254 | } | |
255 | ||
256 | /* | |
257 | * Get the inputdevice object iff exists and its refcount > 0 | |
258 | */ | |
259 | static inline struct mousevsc_dev *MustGetInputDevice(struct hv_device *Device) | |
260 | { | |
261 | struct mousevsc_dev *inputDevice; | |
262 | ||
263 | inputDevice = (struct mousevsc_dev *)Device->ext; | |
264 | ||
265 | if (inputDevice && atomic_read(&inputDevice->RefCount)) | |
266 | atomic_inc(&inputDevice->RefCount); | |
267 | else | |
268 | inputDevice = NULL; | |
269 | ||
270 | return inputDevice; | |
271 | } | |
272 | ||
273 | static inline void PutInputDevice(struct hv_device *Device) | |
274 | { | |
275 | struct mousevsc_dev *inputDevice; | |
276 | ||
277 | inputDevice = (struct mousevsc_dev *)Device->ext; | |
278 | ||
279 | atomic_dec(&inputDevice->RefCount); | |
280 | } | |
281 | ||
282 | /* | |
283 | * Drop ref count to 1 to effectively disable GetInputDevice() | |
284 | */ | |
285 | static inline struct mousevsc_dev *ReleaseInputDevice(struct hv_device *Device) | |
286 | { | |
287 | struct mousevsc_dev *inputDevice; | |
288 | ||
289 | inputDevice = (struct mousevsc_dev *)Device->ext; | |
290 | ||
291 | /* Busy wait until the ref drop to 2, then set it to 1 */ | |
292 | while (atomic_cmpxchg(&inputDevice->RefCount, 2, 1) != 2) | |
293 | udelay(100); | |
294 | ||
295 | return inputDevice; | |
296 | } | |
297 | ||
298 | /* | |
299 | * Drop ref count to 0. No one can use InputDevice object. | |
300 | */ | |
301 | static inline struct mousevsc_dev *FinalReleaseInputDevice(struct hv_device *Device) | |
302 | { | |
303 | struct mousevsc_dev *inputDevice; | |
304 | ||
305 | inputDevice = (struct mousevsc_dev *)Device->ext; | |
306 | ||
307 | /* Busy wait until the ref drop to 1, then set it to 0 */ | |
308 | while (atomic_cmpxchg(&inputDevice->RefCount, 1, 0) != 1) | |
309 | udelay(100); | |
310 | ||
311 | Device->ext = NULL; | |
312 | return inputDevice; | |
313 | } | |
314 | ||
315 | /* | |
316 | * | |
317 | * Name: | |
318 | * MousevscInitialize() | |
319 | * | |
320 | * Description: | |
321 | * Main entry point | |
322 | * | |
323 | */ | |
324 | int mouse_vsc_initialize(struct hv_driver *Driver) | |
325 | { | |
326 | struct mousevsc_drv_obj *inputDriver = | |
327 | (struct mousevsc_drv_obj *)Driver; | |
328 | int ret = 0; | |
329 | ||
330 | Driver->name = gDriverName; | |
331 | memcpy(&Driver->dev_type, &gMousevscDeviceType, | |
332 | sizeof(struct hv_guid)); | |
333 | ||
334 | /* Setup the dispatch table */ | |
335 | inputDriver->Base.dev_add = MousevscOnDeviceAdd; | |
336 | inputDriver->Base.dev_rm = MousevscOnDeviceRemove; | |
337 | inputDriver->Base.cleanup = MousevscOnCleanup; | |
338 | ||
339 | inputDriver->OnOpen = NULL; | |
340 | inputDriver->OnClose = NULL; | |
341 | ||
342 | return ret; | |
343 | } | |
344 | ||
345 | /* | |
346 | * | |
347 | * Name: | |
348 | * MousevscOnDeviceAdd() | |
349 | * | |
350 | * Description: | |
351 | * Callback when the device belonging to this driver is added | |
352 | * | |
353 | */ | |
354 | int | |
355 | MousevscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo) | |
356 | { | |
357 | int ret = 0; | |
358 | struct mousevsc_dev *inputDevice; | |
359 | struct mousevsc_drv_obj *inputDriver; | |
360 | struct input_dev_info deviceInfo; | |
361 | ||
362 | inputDevice = AllocInputDevice(Device); | |
363 | ||
364 | if (!inputDevice) { | |
365 | ret = -1; | |
366 | goto Cleanup; | |
367 | } | |
368 | ||
369 | inputDevice->bInitializeComplete = false; | |
370 | ||
371 | /* Open the channel */ | |
372 | ret = vmbus_open(Device->channel, | |
373 | INPUTVSC_SEND_RING_BUFFER_SIZE, | |
374 | INPUTVSC_RECV_RING_BUFFER_SIZE, | |
375 | NULL, | |
376 | 0, | |
377 | MousevscOnChannelCallback, | |
378 | Device | |
379 | ); | |
380 | ||
381 | if (ret != 0) { | |
382 | pr_err("unable to open channel: %d", ret); | |
383 | return -1; | |
384 | } | |
385 | ||
386 | pr_info("InputVsc channel open: %d", ret); | |
387 | ||
388 | ret = MousevscConnectToVsp(Device); | |
389 | ||
390 | if (ret != 0) { | |
391 | pr_err("unable to connect channel: %d", ret); | |
392 | ||
393 | vmbus_close(Device->channel); | |
394 | return ret; | |
395 | } | |
396 | ||
397 | inputDriver = (struct mousevsc_drv_obj *)inputDevice->Device->drv; | |
398 | ||
399 | deviceInfo.VendorID = inputDevice->DeviceAttr.VendorID; | |
400 | deviceInfo.ProductID = inputDevice->DeviceAttr.ProductID; | |
401 | deviceInfo.VersionNumber = inputDevice->DeviceAttr.VersionNumber; | |
402 | strcpy(deviceInfo.Name, "Microsoft Vmbus HID-compliant Mouse"); | |
403 | ||
404 | /* Send the device info back up */ | |
405 | inputDriver->OnDeviceInfo(Device, &deviceInfo); | |
406 | ||
407 | /* Send the report desc back up */ | |
408 | /* workaround SA-167 */ | |
409 | if (inputDevice->ReportDesc[14] == 0x25) | |
410 | inputDevice->ReportDesc[14] = 0x29; | |
411 | ||
412 | inputDriver->OnReportDescriptor(Device, inputDevice->ReportDesc, inputDevice->ReportDescSize); | |
413 | ||
414 | inputDevice->bInitializeComplete = true; | |
415 | ||
416 | Cleanup: | |
417 | return ret; | |
418 | } | |
419 | ||
420 | int | |
421 | MousevscConnectToVsp(struct hv_device *Device) | |
422 | { | |
423 | int ret = 0; | |
424 | struct mousevsc_dev *inputDevice; | |
425 | struct mousevsc_prt_msg *request; | |
426 | struct mousevsc_prt_msg *response; | |
427 | ||
428 | inputDevice = GetInputDevice(Device); | |
429 | ||
430 | if (!inputDevice) { | |
431 | pr_err("unable to get input device...device being destroyed?"); | |
432 | return -1; | |
433 | } | |
434 | ||
435 | init_waitqueue_head(&inputDevice->ProtocolWaitEvent); | |
436 | init_waitqueue_head(&inputDevice->DeviceInfoWaitEvent); | |
437 | ||
438 | request = &inputDevice->ProtocolReq; | |
439 | ||
440 | /* | |
441 | * Now, initiate the vsc/vsp initialization protocol on the open channel | |
442 | */ | |
443 | memset(request, sizeof(struct mousevsc_prt_msg), 0); | |
444 | ||
445 | request->PacketType = PipeMessageData; | |
446 | request->DataSize = sizeof(SYNTHHID_PROTOCOL_REQUEST); | |
447 | ||
448 | request->u.Request.Header.Type = SynthHidProtocolRequest; | |
449 | request->u.Request.Header.Size = sizeof(unsigned long); | |
450 | request->u.Request.VersionRequested.AsDWord = | |
451 | SYNTHHID_INPUT_VERSION_DWORD; | |
452 | ||
453 | pr_info("SYNTHHID_PROTOCOL_REQUEST..."); | |
454 | ||
455 | ret = vmbus_sendpacket(Device->channel, request, | |
456 | sizeof(struct pipe_prt_msg) - | |
457 | sizeof(unsigned char) + | |
458 | sizeof(SYNTHHID_PROTOCOL_REQUEST), | |
459 | (unsigned long)request, | |
460 | VM_PKT_DATA_INBAND, | |
461 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | |
462 | if (ret != 0) { | |
463 | pr_err("unable to send SYNTHHID_PROTOCOL_REQUEST"); | |
464 | goto Cleanup; | |
465 | } | |
466 | ||
467 | inputDevice->protocol_wait_condition = 0; | |
468 | wait_event_timeout(inputDevice->ProtocolWaitEvent, inputDevice->protocol_wait_condition, msecs_to_jiffies(1000)); | |
469 | if (inputDevice->protocol_wait_condition == 0) { | |
470 | ret = -ETIMEDOUT; | |
471 | goto Cleanup; | |
472 | } | |
473 | ||
474 | response = &inputDevice->ProtocolResp; | |
475 | ||
476 | if (!response->u.Response.Approved) { | |
477 | pr_err("SYNTHHID_PROTOCOL_REQUEST failed (version %d)", | |
478 | SYNTHHID_INPUT_VERSION_DWORD); | |
479 | ret = -1; | |
480 | goto Cleanup; | |
481 | } | |
482 | ||
483 | inputDevice->device_wait_condition = 0; | |
484 | wait_event_timeout(inputDevice->DeviceInfoWaitEvent, inputDevice->device_wait_condition, msecs_to_jiffies(1000)); | |
485 | if (inputDevice->device_wait_condition == 0) { | |
486 | ret = -ETIMEDOUT; | |
487 | goto Cleanup; | |
488 | } | |
489 | ||
490 | /* | |
491 | * We should have gotten the device attr, hid desc and report | |
492 | * desc at this point | |
493 | */ | |
494 | if (!inputDevice->DeviceInfoStatus) | |
495 | pr_info("**** input channel up and running!! ****"); | |
496 | else | |
497 | ret = -1; | |
498 | ||
499 | Cleanup: | |
500 | PutInputDevice(Device); | |
501 | ||
502 | return ret; | |
503 | } | |
504 | ||
505 | ||
506 | /* | |
507 | * | |
508 | * Name: | |
509 | * MousevscOnDeviceRemove() | |
510 | * | |
511 | * Description: | |
512 | * Callback when the our device is being removed | |
513 | * | |
514 | */ | |
515 | int | |
516 | MousevscOnDeviceRemove(struct hv_device *Device) | |
517 | { | |
518 | struct mousevsc_dev *inputDevice; | |
519 | int ret = 0; | |
520 | ||
521 | pr_info("disabling input device (%p)...", | |
522 | Device->ext); | |
523 | ||
524 | inputDevice = ReleaseInputDevice(Device); | |
525 | ||
526 | ||
527 | /* | |
528 | * At this point, all outbound traffic should be disable. We only | |
529 | * allow inbound traffic (responses) to proceed | |
530 | * | |
531 | * so that outstanding requests can be completed. | |
532 | */ | |
533 | while (inputDevice->NumOutstandingRequests) { | |
534 | pr_info("waiting for %d requests to complete...", inputDevice->NumOutstandingRequests); | |
535 | ||
536 | udelay(100); | |
537 | } | |
538 | ||
539 | pr_info("removing input device (%p)...", Device->ext); | |
540 | ||
541 | inputDevice = FinalReleaseInputDevice(Device); | |
542 | ||
543 | pr_info("input device (%p) safe to remove", inputDevice); | |
544 | ||
545 | /* Close the channel */ | |
546 | vmbus_close(Device->channel); | |
547 | ||
548 | FreeInputDevice(inputDevice); | |
549 | ||
550 | return ret; | |
551 | } | |
552 | ||
553 | ||
554 | /* | |
555 | * | |
556 | * Name: | |
557 | * MousevscOnCleanup() | |
558 | * | |
559 | * Description: | |
560 | * Perform any cleanup when the driver is removed | |
561 | */ | |
562 | static void MousevscOnCleanup(struct hv_driver *drv) | |
563 | { | |
564 | } | |
565 | ||
566 | ||
567 | static void | |
568 | MousevscOnSendCompletion(struct hv_device *Device, | |
569 | struct vmpacket_descriptor *Packet) | |
570 | { | |
571 | struct mousevsc_dev *inputDevice; | |
572 | void *request; | |
573 | ||
574 | inputDevice = MustGetInputDevice(Device); | |
575 | if (!inputDevice) { | |
576 | pr_err("unable to get input device...device being destroyed?"); | |
577 | return; | |
578 | } | |
579 | ||
580 | request = (void *)(unsigned long *)Packet->trans_id; | |
581 | ||
582 | if (request == &inputDevice->ProtocolReq) { | |
583 | /* FIXME */ | |
584 | /* Shouldn't we be doing something here? */ | |
585 | } | |
586 | ||
587 | PutInputDevice(Device); | |
588 | } | |
589 | ||
590 | void | |
591 | MousevscOnReceiveDeviceInfo( | |
592 | struct mousevsc_dev *InputDevice, | |
593 | SYNTHHID_DEVICE_INFO *DeviceInfo) | |
594 | { | |
595 | int ret = 0; | |
596 | struct hid_descriptor *desc; | |
597 | struct mousevsc_prt_msg ack; | |
598 | ||
599 | /* Assume success for now */ | |
600 | InputDevice->DeviceInfoStatus = 0; | |
601 | ||
602 | /* Save the device attr */ | |
603 | memcpy(&InputDevice->DeviceAttr, &DeviceInfo->HidDeviceAttributes, sizeof(struct input_dev_info)); | |
604 | ||
605 | /* Save the hid desc */ | |
606 | desc = (struct hid_descriptor *)DeviceInfo->HidDescriptorInformation; | |
607 | WARN_ON(desc->bLength > 0); | |
608 | ||
609 | InputDevice->HidDesc = kzalloc(desc->bLength, GFP_KERNEL); | |
610 | ||
611 | if (!InputDevice->HidDesc) { | |
612 | pr_err("unable to allocate hid descriptor - size %d", desc->bLength); | |
613 | goto Cleanup; | |
614 | } | |
615 | ||
616 | memcpy(InputDevice->HidDesc, desc, desc->bLength); | |
617 | ||
618 | /* Save the report desc */ | |
619 | InputDevice->ReportDescSize = desc->desc[0].wDescriptorLength; | |
620 | InputDevice->ReportDesc = kzalloc(InputDevice->ReportDescSize, | |
621 | GFP_KERNEL); | |
622 | ||
623 | if (!InputDevice->ReportDesc) { | |
624 | pr_err("unable to allocate report descriptor - size %d", | |
625 | InputDevice->ReportDescSize); | |
626 | goto Cleanup; | |
627 | } | |
628 | ||
629 | memcpy(InputDevice->ReportDesc, | |
630 | ((unsigned char *)desc) + desc->bLength, | |
631 | desc->desc[0].wDescriptorLength); | |
632 | ||
633 | /* Send the ack */ | |
634 | memset(&ack, sizeof(struct mousevsc_prt_msg), 0); | |
635 | ||
636 | ack.PacketType = PipeMessageData; | |
637 | ack.DataSize = sizeof(SYNTHHID_DEVICE_INFO_ACK); | |
638 | ||
639 | ack.u.Ack.Header.Type = SynthHidInitialDeviceInfoAck; | |
640 | ack.u.Ack.Header.Size = 1; | |
641 | ack.u.Ack.Reserved = 0; | |
642 | ||
643 | ret = vmbus_sendpacket(InputDevice->Device->channel, | |
644 | &ack, | |
645 | sizeof(struct pipe_prt_msg) - sizeof(unsigned char) + sizeof(SYNTHHID_DEVICE_INFO_ACK), | |
646 | (unsigned long)&ack, | |
647 | VM_PKT_DATA_INBAND, | |
648 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | |
649 | if (ret != 0) { | |
650 | pr_err("unable to send SYNTHHID_DEVICE_INFO_ACK - ret %d", | |
651 | ret); | |
652 | goto Cleanup; | |
653 | } | |
654 | ||
655 | InputDevice->device_wait_condition = 1; | |
656 | wake_up(&InputDevice->DeviceInfoWaitEvent); | |
657 | ||
658 | return; | |
659 | ||
660 | Cleanup: | |
661 | if (InputDevice->HidDesc) { | |
662 | kfree(InputDevice->HidDesc); | |
663 | InputDevice->HidDesc = NULL; | |
664 | } | |
665 | ||
666 | if (InputDevice->ReportDesc) { | |
667 | kfree(InputDevice->ReportDesc); | |
668 | InputDevice->ReportDesc = NULL; | |
669 | } | |
670 | ||
671 | InputDevice->DeviceInfoStatus = -1; | |
672 | InputDevice->device_wait_condition = 1; | |
673 | wake_up(&InputDevice->DeviceInfoWaitEvent); | |
674 | } | |
675 | ||
676 | ||
677 | void | |
678 | MousevscOnReceiveInputReport( | |
679 | struct mousevsc_dev *InputDevice, | |
680 | SYNTHHID_INPUT_REPORT *InputReport) | |
681 | { | |
682 | struct mousevsc_drv_obj *inputDriver; | |
683 | ||
684 | if (!InputDevice->bInitializeComplete) { | |
685 | pr_info("Initialization incomplete...ignoring InputReport msg"); | |
686 | return; | |
687 | } | |
688 | ||
689 | inputDriver = (struct mousevsc_drv_obj *)InputDevice->Device->drv; | |
690 | ||
691 | inputDriver->OnInputReport(InputDevice->Device, | |
692 | InputReport->ReportBuffer, | |
693 | InputReport->Header.Size); | |
694 | } | |
695 | ||
696 | void | |
697 | MousevscOnReceive(struct hv_device *Device, struct vmpacket_descriptor *Packet) | |
698 | { | |
699 | struct pipe_prt_msg *pipeMsg; | |
700 | SYNTHHID_MESSAGE *hidMsg; | |
701 | struct mousevsc_dev *inputDevice; | |
702 | ||
703 | inputDevice = MustGetInputDevice(Device); | |
704 | if (!inputDevice) { | |
705 | pr_err("unable to get input device...device being destroyed?"); | |
706 | return; | |
707 | } | |
708 | ||
709 | pipeMsg = (struct pipe_prt_msg *)((unsigned long)Packet + (Packet->offset8 << 3)); | |
710 | ||
711 | if (pipeMsg->PacketType != PipeMessageData) { | |
712 | pr_err("unknown pipe msg type - type %d len %d", | |
713 | pipeMsg->PacketType, pipeMsg->DataSize); | |
714 | PutInputDevice(Device); | |
715 | return ; | |
716 | } | |
717 | ||
718 | hidMsg = (SYNTHHID_MESSAGE *)&pipeMsg->Data[0]; | |
719 | ||
720 | switch (hidMsg->Header.Type) { | |
721 | case SynthHidProtocolResponse: | |
722 | memcpy(&inputDevice->ProtocolResp, pipeMsg, pipeMsg->DataSize+sizeof(struct pipe_prt_msg) - sizeof(unsigned char)); | |
723 | inputDevice->protocol_wait_condition = 1; | |
724 | wake_up(&inputDevice->ProtocolWaitEvent); | |
725 | break; | |
726 | ||
727 | case SynthHidInitialDeviceInfo: | |
728 | WARN_ON(pipeMsg->DataSize >= sizeof(struct input_dev_info)); | |
729 | ||
730 | /* | |
731 | * Parse out the device info into device attr, | |
732 | * hid desc and report desc | |
733 | */ | |
734 | MousevscOnReceiveDeviceInfo(inputDevice, | |
735 | (SYNTHHID_DEVICE_INFO *)&pipeMsg->Data[0]); | |
736 | break; | |
737 | case SynthHidInputReport: | |
738 | MousevscOnReceiveInputReport(inputDevice, | |
739 | (SYNTHHID_INPUT_REPORT *)&pipeMsg->Data[0]); | |
740 | ||
741 | break; | |
742 | default: | |
743 | pr_err("unsupported hid msg type - type %d len %d", | |
744 | hidMsg->Header.Type, hidMsg->Header.Size); | |
745 | break; | |
746 | } | |
747 | ||
748 | PutInputDevice(Device); | |
749 | } | |
750 | ||
751 | void MousevscOnChannelCallback(void *Context) | |
752 | { | |
753 | const int packetSize = 0x100; | |
754 | int ret = 0; | |
755 | struct hv_device *device = (struct hv_device *)Context; | |
756 | struct mousevsc_dev *inputDevice; | |
757 | ||
758 | u32 bytesRecvd; | |
759 | u64 requestId; | |
760 | unsigned char packet[packetSize]; | |
761 | struct vmpacket_descriptor *desc; | |
762 | unsigned char *buffer = packet; | |
763 | int bufferlen = packetSize; | |
764 | ||
765 | inputDevice = MustGetInputDevice(device); | |
766 | ||
767 | if (!inputDevice) { | |
768 | pr_err("unable to get input device...device being destroyed?"); | |
769 | return; | |
770 | } | |
771 | ||
772 | do { | |
773 | ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen, &bytesRecvd, &requestId); | |
774 | ||
775 | if (ret == 0) { | |
776 | if (bytesRecvd > 0) { | |
777 | desc = (struct vmpacket_descriptor *)buffer; | |
778 | ||
779 | switch (desc->type) { | |
780 | case VM_PKT_COMP: | |
781 | MousevscOnSendCompletion(device, | |
782 | desc); | |
783 | break; | |
784 | ||
785 | case VM_PKT_DATA_INBAND: | |
786 | MousevscOnReceive(device, desc); | |
787 | break; | |
788 | ||
789 | default: | |
790 | pr_err("unhandled packet type %d, tid %llx len %d\n", | |
791 | desc->type, | |
792 | requestId, | |
793 | bytesRecvd); | |
794 | break; | |
795 | } | |
796 | ||
797 | /* reset */ | |
798 | if (bufferlen > packetSize) { | |
799 | kfree(buffer); | |
800 | ||
801 | buffer = packet; | |
802 | bufferlen = packetSize; | |
803 | } | |
804 | } else { | |
805 | /* | |
806 | * pr_debug("nothing else to read..."); | |
807 | * reset | |
808 | */ | |
809 | if (bufferlen > packetSize) { | |
810 | kfree(buffer); | |
811 | ||
812 | buffer = packet; | |
813 | bufferlen = packetSize; | |
814 | } | |
815 | break; | |
816 | } | |
817 | } else if (ret == -2) { | |
818 | /* Handle large packet */ | |
819 | bufferlen = bytesRecvd; | |
820 | buffer = kzalloc(bytesRecvd, GFP_KERNEL); | |
821 | ||
822 | if (buffer == NULL) { | |
823 | buffer = packet; | |
824 | bufferlen = packetSize; | |
825 | ||
826 | /* Try again next time around */ | |
827 | pr_err("unable to allocate buffer of size %d!", | |
828 | bytesRecvd); | |
829 | break; | |
830 | } | |
831 | } | |
832 | } while (1); | |
833 | ||
834 | PutInputDevice(device); | |
835 | ||
836 | return; | |
837 | } | |
0c3a6ede GKH |
838 | |
839 | /* | |
840 | * Data types | |
841 | */ | |
842 | struct input_device_context { | |
843 | struct vm_device *device_ctx; | |
844 | struct hid_device *hid_device; | |
845 | struct input_dev_info device_info; | |
846 | int connected; | |
847 | }; | |
848 | ||
849 | struct mousevsc_driver_context { | |
850 | struct driver_context drv_ctx; | |
851 | struct mousevsc_drv_obj drv_obj; | |
852 | }; | |
853 | ||
854 | static struct mousevsc_driver_context g_mousevsc_drv; | |
855 | ||
856 | void mousevsc_deviceinfo_callback(struct hv_device *dev, | |
857 | struct input_dev_info *info) | |
858 | { | |
859 | struct vm_device *device_ctx = to_vm_device(dev); | |
860 | struct input_device_context *input_device_ctx = | |
861 | dev_get_drvdata(&device_ctx->device); | |
862 | ||
863 | memcpy(&input_device_ctx->device_info, info, | |
864 | sizeof(struct input_dev_info)); | |
865 | ||
866 | DPRINT_INFO(INPUTVSC_DRV, "mousevsc_deviceinfo_callback()"); | |
867 | } | |
868 | ||
869 | void mousevsc_inputreport_callback(struct hv_device *dev, void *packet, u32 len) | |
870 | { | |
871 | int ret = 0; | |
872 | ||
873 | struct vm_device *device_ctx = to_vm_device(dev); | |
874 | struct input_device_context *input_dev_ctx = | |
875 | dev_get_drvdata(&device_ctx->device); | |
876 | ||
877 | ret = hid_input_report(input_dev_ctx->hid_device, | |
878 | HID_INPUT_REPORT, packet, len, 1); | |
879 | ||
880 | DPRINT_DBG(INPUTVSC_DRV, "hid_input_report (ret %d)", ret); | |
881 | } | |
882 | ||
883 | int mousevsc_hid_open(struct hid_device *hid) | |
884 | { | |
885 | return 0; | |
886 | } | |
887 | ||
888 | void mousevsc_hid_close(struct hid_device *hid) | |
889 | { | |
890 | } | |
891 | ||
892 | int mousevsc_probe(struct device *device) | |
893 | { | |
894 | int ret = 0; | |
895 | ||
896 | struct driver_context *driver_ctx = | |
897 | driver_to_driver_context(device->driver); | |
898 | struct mousevsc_driver_context *mousevsc_drv_ctx = | |
899 | (struct mousevsc_driver_context *)driver_ctx; | |
900 | struct mousevsc_drv_obj *mousevsc_drv_obj = &mousevsc_drv_ctx->drv_obj; | |
901 | ||
902 | struct vm_device *device_ctx = device_to_vm_device(device); | |
903 | struct hv_device *device_obj = &device_ctx->device_obj; | |
904 | struct input_device_context *input_dev_ctx; | |
905 | ||
906 | input_dev_ctx = kmalloc(sizeof(struct input_device_context), | |
907 | GFP_KERNEL); | |
908 | ||
909 | dev_set_drvdata(device, input_dev_ctx); | |
910 | ||
911 | /* Call to the vsc driver to add the device */ | |
912 | ret = mousevsc_drv_obj->Base.dev_add(device_obj, NULL); | |
913 | ||
914 | if (ret != 0) { | |
915 | DPRINT_ERR(INPUTVSC_DRV, "unable to add input vsc device"); | |
916 | ||
917 | return -1; | |
918 | } | |
919 | ||
920 | return 0; | |
921 | } | |
922 | ||
923 | ||
924 | int mousevsc_remove(struct device *device) | |
925 | { | |
926 | int ret = 0; | |
927 | ||
928 | struct driver_context *driver_ctx = | |
929 | driver_to_driver_context(device->driver); | |
930 | struct mousevsc_driver_context *mousevsc_drv_ctx = | |
931 | (struct mousevsc_driver_context *)driver_ctx; | |
932 | struct mousevsc_drv_obj *mousevsc_drv_obj = &mousevsc_drv_ctx->drv_obj; | |
933 | ||
934 | struct vm_device *device_ctx = device_to_vm_device(device); | |
935 | struct hv_device *device_obj = &device_ctx->device_obj; | |
936 | struct input_device_context *input_dev_ctx; | |
937 | ||
938 | input_dev_ctx = kmalloc(sizeof(struct input_device_context), | |
939 | GFP_KERNEL); | |
940 | ||
941 | dev_set_drvdata(device, input_dev_ctx); | |
942 | ||
943 | if (input_dev_ctx->connected) { | |
944 | hidinput_disconnect(input_dev_ctx->hid_device); | |
945 | input_dev_ctx->connected = 0; | |
946 | } | |
947 | ||
e8290f9f | 948 | if (!mousevsc_drv_obj->Base.dev_rm) |
0c3a6ede | 949 | return -1; |
0c3a6ede GKH |
950 | |
951 | /* | |
952 | * Call to the vsc driver to let it know that the device | |
953 | * is being removed | |
954 | */ | |
955 | ret = mousevsc_drv_obj->Base.dev_rm(device_obj); | |
956 | ||
957 | if (ret != 0) { | |
958 | DPRINT_ERR(INPUTVSC_DRV, | |
959 | "unable to remove vsc device (ret %d)", ret); | |
960 | } | |
961 | ||
962 | kfree(input_dev_ctx); | |
963 | ||
964 | return ret; | |
965 | } | |
966 | ||
967 | void mousevsc_reportdesc_callback(struct hv_device *dev, void *packet, u32 len) | |
968 | { | |
969 | struct vm_device *device_ctx = to_vm_device(dev); | |
970 | struct input_device_context *input_device_ctx = | |
971 | dev_get_drvdata(&device_ctx->device); | |
972 | struct hid_device *hid_dev; | |
973 | ||
974 | /* hid_debug = -1; */ | |
975 | hid_dev = kmalloc(sizeof(struct hid_device), GFP_KERNEL); | |
976 | ||
977 | if (hid_parse_report(hid_dev, packet, len)) { | |
978 | DPRINT_INFO(INPUTVSC_DRV, "Unable to call hd_parse_report"); | |
979 | return; | |
980 | } | |
981 | ||
982 | if (hid_dev) { | |
983 | DPRINT_INFO(INPUTVSC_DRV, "hid_device created"); | |
984 | ||
985 | hid_dev->ll_driver->open = mousevsc_hid_open; | |
986 | hid_dev->ll_driver->close = mousevsc_hid_close; | |
987 | ||
988 | hid_dev->bus = 0x06; /* BUS_VIRTUAL */ | |
989 | hid_dev->vendor = input_device_ctx->device_info.VendorID; | |
990 | hid_dev->product = input_device_ctx->device_info.ProductID; | |
991 | hid_dev->version = input_device_ctx->device_info.VersionNumber; | |
992 | hid_dev->dev = device_ctx->device; | |
993 | ||
994 | sprintf(hid_dev->name, "%s", | |
995 | input_device_ctx->device_info.Name); | |
996 | ||
997 | /* | |
998 | * HJ Do we want to call it with a 0 | |
999 | */ | |
1000 | if (!hidinput_connect(hid_dev, 0)) { | |
1001 | hid_dev->claimed |= HID_CLAIMED_INPUT; | |
1002 | ||
1003 | input_device_ctx->connected = 1; | |
1004 | ||
1005 | DPRINT_INFO(INPUTVSC_DRV, | |
1006 | "HID device claimed by input\n"); | |
1007 | } | |
1008 | ||
1009 | if (!hid_dev->claimed) { | |
1010 | DPRINT_ERR(INPUTVSC_DRV, | |
1011 | "HID device not claimed by " | |
1012 | "input or hiddev\n"); | |
1013 | } | |
1014 | ||
1015 | input_device_ctx->hid_device = hid_dev; | |
1016 | } | |
1017 | ||
1018 | kfree(hid_dev); | |
1019 | } | |
1020 | ||
1021 | /* | |
1022 | * | |
1023 | * Name: mousevsc_drv_init() | |
1024 | * | |
1025 | * Desc: Driver initialization. | |
1026 | */ | |
1027 | int mousevsc_drv_init(int (*pfn_drv_init)(struct hv_driver *pfn_drv_init)) | |
1028 | { | |
1029 | int ret = 0; | |
1030 | struct mousevsc_drv_obj *input_drv_obj = &g_mousevsc_drv.drv_obj; | |
1031 | struct driver_context *drv_ctx = &g_mousevsc_drv.drv_ctx; | |
1032 | ||
0c3a6ede GKH |
1033 | input_drv_obj->OnDeviceInfo = mousevsc_deviceinfo_callback; |
1034 | input_drv_obj->OnInputReport = mousevsc_inputreport_callback; | |
1035 | input_drv_obj->OnReportDescriptor = mousevsc_reportdesc_callback; | |
1036 | ||
1037 | /* Callback to client driver to complete the initialization */ | |
1038 | pfn_drv_init(&input_drv_obj->Base); | |
1039 | ||
1040 | drv_ctx->driver.name = input_drv_obj->Base.name; | |
1041 | memcpy(&drv_ctx->class_id, &input_drv_obj->Base.dev_type, | |
1042 | sizeof(struct hv_guid)); | |
1043 | ||
1044 | drv_ctx->probe = mousevsc_probe; | |
1045 | drv_ctx->remove = mousevsc_remove; | |
1046 | ||
1047 | /* The driver belongs to vmbus */ | |
1048 | vmbus_child_driver_register(drv_ctx); | |
1049 | ||
1050 | return ret; | |
1051 | } | |
1052 | ||
1053 | ||
1054 | int mousevsc_drv_exit_cb(struct device *dev, void *data) | |
1055 | { | |
1056 | struct device **curr = (struct device **)data; | |
1057 | *curr = dev; | |
1058 | ||
1059 | return 1; | |
1060 | } | |
1061 | ||
1062 | void mousevsc_drv_exit(void) | |
1063 | { | |
1064 | struct mousevsc_drv_obj *mousevsc_drv_obj = &g_mousevsc_drv.drv_obj; | |
1065 | struct driver_context *drv_ctx = &g_mousevsc_drv.drv_ctx; | |
1066 | int ret; | |
1067 | ||
1068 | struct device *current_dev = NULL; | |
1069 | ||
1070 | while (1) { | |
1071 | current_dev = NULL; | |
1072 | ||
1073 | /* Get the device */ | |
e8290f9f GKH |
1074 | ret = driver_for_each_device(&drv_ctx->driver, NULL, |
1075 | (void *)¤t_dev, | |
1076 | mousevsc_drv_exit_cb); | |
0c3a6ede GKH |
1077 | if (ret) |
1078 | printk(KERN_ERR "Can't find mouse device!\n"); | |
1079 | ||
1080 | if (current_dev == NULL) | |
1081 | break; | |
1082 | ||
1083 | /* Initiate removal from the top-down */ | |
1084 | device_unregister(current_dev); | |
1085 | } | |
1086 | ||
1087 | if (mousevsc_drv_obj->Base.cleanup) | |
1088 | mousevsc_drv_obj->Base.cleanup(&mousevsc_drv_obj->Base); | |
1089 | ||
1090 | vmbus_child_driver_unregister(drv_ctx); | |
1091 | ||
1092 | return; | |
1093 | } | |
1094 | ||
1095 | static int __init mousevsc_init(void) | |
1096 | { | |
1097 | int ret; | |
1098 | ||
1099 | DPRINT_INFO(INPUTVSC_DRV, "Hyper-V Mouse driver initializing."); | |
1100 | ||
1101 | ret = mousevsc_drv_init(mouse_vsc_initialize); | |
1102 | ||
1103 | return ret; | |
1104 | } | |
1105 | ||
1106 | static void __exit mousevsc_exit(void) | |
1107 | { | |
1108 | mousevsc_drv_exit(); | |
1109 | } | |
1110 | ||
76e63665 GKH |
1111 | /* |
1112 | * We don't want to automatically load this driver just yet, it's quite | |
1113 | * broken. It's safe if you want to load it yourself manually, but | |
1114 | * don't inflict it on unsuspecting users, that's just mean. | |
1115 | */ | |
1116 | #if 0 | |
1117 | ||
0c3a6ede GKH |
1118 | /* |
1119 | * We use a PCI table to determine if we should autoload this driver This is | |
1120 | * needed by distro tools to determine if the hyperv drivers should be | |
1121 | * installed and/or configured. We don't do anything else with the table, but | |
1122 | * it needs to be present. | |
1123 | */ | |
1124 | const static struct pci_device_id microsoft_hv_pci_table[] = { | |
1125 | { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */ | |
1126 | { 0 } | |
1127 | }; | |
1128 | MODULE_DEVICE_TABLE(pci, microsoft_hv_pci_table); | |
76e63665 | 1129 | #endif |
0c3a6ede GKH |
1130 | |
1131 | MODULE_LICENSE("GPL"); | |
1132 | MODULE_VERSION(HV_DRV_VERSION); | |
1133 | module_init(mousevsc_init); | |
1134 | module_exit(mousevsc_exit); | |
1135 |