xref: /reactos/drivers/usb/usbstor/pdo.c (revision d8c6ef5e)
1 /*
2  * PROJECT:     ReactOS Universal Serial Bus Bulk Storage Driver
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     USB block storage device driver.
5  * COPYRIGHT:   2005-2006 James Tabor
6  *              2011-2012 Michael Martin (michael.martin@reactos.org)
7  *              2011-2013 Johannes Anderwald (johannes.anderwald@reactos.org)
8  *              2017 Vadim Galyant
9  *              2019 Victor Perevertkin (victor.perevertkin@reactos.org)
10  */
11 
12 #include "usbstor.h"
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 
18 LPCSTR
19 USBSTOR_GetDeviceType(
20     IN PINQUIRYDATA InquiryData,
21     IN UCHAR IsFloppy)
22 {
23     if (InquiryData->DeviceType == 0)
24     {
25         if (IsFloppy)
26         {
27             // floppy device
28             return "SFloppy";
29         }
30 
31         // direct access device
32         return "Disk";
33     }
34 
35     switch (InquiryData->DeviceType)
36     {
37         case 1:
38         {
39             // sequential device, i.e magnetic tape
40             return "Sequential";
41         }
42         case 4:
43         {
44             // write once device
45             return "Worm";
46         }
47         case 5:
48         {
49             // CDROM device
50             return "CdRom";
51         }
52         case 7:
53         {
54             // optical memory device
55             return "Optical";
56         }
57         case 8:
58         {
59             // medium change device
60             return "Changer";
61         }
62         default:
63         {
64             // other device
65             return "Other";
66         }
67     }
68 }
69 
70 LPCSTR
71 USBSTOR_GetGenericType(
72     IN PINQUIRYDATA InquiryData,
73     IN UCHAR IsFloppy)
74 {
75     if (InquiryData->DeviceType == 0)
76     {
77         if (IsFloppy)
78         {
79             // floppy device
80             return "GenSFloppy";
81         }
82 
83         // direct access device
84         return "GenDisk";
85     }
86 
87     switch (InquiryData->DeviceType)
88     {
89         case 1:
90         {
91             // sequential device, i.e magnetic tape
92             return "GenSequential";
93         }
94         case 4:
95         {
96             // write once device
97             return "GenWorm";
98         }
99         case 5:
100         {
101             // CDROM device
102             return "GenCdRom";
103         }
104         case 7:
105         {
106             // optical memory device
107             return "GenOptical";
108         }
109         case 8:
110         {
111             // medium change device
112             return "GenChanger";
113         }
114         default:
115         {
116             // other device
117             return "UsbstorOther";
118         }
119     }
120 }
121 
122 static
123 ULONG
124 CopyField(
125     IN PUCHAR Name,
126     IN PCHAR Buffer,
127     IN ULONG MaxLength)
128 {
129     ULONG Index;
130 
131     for (Index = 0; Index < MaxLength; Index++)
132     {
133         if (Name[Index] <= ' ' || Name[Index] >= 0x7F /* last printable ascii character */ ||  Name[Index] == ',')
134         {
135             // convert to underscore
136             Buffer[Index] = '_';
137         }
138         else
139         {
140             // just copy character
141             Buffer[Index] = Name[Index];
142         }
143     }
144 
145     return MaxLength;
146 }
147 
148 static
149 ULONG
150 CopyFieldTruncate(
151     IN PUCHAR Name,
152     IN PCHAR Buffer,
153     IN ULONG MaxLength)
154 {
155     ULONG Index;
156 
157     for (Index = 0; Index < MaxLength; Index++)
158     {
159         if (Name[Index] == '\0')
160         {
161             break;
162         }
163         else if (Name[Index] <= ' ' || Name[Index] >= 0x7F /* last printable ascii character */ ||  Name[Index] == ',')
164         {
165             // convert to underscore
166             Buffer[Index] = ' ';
167         }
168         else
169         {
170             // just copy character
171             Buffer[Index] = Name[Index];
172         }
173     }
174 
175     return Index;
176 }
177 
178 NTSTATUS
179 USBSTOR_PdoHandleQueryDeviceText(
180     IN PDEVICE_OBJECT DeviceObject,
181     IN PIRP Irp)
182 {
183     PPDO_DEVICE_EXTENSION DeviceExtension;
184     PIO_STACK_LOCATION IoStack;
185     CHAR LocalBuffer[26];
186     UINT32 Offset = 0;
187     PINQUIRYDATA InquiryData;
188     ANSI_STRING AnsiString;
189     UNICODE_STRING DeviceDescription;
190 
191     IoStack = IoGetCurrentIrpStackLocation(Irp);
192 
193     DeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
194     ASSERT(DeviceExtension->InquiryData);
195     InquiryData = DeviceExtension->InquiryData;
196 
197     switch (IoStack->Parameters.QueryDeviceText.DeviceTextType)
198     {
199         case DeviceTextDescription:
200         case DeviceTextLocationInformation:
201         {
202             DPRINT("USBSTOR_PdoHandleQueryDeviceText\n");
203 
204             Offset += CopyFieldTruncate(InquiryData->VendorId, &LocalBuffer[Offset], sizeof(InquiryData->VendorId));
205             LocalBuffer[Offset++] = ' ';
206             Offset += CopyFieldTruncate(InquiryData->ProductId, &LocalBuffer[Offset], sizeof(InquiryData->ProductId));
207             LocalBuffer[Offset++] = '\0';
208 
209             RtlInitAnsiString(&AnsiString, (PCSZ)&LocalBuffer);
210 
211             DeviceDescription.Length = 0;
212             DeviceDescription.MaximumLength = (USHORT)(Offset * sizeof(WCHAR));
213             DeviceDescription.Buffer = (LPWSTR)AllocateItem(PagedPool, DeviceDescription.MaximumLength);
214             if (!DeviceDescription.Buffer)
215             {
216                 Irp->IoStatus.Information = 0;
217                 return STATUS_INSUFFICIENT_RESOURCES;
218             }
219 
220             RtlAnsiStringToUnicodeString(&DeviceDescription, &AnsiString, FALSE);
221 
222             Irp->IoStatus.Information = (ULONG_PTR)DeviceDescription.Buffer;
223             return STATUS_SUCCESS;
224         }
225         default:
226         {
227             Irp->IoStatus.Information = 0;
228             return Irp->IoStatus.Status;
229         }
230     }
231 }
232 
233 NTSTATUS
234 USBSTOR_PdoHandleQueryDeviceId(
235     IN PDEVICE_OBJECT DeviceObject,
236     IN PIRP Irp)
237 {
238     PPDO_DEVICE_EXTENSION DeviceExtension;
239     NTSTATUS Status;
240     CHAR Buffer[100] = {0};
241     LPCSTR DeviceType;
242     ULONG Offset = 0;
243     PINQUIRYDATA InquiryData;
244     ANSI_STRING AnsiString;
245     UNICODE_STRING DeviceId;
246 
247     DeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
248     ASSERT(DeviceExtension->InquiryData);
249     InquiryData = DeviceExtension->InquiryData;
250 
251     DeviceType = USBSTOR_GetDeviceType(InquiryData, DeviceExtension->IsFloppy);
252 
253     // lets create device string
254     Offset = sprintf(&Buffer[Offset], "USBSTOR\\");
255     Offset += sprintf(&Buffer[Offset], DeviceType);
256     Offset += sprintf(&Buffer[Offset], "&Ven_");
257     Offset += CopyField(InquiryData->VendorId, &Buffer[Offset], 8);
258     Offset += sprintf(&Buffer[Offset], "&Prod_");
259     Offset += CopyField(InquiryData->ProductId, &Buffer[Offset], 16);
260     Offset += sprintf(&Buffer[Offset], "&Rev_");
261     Offset += CopyField(InquiryData->ProductRevisionLevel, &Buffer[Offset], 4);
262 
263     RtlInitAnsiString(&AnsiString, (PCSZ)Buffer);
264 
265     // allocate DeviceId string
266     DeviceId.Length = 0;
267     DeviceId.MaximumLength = (USHORT)((strlen((PCHAR)Buffer) + 1) * sizeof(WCHAR));
268     DeviceId.Buffer = (LPWSTR)AllocateItem(PagedPool, DeviceId.MaximumLength);
269     if (!DeviceId.Buffer)
270     {
271         Irp->IoStatus.Information = 0;
272         return STATUS_INSUFFICIENT_RESOURCES;
273     }
274 
275     Status = RtlAnsiStringToUnicodeString(&DeviceId, &AnsiString, FALSE);
276 
277     if (NT_SUCCESS(Status))
278     {
279         Irp->IoStatus.Information = (ULONG_PTR)DeviceId.Buffer;
280     }
281 
282     DPRINT("DeviceId %wZ Status %x\n", &DeviceId, Status);
283 
284     return Status;
285 }
286 
287 VOID
288 USBSTOR_ConvertToUnicodeString(
289     IN CHAR * Buffer,
290     IN ULONG ResultBufferLength,
291     IN ULONG ResultBufferOffset,
292     OUT LPWSTR ResultBuffer,
293     OUT PULONG NewResultBufferOffset)
294 {
295     UNICODE_STRING DeviceString;
296     ANSI_STRING AnsiString;
297     NTSTATUS Status;
298 
299     ASSERT(ResultBufferLength);
300     ASSERT(ResultBufferLength > ResultBufferOffset);
301 
302     DPRINT("ResultBufferOffset %lu ResultBufferLength %lu Buffer %s Length %lu\n", ResultBufferOffset, ResultBufferLength, Buffer, strlen(Buffer));
303 
304     // construct destination string
305     DeviceString.Buffer = &ResultBuffer[ResultBufferOffset];
306     DeviceString.Length = 0;
307     DeviceString.MaximumLength = (ResultBufferLength - ResultBufferOffset) * sizeof(WCHAR);
308 
309     // initialize source string
310     RtlInitAnsiString(&AnsiString, Buffer);
311 
312     Status = RtlAnsiStringToUnicodeString(&DeviceString, &AnsiString, FALSE);
313     ASSERT(Status == STATUS_SUCCESS);
314 
315     // subtract consumed bytes
316     ResultBufferLength -= (DeviceString.Length + sizeof(WCHAR)) / sizeof(WCHAR);
317     ResultBufferOffset += (DeviceString.Length + sizeof(WCHAR)) / sizeof(WCHAR);
318 
319     *NewResultBufferOffset = ResultBufferOffset;
320 }
321 
322 NTSTATUS
323 USBSTOR_PdoHandleQueryHardwareId(
324     IN PDEVICE_OBJECT DeviceObject,
325     IN OUT PIRP Irp)
326 {
327     PPDO_DEVICE_EXTENSION PDODeviceExtension;
328     PFDO_DEVICE_EXTENSION FDODeviceExtension;
329     LPCSTR GenericType, DeviceType;
330     LPWSTR Buffer;
331     CHAR Id1[50], Id2[50], Id3[50], Id4[50], Id5[50], Id6[50], Id7[50];
332     ULONG Id1Length, Id2Length, Id3Length, Id4Length, Id5Length, Id6Length, Id7Length;
333     ULONG Offset, TotalLength, Length;
334     PINQUIRYDATA InquiryData;
335 
336     PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
337     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension;
338     ASSERT(FDODeviceExtension->DeviceDescriptor);
339     InquiryData = PDODeviceExtension->InquiryData;
340 
341     DeviceType = USBSTOR_GetDeviceType(InquiryData, PDODeviceExtension->IsFloppy);
342     GenericType = USBSTOR_GetGenericType(InquiryData, PDODeviceExtension->IsFloppy);
343 
344     ASSERT(GenericType);
345 
346     // generate id 1
347     // USBSTOR\SCSIType_VendorId(8)_ProductId(16)_Revision(4)
348     RtlZeroMemory(Id1, sizeof(Id1));
349     Offset = 0;
350     Offset = sprintf(&Id1[Offset], "USBSTOR\\");
351     Offset += sprintf(&Id1[Offset], DeviceType);
352     Offset += CopyField(InquiryData->VendorId, &Id1[Offset], 8);
353     Offset += CopyField(InquiryData->ProductId, &Id1[Offset], 16);
354     Offset += CopyField(InquiryData->ProductRevisionLevel, &Id1[Offset], 4);
355     Id1Length = strlen(Id1) + 1;
356     DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId1 %s\n", Id1);
357 
358     // generate id 2
359     // USBSTOR\SCSIType_VendorId(8)_ProductId(16)
360     RtlZeroMemory(Id2, sizeof(Id2));
361     Offset = 0;
362     Offset = sprintf(&Id2[Offset], "USBSTOR\\");
363     Offset += sprintf(&Id2[Offset], DeviceType);
364     Offset += CopyField(InquiryData->VendorId, &Id2[Offset], 8);
365     Offset += CopyField(InquiryData->ProductId, &Id2[Offset], 16);
366     Id2Length = strlen(Id2) + 1;
367     DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId2 %s\n", Id2);
368 
369     // generate id 3
370     // USBSTOR\SCSIType_VendorId(8)
371     RtlZeroMemory(Id3, sizeof(Id3));
372     Offset = 0;
373     Offset = sprintf(&Id3[Offset], "USBSTOR\\");
374     Offset += sprintf(&Id3[Offset], DeviceType);
375     Offset += CopyField(InquiryData->VendorId, &Id3[Offset], 8);
376     Id3Length = strlen(Id3) + 1;
377     DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId3 %s\n", Id3);
378 
379     // generate id 4
380     // USBSTOR\SCSIType_VendorId(8)_ProductId(16)_Revision(1)
381     RtlZeroMemory(Id4, sizeof(Id4));
382     Offset = 0;
383     Offset = sprintf(&Id4[Offset], "USBSTOR\\");
384     Offset += sprintf(&Id4[Offset], DeviceType);
385     Offset += CopyField(InquiryData->VendorId, &Id4[Offset], 8);
386     Offset += CopyField(InquiryData->ProductId, &Id4[Offset], 16);
387     Offset += CopyField(InquiryData->ProductRevisionLevel, &Id4[Offset], 1);
388     Id4Length = strlen(Id4) + 1;
389     DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId4 %s\n", Id4);
390 
391     // generate id 5
392     // SCSIType_VendorId(8)_ProductId(16)_Revision(1)
393     RtlZeroMemory(Id5, sizeof(Id5));
394     Offset = 0;
395     Offset = sprintf(&Id5[Offset], DeviceType);
396     Offset += CopyField(InquiryData->VendorId, &Id5[Offset], 8);
397     Offset += CopyField(InquiryData->ProductId, &Id5[Offset], 16);
398     Offset += CopyField(InquiryData->ProductRevisionLevel, &Id5[Offset], 1);
399     Id5Length = strlen(Id5) + 1;
400     DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId5 %s\n", Id5);
401 
402     // generate id 6
403     // USBSTOR\SCSIType
404     RtlZeroMemory(Id6, sizeof(Id6));
405     Offset = 0;
406     Offset = sprintf(&Id6[Offset], "USBSTOR\\");
407     Offset += sprintf(&Id6[Offset], GenericType);
408     Id6Length = strlen(Id6) + 1;
409     DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId6 %s\n", Id6);
410 
411     // generate id 7
412     // SCSIType
413     RtlZeroMemory(Id7, sizeof(Id7));
414     Offset = 0;
415     Offset = sprintf(&Id7[Offset], GenericType);
416     Id7Length = strlen(Id7) + 1;
417     DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId7 %s\n", Id7);
418 
419     TotalLength = Id1Length + Id2Length + Id3Length + Id4Length + Id5Length + Id6Length + Id7Length + 1;
420 
421     Buffer = (LPWSTR)AllocateItem(PagedPool, TotalLength * sizeof(WCHAR));
422     if (!Buffer)
423     {
424         Irp->IoStatus.Information = 0;
425         return STATUS_INSUFFICIENT_RESOURCES;
426     }
427 
428     // reset offset
429     Offset = 0;
430     Length = TotalLength;
431 
432     USBSTOR_ConvertToUnicodeString(Id1, Length, Offset, Buffer, &Offset);
433     USBSTOR_ConvertToUnicodeString(Id2, Length, Offset, Buffer, &Offset);
434     USBSTOR_ConvertToUnicodeString(Id3, Length, Offset, Buffer, &Offset);
435     USBSTOR_ConvertToUnicodeString(Id4, Length, Offset, Buffer, &Offset);
436     USBSTOR_ConvertToUnicodeString(Id5, Length, Offset, Buffer, &Offset);
437     USBSTOR_ConvertToUnicodeString(Id6, Length, Offset, Buffer, &Offset);
438     USBSTOR_ConvertToUnicodeString(Id7, Length, Offset, Buffer, &Offset);
439 
440     ASSERT(Offset + 1 == Length);
441 
442     Irp->IoStatus.Information = (ULONG_PTR)Buffer;
443     return STATUS_SUCCESS;
444 }
445 
446 NTSTATUS
447 USBSTOR_PdoHandleQueryCompatibleId(
448     IN PDEVICE_OBJECT DeviceObject,
449     IN OUT PIRP Irp)
450 {
451     PPDO_DEVICE_EXTENSION PDODeviceExtension;
452     PFDO_DEVICE_EXTENSION FDODeviceExtension;
453     CHAR Buffer[100] = {0};
454     ULONG Length, Offset;
455     LPWSTR InstanceId;
456     LPCSTR DeviceType;
457 
458     PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
459     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension;
460     ASSERT(FDODeviceExtension->DeviceDescriptor);
461     DeviceType = USBSTOR_GetDeviceType(PDODeviceExtension->InquiryData, PDODeviceExtension->IsFloppy);
462 
463     // format instance id
464     Length = sprintf(Buffer, "USBSTOR\\%s", DeviceType) + 1;
465     Length += sprintf(&Buffer[Length], "USBSTOR\\%s", "RAW") + 2;
466 
467     InstanceId = (LPWSTR)AllocateItem(PagedPool, Length * sizeof(WCHAR));
468     if (!InstanceId)
469     {
470         Irp->IoStatus.Information = 0;
471         return STATUS_INSUFFICIENT_RESOURCES;
472     }
473 
474     USBSTOR_ConvertToUnicodeString(Buffer, Length, 0, InstanceId, &Offset);
475     USBSTOR_ConvertToUnicodeString(&Buffer[Offset], Length, Offset, InstanceId, &Offset);
476 
477     DPRINT("USBSTOR_PdoHandleQueryCompatibleId %S\n", InstanceId);
478 
479     Irp->IoStatus.Information = (ULONG_PTR)InstanceId;
480     return STATUS_SUCCESS;
481 }
482 
483 NTSTATUS
484 USBSTOR_PdoHandleQueryInstanceId(
485     IN PDEVICE_OBJECT DeviceObject,
486     IN OUT PIRP Irp)
487 {
488     PPDO_DEVICE_EXTENSION PDODeviceExtension;
489     PFDO_DEVICE_EXTENSION FDODeviceExtension;
490     WCHAR Buffer[100];
491     ULONG Length;
492     LPWSTR InstanceId;
493 
494     PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
495     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension;
496 
497     // format instance id
498     if (FDODeviceExtension->SerialNumber)
499     {
500         // using serial number from device
501         swprintf(Buffer, L"%s&%c", FDODeviceExtension->SerialNumber->bString, PDODeviceExtension->LUN);
502     }
503     else
504     {
505         // use instance count and LUN
506         swprintf(Buffer, L"%04lu&%c", FDODeviceExtension->InstanceCount, PDODeviceExtension->LUN);
507     }
508 
509     Length = wcslen(Buffer) + 1;
510 
511     InstanceId = (LPWSTR)AllocateItem(PagedPool, Length * sizeof(WCHAR));
512     if (!InstanceId)
513     {
514         Irp->IoStatus.Information = 0;
515         return STATUS_INSUFFICIENT_RESOURCES;
516     }
517 
518     wcscpy(InstanceId, Buffer);
519 
520     DPRINT("USBSTOR_PdoHandleQueryInstanceId %S\n", InstanceId);
521 
522     Irp->IoStatus.Information = (ULONG_PTR)InstanceId;
523     return STATUS_SUCCESS;
524 }
525 
526 NTSTATUS
527 USBSTOR_PdoHandleDeviceRelations(
528     IN PDEVICE_OBJECT DeviceObject,
529     IN OUT PIRP Irp)
530 {
531     PDEVICE_RELATIONS DeviceRelations;
532     PIO_STACK_LOCATION IoStack;
533 
534     DPRINT("USBSTOR_PdoHandleDeviceRelations\n");
535 
536     IoStack = IoGetCurrentIrpStackLocation(Irp);
537 
538     // check if relation type is BusRelations
539     if (IoStack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
540     {
541         // PDO handles only target device relation
542         return Irp->IoStatus.Status;
543     }
544 
545     DeviceRelations = (PDEVICE_RELATIONS)AllocateItem(PagedPool, sizeof(DEVICE_RELATIONS));
546     if (!DeviceRelations)
547     {
548         return STATUS_INSUFFICIENT_RESOURCES;
549     }
550 
551     // initialize device relations
552     DeviceRelations->Count = 1;
553     DeviceRelations->Objects[0] = DeviceObject;
554     ObReferenceObject(DeviceObject);
555 
556     Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
557     return STATUS_SUCCESS;
558 }
559 
560 NTSTATUS
561 USBSTOR_PdoHandlePnp(
562     IN PDEVICE_OBJECT DeviceObject,
563     IN OUT PIRP Irp)
564 {
565     PIO_STACK_LOCATION IoStack;
566     PPDO_DEVICE_EXTENSION DeviceExtension;
567     NTSTATUS Status;
568     PDEVICE_CAPABILITIES Caps;
569     ULONG bDelete;
570 
571     IoStack = IoGetCurrentIrpStackLocation(Irp);
572     DeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
573     ASSERT(DeviceExtension->Common.IsFDO == FALSE);
574 
575     switch(IoStack->MinorFunction)
576     {
577        case IRP_MN_QUERY_DEVICE_RELATIONS:
578        {
579            Status = USBSTOR_PdoHandleDeviceRelations(DeviceObject, Irp);
580            break;
581        }
582        case IRP_MN_QUERY_DEVICE_TEXT:
583        {
584            Status = USBSTOR_PdoHandleQueryDeviceText(DeviceObject, Irp);
585            break;
586        }
587        case IRP_MN_QUERY_ID:
588        {
589            if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID)
590            {
591                Status = USBSTOR_PdoHandleQueryDeviceId(DeviceObject, Irp);
592                break;
593            }
594            else if (IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs)
595            {
596                Status = USBSTOR_PdoHandleQueryHardwareId(DeviceObject, Irp);
597                break;
598            }
599            else if (IoStack->Parameters.QueryId.IdType == BusQueryInstanceID)
600            {
601                Status = USBSTOR_PdoHandleQueryInstanceId(DeviceObject, Irp);
602                break;
603            }
604            else if (IoStack->Parameters.QueryId.IdType == BusQueryCompatibleIDs)
605            {
606                Status = USBSTOR_PdoHandleQueryCompatibleId(DeviceObject, Irp);
607                break;
608            }
609 
610            DPRINT1("USBSTOR_PdoHandlePnp: IRP_MN_QUERY_ID IdType %x unimplemented\n", IoStack->Parameters.QueryId.IdType);
611            Status = STATUS_NOT_SUPPORTED;
612            Irp->IoStatus.Information = 0;
613            break;
614        }
615        case IRP_MN_REMOVE_DEVICE:
616        {
617            DPRINT("IRP_MN_REMOVE_DEVICE\n");
618 
619            if(*DeviceExtension->PDODeviceObject != NULL)
620            {
621                *DeviceExtension->PDODeviceObject = NULL;
622                bDelete = TRUE;
623            }
624            else
625            {
626                // device object already marked for deletion
627                bDelete = FALSE;
628            }
629 
630            // clean up the device extension
631            ASSERT(DeviceExtension->InquiryData);
632            ExFreePoolWithTag(DeviceExtension->InquiryData, USB_STOR_TAG);
633 
634            Irp->IoStatus.Status = STATUS_SUCCESS;
635            IoCompleteRequest(Irp, IO_NO_INCREMENT);
636 
637            if (bDelete)
638            {
639                IoDeleteDevice(DeviceObject);
640            }
641            return STATUS_SUCCESS;
642        }
643        case IRP_MN_QUERY_CAPABILITIES:
644        {
645            // just forward irp to lower device
646            Status = USBSTOR_SyncForwardIrp(DeviceExtension->LowerDeviceObject, Irp);
647            ASSERT(Status == STATUS_SUCCESS);
648 
649            if (NT_SUCCESS(Status))
650            {
651                // check if no unique id
652                Caps = (PDEVICE_CAPABILITIES)IoStack->Parameters.DeviceCapabilities.Capabilities;
653                Caps->UniqueID = FALSE; // no unique id is supported
654                Caps->Removable = TRUE; //FIXME
655            }
656            break;
657        }
658        case IRP_MN_QUERY_REMOVE_DEVICE:
659        case IRP_MN_QUERY_STOP_DEVICE:
660        {
661 #if 0
662            //
663            // if we're not claimed it's ok
664            //
665            if (DeviceExtension->Claimed)
666 #else
667            if (TRUE)
668 #endif
669            {
670                Status = STATUS_UNSUCCESSFUL;
671                DPRINT1("[USBSTOR] Request %x fails because device is still claimed\n", IoStack->MinorFunction);
672            }
673            else
674                Status = STATUS_SUCCESS;
675            break;
676        }
677        case IRP_MN_START_DEVICE:
678        {
679            // no-op for PDO
680            Status = STATUS_SUCCESS;
681            break;
682        }
683        case IRP_MN_SURPRISE_REMOVAL:
684        {
685            Status = STATUS_SUCCESS;
686            break;
687        }
688        default:
689         {
690             // do nothing
691             Status = Irp->IoStatus.Status;
692         }
693     }
694 
695     if (Status != STATUS_PENDING)
696     {
697         Irp->IoStatus.Status = Status;
698         IoCompleteRequest(Irp, IO_NO_INCREMENT);
699     }
700 
701     return Status;
702 }
703 
704 NTSTATUS
705 NTAPI
706 USBSTOR_SyncCompletionRoutine(
707     IN PDEVICE_OBJECT DeviceObject,
708     IN PIRP Irp,
709     IN PVOID Ctx)
710 {
711     KeSetEvent((PKEVENT)Ctx, IO_NO_INCREMENT, FALSE);
712     return STATUS_MORE_PROCESSING_REQUIRED;
713 }
714 
715 /*
716 * @name USBSTOR_SendInternalCdb
717 *
718 * Issues an internal SCSI request to device.
719 * The request is sent in a synchronous way.
720 */
721 static
722 NTSTATUS
723 USBSTOR_SendInternalCdb(
724     IN PDEVICE_OBJECT PdoDevice,
725     IN PCDB Cdb,
726     IN UCHAR CdbLength,
727     IN ULONG TimeOutValue,
728     OUT PVOID OutDataBuffer,
729     OUT PULONG OutDataTransferLength)
730 {
731     PSCSI_REQUEST_BLOCK Srb;
732     PSENSE_DATA SenseBuffer;
733     PIO_STACK_LOCATION IoStack;
734     KEVENT Event;
735     PIRP Irp = NULL;
736     PMDL Mdl = NULL;
737     ULONG ix = 0;
738     NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
739     UCHAR SrbStatus;
740 
741     DPRINT("USBSTOR_SendInternalCdb SCSIOP %x\n", Cdb->CDB6GENERIC.OperationCode);
742 
743     Srb = ExAllocatePoolWithTag(NonPagedPool,
744                                 sizeof(SCSI_REQUEST_BLOCK),
745                                 USB_STOR_TAG);
746 
747     if (Srb)
748     {
749         SenseBuffer = ExAllocatePoolWithTag(NonPagedPool,
750                                             SENSE_BUFFER_SIZE,
751                                             USB_STOR_TAG);
752 
753         if (SenseBuffer)
754         {
755             Mdl = IoAllocateMdl(OutDataBuffer,
756                     *OutDataTransferLength,
757                     FALSE,
758                     FALSE,
759                     NULL);
760 
761             if (!Mdl)
762             {
763                 ExFreePoolWithTag(SenseBuffer, USB_STOR_TAG);
764                 ExFreePoolWithTag(Srb, USB_STOR_TAG);
765                 return Status;
766             }
767 
768             MmBuildMdlForNonPagedPool(Mdl);
769 
770             // make 3 attempts - the device may be in STALL state after the first one
771             do
772             {
773                 Irp = IoAllocateIrp(PdoDevice->StackSize, FALSE);
774 
775                 if (!Irp)
776                 {
777                     break;
778                 }
779 
780                 IoStack = IoGetNextIrpStackLocation(Irp);
781                 IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
782                 IoStack->Parameters.Scsi.Srb = Srb;
783 
784                 RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
785 
786                 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
787                 Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
788                 Srb->CdbLength = CdbLength;
789                 Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
790                 Srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_NO_QUEUE_FREEZE;
791                 Srb->DataTransferLength = *OutDataTransferLength;
792                 Srb->TimeOutValue = TimeOutValue;
793                 Srb->DataBuffer = OutDataBuffer;
794                 Srb->SenseInfoBuffer = SenseBuffer;
795 
796                 RtlCopyMemory(Srb->Cdb, Cdb, CdbLength);
797 
798                 Irp->MdlAddress = Mdl;
799 
800                 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
801 
802                 IoSetCompletionRoutine(Irp,
803                                        USBSTOR_SyncCompletionRoutine,
804                                        &Event,
805                                        TRUE,
806                                        TRUE,
807                                        TRUE);
808 
809                 if (IoCallDriver(PdoDevice, Irp) == STATUS_PENDING)
810                 {
811                     KeWaitForSingleObject(&Event,
812                                           Executive,
813                                           KernelMode,
814                                           FALSE,
815                                           NULL);
816                 }
817 
818                 SrbStatus = SRB_STATUS(Srb->SrbStatus);
819 
820                 IoFreeIrp(Irp);
821                 Irp = NULL;
822 
823                 if (SrbStatus == SRB_STATUS_SUCCESS ||
824                      SrbStatus == SRB_STATUS_DATA_OVERRUN)
825                 {
826                     Status = STATUS_SUCCESS;
827                     *OutDataTransferLength = Srb->DataTransferLength;
828                     break;
829                 }
830 
831                 Status = STATUS_UNSUCCESSFUL;
832 
833                 ++ix;
834             } while (ix < 3);
835 
836             if (Mdl)
837             {
838                 IoFreeMdl(Mdl);
839             }
840 
841             ExFreePoolWithTag(SenseBuffer, USB_STOR_TAG);
842         }
843 
844         ExFreePoolWithTag(Srb, USB_STOR_TAG);
845     }
846 
847     return Status;
848 }
849 
850 /*
851 * @name USBSTOR_FillInquiryData
852 *
853 * Sends a SCSI Inquiry request and fills in the PDODeviceExtension->InquiryData field with a result.
854 */
855 static
856 NTSTATUS
857 USBSTOR_FillInquiryData(
858     IN PDEVICE_OBJECT PDODeviceObject)
859 {
860     NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
861     PPDO_DEVICE_EXTENSION PDODeviceExtension;
862     CDB Cdb;
863     ULONG DataTransferLength = INQUIRYDATABUFFERSIZE;
864     PINQUIRYDATA InquiryData;
865 
866     PDODeviceExtension = (PPDO_DEVICE_EXTENSION)PDODeviceObject->DeviceExtension;
867     InquiryData = ExAllocatePoolWithTag(NonPagedPool, INQUIRYDATABUFFERSIZE, USB_STOR_TAG);
868 
869     if (!InquiryData)
870     {
871         DPRINT1("USBSTOR_FillInquiryData failed with %x\n", Status);
872         return Status;
873     }
874 
875     RtlZeroMemory(&Cdb, sizeof(Cdb));
876     Cdb.CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
877     Cdb.CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;
878 
879     Status = USBSTOR_SendInternalCdb(PDODeviceObject, &Cdb, CDB6GENERIC_LENGTH, 20, InquiryData, &DataTransferLength);
880 
881     if (!NT_SUCCESS(Status))
882     {
883         DPRINT1("USBSTOR_FillInquiryData failed with %x\n", Status);
884         ExFreePoolWithTag(InquiryData, USB_STOR_TAG);
885         return Status;
886     }
887 
888     DPRINT("DeviceType %x\n", InquiryData->DeviceType);
889     DPRINT("DeviceTypeModifier %x\n", InquiryData->DeviceTypeModifier);
890     DPRINT("RemovableMedia %x\n", InquiryData->RemovableMedia);
891     DPRINT("Version %x\n", InquiryData->Versions);
892     DPRINT("Format %x\n", InquiryData->ResponseDataFormat);
893     DPRINT("Length %x\n", InquiryData->AdditionalLength);
894     DPRINT("Reserved %p\n", InquiryData->Reserved);
895     DPRINT("VendorId %c%c%c%c%c%c%c%c\n", InquiryData->VendorId[0], InquiryData->VendorId[1], InquiryData->VendorId[2], InquiryData->VendorId[3], InquiryData->VendorId[4], InquiryData->VendorId[5], InquiryData->VendorId[6], InquiryData->VendorId[7]);
896     DPRINT("ProductId %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", InquiryData->ProductId[0], InquiryData->ProductId[1], InquiryData->ProductId[2], InquiryData->ProductId[3],
897                                                            InquiryData->ProductId[4], InquiryData->ProductId[5], InquiryData->ProductId[6], InquiryData->ProductId[7],
898                                                            InquiryData->ProductId[8], InquiryData->ProductId[9], InquiryData->ProductId[10], InquiryData->ProductId[11],
899                                                            InquiryData->ProductId[12], InquiryData->ProductId[13], InquiryData->ProductId[14], InquiryData->ProductId[15]);
900 
901     DPRINT("Revision %c%c%c%c\n", InquiryData->ProductRevisionLevel[0], InquiryData->ProductRevisionLevel[1], InquiryData->ProductRevisionLevel[2], InquiryData->ProductRevisionLevel[3]);
902 
903     PDODeviceExtension->InquiryData = InquiryData;
904     return Status;
905 }
906 
907 NTSTATUS
908 USBSTOR_CreatePDO(
909     IN PDEVICE_OBJECT DeviceObject,
910     IN UCHAR LUN)
911 {
912     PDEVICE_OBJECT PDO;
913     NTSTATUS Status;
914     PPDO_DEVICE_EXTENSION PDODeviceExtension;
915     PFDO_DEVICE_EXTENSION FDODeviceExtension;
916 
917     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
918 
919     // create child device object
920     Status = IoCreateDevice(DeviceObject->DriverObject, sizeof(PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_MASS_STORAGE, FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN, FALSE, &PDO);
921     if (!NT_SUCCESS(Status))
922     {
923         DPRINT1("Failed to create PDO, status %x\n", Status);
924         return Status;
925     }
926 
927     // patch the stack size
928     PDO->StackSize = DeviceObject->StackSize;
929 
930     PDODeviceExtension = (PPDO_DEVICE_EXTENSION)PDO->DeviceExtension;
931 
932     // initialize device extension
933     RtlZeroMemory(PDODeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
934     PDODeviceExtension->Common.IsFDO = FALSE;
935     PDODeviceExtension->LowerDeviceObject = DeviceObject;
936     PDODeviceExtension->PDODeviceObject = &FDODeviceExtension->ChildPDO[LUN];
937     PDODeviceExtension->Self = PDO;
938     PDODeviceExtension->LUN = LUN;
939 
940     PDO->Flags |= DO_DIRECT_IO;
941 
942     // device is initialized
943     PDO->Flags &= ~DO_DEVICE_INITIALIZING;
944 
945     // output device object
946     FDODeviceExtension->ChildPDO[LUN] = PDO;
947 
948     // send inquiry command by irp
949     Status = USBSTOR_FillInquiryData(PDO);
950 
951     if (!NT_SUCCESS(Status))
952     {
953         return Status;
954     }
955 
956     if (PDODeviceExtension->InquiryData->DeviceType == DIRECT_ACCESS_DEVICE || PDODeviceExtension->InquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE)
957     {
958         PDODeviceExtension->IsFloppy = FALSE; // TODO: implement the actual check
959     }
960     else
961     {
962         // we work only with DIRECT_ACCESS_DEVICE for now
963         return STATUS_NOT_SUPPORTED;
964     }
965 
966     return Status;
967 }
968