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