bd6e0d92b26f4cfa2d8293e9ee3a18aabbcd5a0d
[reactos.git] / drivers / hid / hidclass / pdo.c
1 /*
2 * PROJECT: ReactOS Universal Serial Bus Human Interface Device Driver
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/hid/hidclass/fdo.c
5 * PURPOSE: HID Class Driver
6 * PROGRAMMERS:
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
9 */
10 #include "precomp.h"
11
12 PHIDP_COLLECTION_DESC
13 HidClassPDO_GetCollectionDescription(
14 PHIDP_DEVICE_DESC DeviceDescription,
15 ULONG CollectionNumber)
16 {
17 ULONG Index;
18
19 for(Index = 0; Index < DeviceDescription->CollectionDescLength; Index++)
20 {
21 if (DeviceDescription->CollectionDesc[Index].CollectionNumber == CollectionNumber)
22 {
23 //
24 // found collection
25 //
26 return &DeviceDescription->CollectionDesc[Index];
27 }
28 }
29
30 //
31 // failed to find collection
32 //
33 DPRINT1("[HIDCLASS] GetCollectionDescription CollectionNumber %x not found\n", CollectionNumber);
34 ASSERT(FALSE);
35 return NULL;
36 }
37
38 PHIDP_REPORT_IDS
39 HidClassPDO_GetReportDescription(
40 PHIDP_DEVICE_DESC DeviceDescription,
41 ULONG CollectionNumber)
42 {
43 ULONG Index;
44
45 for(Index = 0; Index < DeviceDescription->ReportIDsLength; Index++)
46 {
47 if (DeviceDescription->ReportIDs[Index].CollectionNumber == CollectionNumber)
48 {
49 //
50 // found collection
51 //
52 return &DeviceDescription->ReportIDs[Index];
53 }
54 }
55
56 //
57 // failed to find collection
58 //
59 DPRINT1("[HIDCLASS] GetReportDescription CollectionNumber %x not found\n", CollectionNumber);
60 ASSERT(FALSE);
61 return NULL;
62 }
63
64 NTSTATUS
65 HidClassPDO_HandleQueryDeviceId(
66 IN PDEVICE_OBJECT DeviceObject,
67 IN PIRP Irp)
68 {
69 NTSTATUS Status;
70 LPWSTR Buffer;
71 LPWSTR NewBuffer, Ptr;
72 ULONG Length;
73
74 //
75 // copy current stack location
76 //
77 IoCopyCurrentIrpStackLocationToNext(Irp);
78
79 //
80 // call mini-driver
81 //
82 Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp);
83 if (!NT_SUCCESS(Status))
84 {
85 //
86 // failed
87 //
88 return Status;
89 }
90
91 //
92 // get buffer
93 //
94 Buffer = (LPWSTR)Irp->IoStatus.Information;
95 Length = wcslen(Buffer);
96
97 //
98 // allocate new buffer
99 //
100 NewBuffer = (LPWSTR)ExAllocatePool(NonPagedPool, (Length + 1) * sizeof(WCHAR));
101 if (!NewBuffer)
102 {
103 //
104 // failed to allocate buffer
105 //
106 return STATUS_INSUFFICIENT_RESOURCES;
107 }
108
109 //
110 // replace bus
111 //
112 wcscpy(NewBuffer, L"HID\\");
113
114 //
115 // get offset to first '\\'
116 //
117 Ptr = wcschr(Buffer, L'\\');
118 if (Ptr)
119 {
120 //
121 // append result
122 //
123 wcscat(NewBuffer, Ptr + 1);
124 }
125
126 //
127 // free old buffer
128 //
129 ExFreePool(Buffer);
130
131 //
132 // store result
133 //
134 DPRINT1("NewBuffer %S\n", NewBuffer);
135 Irp->IoStatus.Information = (ULONG_PTR)NewBuffer;
136 return STATUS_SUCCESS;
137 }
138
139 NTSTATUS
140 HidClassPDO_HandleQueryHardwareId(
141 IN PDEVICE_OBJECT DeviceObject,
142 IN PIRP Irp)
143 {
144 NTSTATUS Status;
145 PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
146 WCHAR Buffer[100];
147 ULONG Offset = 0;
148 LPWSTR Ptr;
149 PHIDP_COLLECTION_DESC CollectionDescription;
150
151 //
152 // get device extension
153 //
154 PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
155 ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
156
157 //
158 // copy current stack location
159 //
160 IoCopyCurrentIrpStackLocationToNext(Irp);
161
162 //
163 // call mini-driver
164 //
165 Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp);
166 if (!NT_SUCCESS(Status))
167 {
168 //
169 // failed
170 //
171 return Status;
172 }
173
174 if (PDODeviceExtension->Common.DeviceDescription.CollectionDescLength > 1)
175 {
176 //
177 // multi-tlc device
178 //
179 Offset = swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x&Rev_%04x&Col%02x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->Common.Attributes.VersionNumber, PDODeviceExtension->CollectionNumber) + 1;
180 Offset += swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x&Col%02x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->CollectionNumber) + 1;
181 }
182 else
183 {
184 //
185 // single tlc device
186 //
187 Offset = swprintf(&Buffer[Offset], L"HID\\Vix_%04x&Pid_%04x&Rev_%04x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->Common.Attributes.VersionNumber) + 1;
188 Offset += swprintf(&Buffer[Offset], L"HID\\Vix_%04x&Pid_%04x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID) + 1;
189 }
190
191 //
192 // get collection description
193 //
194 CollectionDescription = HidClassPDO_GetCollectionDescription(&PDODeviceExtension->Common.DeviceDescription, PDODeviceExtension->CollectionNumber);
195 ASSERT(CollectionDescription);
196
197 if (CollectionDescription->UsagePage == HID_USAGE_PAGE_GENERIC)
198 {
199 switch(CollectionDescription->Usage)
200 {
201 case HID_USAGE_GENERIC_POINTER:
202 case HID_USAGE_GENERIC_MOUSE:
203 //
204 // Pointer / Mouse
205 //
206 Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_MOUSE") + 1;
207 break;
208 case HID_USAGE_GENERIC_GAMEPAD:
209 case HID_USAGE_GENERIC_JOYSTICK:
210 //
211 // Joystick / Gamepad
212 //
213 Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_GAME") + 1;
214 break;
215 case HID_USAGE_GENERIC_KEYBOARD:
216 case HID_USAGE_GENERIC_KEYPAD:
217 //
218 // Keyboard / Keypad
219 //
220 Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_KEYBOARD") + 1;
221 break;
222 case HID_USAGE_GENERIC_SYSTEM_CTL:
223 //
224 // System Control
225 //
226 Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_CONTROL") + 1;
227 break;
228 }
229 }
230 else if (CollectionDescription->UsagePage == HID_USAGE_PAGE_CONSUMER && CollectionDescription->Usage == HID_USAGE_CONSUMERCTRL)
231 {
232 //
233 // Consumer Audio Control
234 //
235 Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_CONSUMER") + 1;
236 }
237
238 //
239 // FIXME: add 'HID_DEVICE_UP:0001_U:0002'
240 //
241
242 //
243 // add HID
244 //
245 Offset +=swprintf(&Buffer[Offset], L"HID_DEVICE") + 1;
246
247 //
248 // free old buffer
249 //
250 ExFreePool((PVOID)Irp->IoStatus.Information);
251
252 //
253 // allocate buffer
254 //
255 Ptr = (LPWSTR)ExAllocatePool(NonPagedPool, (Offset +1)* sizeof(WCHAR));
256 if (!Ptr)
257 {
258 //
259 // no memory
260 //
261 Irp->IoStatus.Information = 0;
262 return STATUS_INSUFFICIENT_RESOURCES;
263 }
264
265 //
266 // copy buffer
267 //
268 RtlCopyMemory(Ptr, Buffer, Offset * sizeof(WCHAR));
269 Ptr[Offset] = UNICODE_NULL;
270
271 //
272 // store result
273 //
274 Irp->IoStatus.Information = (ULONG_PTR)Ptr;
275 return STATUS_SUCCESS;
276 }
277
278 NTSTATUS
279 HidClassPDO_HandleQueryInstanceId(
280 IN PDEVICE_OBJECT DeviceObject,
281 IN PIRP Irp)
282 {
283 NTSTATUS Status;
284
285 //
286 // copy current stack location
287 //
288 IoCopyCurrentIrpStackLocationToNext(Irp);
289
290 //
291 // call mini-driver
292 //
293 Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp);
294 if (!NT_SUCCESS(Status))
295 {
296 //
297 // failed
298 //
299 return Status;
300 }
301 DPRINT1("HidClassPDO_HandleQueryInstanceId Buffer %S\n", Irp->IoStatus.Information);
302 return Status;
303 }
304
305 NTSTATUS
306 HidClassPDO_HandleQueryCompatibleId(
307 IN PDEVICE_OBJECT DeviceObject,
308 IN PIRP Irp)
309 {
310 LPWSTR Buffer;
311
312 Buffer = (LPWSTR)ExAllocatePool(NonPagedPool, 2 * sizeof(WCHAR));
313 if (!Buffer)
314 {
315 //
316 // no memory
317 //
318 return STATUS_INSUFFICIENT_RESOURCES;
319 }
320
321 //
322 // zero buffer
323 //
324 Buffer[0] = 0;
325 Buffer[1] = 0;
326
327 //
328 // store result
329 //
330 Irp->IoStatus.Information = (ULONG_PTR)Buffer;
331 return STATUS_SUCCESS;
332 }
333
334
335 NTSTATUS
336 HidClassPDO_PnP(
337 IN PDEVICE_OBJECT DeviceObject,
338 IN PIRP Irp)
339 {
340 PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
341 PIO_STACK_LOCATION IoStack;
342 NTSTATUS Status;
343 PPNP_BUS_INFORMATION BusInformation;
344 PDEVICE_RELATIONS DeviceRelation;
345
346 //
347 // get device extension
348 //
349 PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
350 ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
351
352 //
353 // get current irp stack location
354 //
355 IoStack = IoGetCurrentIrpStackLocation(Irp);
356
357 //
358 // handle request
359 //
360 switch(IoStack->MinorFunction)
361 {
362 case IRP_MN_QUERY_ID:
363 {
364 if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID)
365 {
366 //
367 // handle query device id
368 //
369 Status = HidClassPDO_HandleQueryDeviceId(DeviceObject, Irp);
370 break;
371 }
372 else if (IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs)
373 {
374 //
375 // handle instance id
376 //
377 Status = HidClassPDO_HandleQueryHardwareId(DeviceObject, Irp);
378 break;
379 }
380 else if (IoStack->Parameters.QueryId.IdType == BusQueryInstanceID)
381 {
382 //
383 // handle instance id
384 //
385 Status = HidClassPDO_HandleQueryInstanceId(DeviceObject, Irp);
386 break;
387 }
388 else if (IoStack->Parameters.QueryId.IdType == BusQueryCompatibleIDs)
389 {
390 //
391 // handle instance id
392 //
393 Status = HidClassPDO_HandleQueryCompatibleId(DeviceObject, Irp);
394 break;
395 }
396
397 DPRINT1("[HIDCLASS]: IRP_MN_QUERY_ID IdType %x unimplemented\n", IoStack->Parameters.QueryId.IdType);
398 Status = STATUS_NOT_SUPPORTED;
399 Irp->IoStatus.Information = 0;
400 break;
401 }
402 case IRP_MN_QUERY_CAPABILITIES:
403 {
404 if (IoStack->Parameters.DeviceCapabilities.Capabilities == NULL)
405 {
406 //
407 // invalid request
408 //
409 Status = STATUS_DEVICE_CONFIGURATION_ERROR;
410 }
411
412 //
413 // copy capabilities
414 //
415 RtlCopyMemory(IoStack->Parameters.DeviceCapabilities.Capabilities, &PDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES));
416 Status = STATUS_SUCCESS;
417 break;
418 }
419 case IRP_MN_QUERY_BUS_INFORMATION:
420 {
421 //
422 //
423 //
424 BusInformation = (PPNP_BUS_INFORMATION)ExAllocatePool(NonPagedPool, sizeof(PNP_BUS_INFORMATION));
425
426 //
427 // fill in result
428 //
429 RtlCopyMemory(&BusInformation->BusTypeGuid, &GUID_BUS_TYPE_HID, sizeof(GUID));
430 BusInformation->LegacyBusType = PNPBus;
431 BusInformation->BusNumber = 0; //FIXME
432
433 //
434 // store result
435 //
436 Irp->IoStatus.Information = (ULONG_PTR)BusInformation;
437 Status = STATUS_SUCCESS;
438 break;
439 }
440 case IRP_MN_QUERY_PNP_DEVICE_STATE:
441 {
442 //
443 // FIXME set flags when driver fails / disabled
444 //
445 Status = STATUS_SUCCESS;
446 break;
447 }
448 case IRP_MN_QUERY_DEVICE_RELATIONS:
449 {
450 //
451 // only target relations are supported
452 //
453 if (IoStack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
454 {
455 //
456 // not supported
457 //
458 Status = Irp->IoStatus.Status;
459 break;
460 }
461
462 //
463 // allocate device relations
464 //
465 DeviceRelation = (PDEVICE_RELATIONS)ExAllocatePool(NonPagedPool, sizeof(DEVICE_RELATIONS));
466 if (!DeviceRelation)
467 {
468 //
469 // no memory
470 //
471 Status = STATUS_INSUFFICIENT_RESOURCES;
472 break;
473 }
474
475 //
476 // init device relation
477 //
478 DeviceRelation->Count = 1;
479 DeviceRelation->Objects[0] = DeviceObject;
480 ObReferenceObject(DeviceRelation->Objects[0]);
481
482 //
483 // store result
484 //
485 Irp->IoStatus.Information = (ULONG_PTR)DeviceRelation;
486 Status = STATUS_SUCCESS;
487 break;
488 }
489 case IRP_MN_START_DEVICE:
490 {
491 //
492 // FIXME: support polled devices
493 //
494 ASSERT(PDODeviceExtension->Common.DriverExtension->DevicesArePolled == FALSE);
495
496 //
497 // now register the device interface
498 //
499 Status = IoRegisterDeviceInterface(PDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject, &GUID_DEVINTERFACE_HID, NULL, &PDODeviceExtension->DeviceInterface);
500 DPRINT1("[HIDCLASS] IoRegisterDeviceInterfaceState Status %x\n", Status);
501 if (NT_SUCCESS(Status))
502 {
503 //
504 // enable device interface
505 //
506 Status = IoSetDeviceInterfaceState(&PDODeviceExtension->DeviceInterface, TRUE);
507 DPRINT1("[HIDCLASS] IoSetDeviceInterFaceState %x\n", Status);
508 }
509 ASSERT(Status == STATUS_SUCCESS);
510 //
511 // break
512 //
513 break;
514 }
515 case IRP_MN_REMOVE_DEVICE:
516 {
517 DPRINT1("[HIDCLASS] PDO IRP_MN_REMOVE_DEVICE not implemented\n");
518 ASSERT(FALSE);
519
520 //
521 // do nothing
522 //
523 Status = STATUS_SUCCESS; //Irp->IoStatus.Status;
524 break;
525 }
526 case IRP_MN_QUERY_INTERFACE:
527 {
528 DPRINT1("[HIDCLASS] PDO IRP_MN_QUERY_INTERFACE not implemented\n");
529 ASSERT(FALSE);
530
531 //
532 // do nothing
533 //
534 Status = Irp->IoStatus.Status;
535 break;
536 }
537 default:
538 {
539 //
540 // do nothing
541 //
542 Status = Irp->IoStatus.Status;
543 break;
544 }
545 }
546
547 //
548 // complete request
549 //
550 if (Status != STATUS_PENDING)
551 {
552 //
553 // store result
554 //
555 Irp->IoStatus.Status = Status;
556
557 //
558 // complete request
559 //
560 IoCompleteRequest(Irp, IO_NO_INCREMENT);
561 }
562
563 //
564 // done processing
565 //
566 return Status;
567 }
568
569 NTSTATUS
570 HidClassPDO_CreatePDO(
571 IN PDEVICE_OBJECT DeviceObject,
572 OUT PDEVICE_RELATIONS *OutDeviceRelations)
573 {
574 PHIDCLASS_FDO_EXTENSION FDODeviceExtension;
575 NTSTATUS Status;
576 PDEVICE_OBJECT PDODeviceObject;
577 PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension;
578 ULONG Index;
579 PDEVICE_RELATIONS DeviceRelations;
580 ULONG Length;
581
582 //
583 // get device extension
584 //
585 FDODeviceExtension = (PHIDCLASS_FDO_EXTENSION)DeviceObject->DeviceExtension;
586 ASSERT(FDODeviceExtension->Common.IsFDO);
587
588 //
589 // first allocate device relations
590 //
591 Length = sizeof(DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT) * FDODeviceExtension->Common.DeviceDescription.CollectionDescLength;
592 DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(NonPagedPool, Length);
593 if (!DeviceRelations)
594 {
595 //
596 // no memory
597 //
598 return STATUS_INSUFFICIENT_RESOURCES;
599 }
600
601 //
602 // zero device relations
603 //
604 RtlZeroMemory(DeviceRelations, Length);
605
606 //
607 // lets create a PDO for top level collection
608 //
609 Index = 0;
610 do
611 {
612 //
613 // lets create the device object
614 //
615 Status = IoCreateDevice(FDODeviceExtension->Common.DriverExtension->DriverObject, sizeof(HIDCLASS_PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_UNKNOWN, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &PDODeviceObject);
616 if (!NT_SUCCESS(Status))
617 {
618 //
619 // failed to create device
620 //
621 DPRINT1("[HIDCLASS] Failed to create PDO %x\n", Status);
622 break;
623 }
624
625 //
626 // patch stack size
627 //
628 PDODeviceObject->StackSize = DeviceObject->StackSize + 1;
629
630 //
631 // get device extension
632 //
633 PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)PDODeviceObject->DeviceExtension;
634
635 //
636 // init device extension
637 //
638 PDODeviceExtension->Common.HidDeviceExtension.MiniDeviceExtension = FDODeviceExtension->Common.HidDeviceExtension.MiniDeviceExtension;
639 PDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject = FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject;
640 PDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject = FDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject;
641 PDODeviceExtension->Common.IsFDO = FALSE;
642 PDODeviceExtension->FDODeviceObject = DeviceObject;
643 PDODeviceExtension->Common.DriverExtension = FDODeviceExtension->Common.DriverExtension;
644 PDODeviceExtension->CollectionNumber = FDODeviceExtension->Common.DeviceDescription.CollectionDesc[Index].CollectionNumber;
645
646 //
647 // copy device data
648 //
649 RtlCopyMemory(&PDODeviceExtension->Common.Attributes, &FDODeviceExtension->Common.Attributes, sizeof(HID_DEVICE_ATTRIBUTES));
650 RtlCopyMemory(&PDODeviceExtension->Common.DeviceDescription, &FDODeviceExtension->Common.DeviceDescription, sizeof(HIDP_DEVICE_DESC));
651 RtlCopyMemory(&PDODeviceExtension->Capabilities, &FDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES));
652
653 //
654 // set device flags
655 //
656 PDODeviceObject->Flags |= DO_MAP_IO_BUFFER;
657
658 //
659 // device is initialized
660 //
661 PDODeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
662
663 //
664 // store device object in device relations
665 //
666 DeviceRelations->Objects[Index] = PDODeviceObject;
667 DeviceRelations->Count++;
668
669 //
670 // move to next
671 //
672 Index++;
673
674 }while(Index < FDODeviceExtension->Common.DeviceDescription.CollectionDescLength);
675
676
677 //
678 // check if creating succeeded
679 //
680 if (!NT_SUCCESS(Status))
681 {
682 //
683 // failed
684 //
685 for(Index = 0; Index < DeviceRelations->Count; Index++)
686 {
687 //
688 // delete device
689 //
690 IoDeleteDevice(DeviceRelations->Objects[Index]);
691 }
692
693 //
694 // free device relations
695 //
696 ExFreePool(DeviceRelations);
697 return Status;
698 }
699
700 //
701 // store device relations
702 //
703 *OutDeviceRelations = DeviceRelations;
704
705 //
706 // done
707 //
708 return STATUS_SUCCESS;
709 }