xref: /reactos/drivers/usb/usbstor/pdo.c (revision b8dd046e)
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];
332     ULONG Id1Length, Id2Length, Id3Length, Id4Length, Id5Length,Id6Length;
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     // USBSTOR\SCSIType
393     RtlZeroMemory(Id5, sizeof(Id5));
394     Offset = 0;
395     Offset = sprintf(&Id5[Offset], "USBSTOR\\");
396     Offset += sprintf(&Id5[Offset], GenericType);
397     Id5Length = strlen(Id5) + 1;
398     DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId5 %s\n", Id5);
399 
400     // generate id 6
401     // SCSIType
402     RtlZeroMemory(Id6, sizeof(Id6));
403     Offset = 0;
404     Offset = sprintf(&Id6[Offset], GenericType);
405     Id6Length = strlen(Id6) + 1;
406     DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId6 %s\n", Id6);
407 
408     TotalLength = Id1Length + Id2Length + Id3Length + Id4Length + Id5Length + Id6Length + 1;
409 
410     Buffer = (LPWSTR)AllocateItem(PagedPool, TotalLength * sizeof(WCHAR));
411     if (!Buffer)
412     {
413         Irp->IoStatus.Information = 0;
414         return STATUS_INSUFFICIENT_RESOURCES;
415     }
416 
417     // reset offset
418     Offset = 0;
419     Length = TotalLength;
420 
421     USBSTOR_ConvertToUnicodeString(Id1, Length, Offset, Buffer, &Offset);
422     USBSTOR_ConvertToUnicodeString(Id2, Length, Offset, Buffer, &Offset);
423     USBSTOR_ConvertToUnicodeString(Id3, Length, Offset, Buffer, &Offset);
424     USBSTOR_ConvertToUnicodeString(Id4, Length, Offset, Buffer, &Offset);
425     USBSTOR_ConvertToUnicodeString(Id5, Length, Offset, Buffer, &Offset);
426     USBSTOR_ConvertToUnicodeString(Id6, Length, Offset, Buffer, &Offset);
427 
428     ASSERT(Offset + 1 == Length);
429 
430     Irp->IoStatus.Information = (ULONG_PTR)Buffer;
431     return STATUS_SUCCESS;
432 }
433 
434 NTSTATUS
435 USBSTOR_PdoHandleQueryCompatibleId(
436     IN PDEVICE_OBJECT DeviceObject,
437     IN OUT PIRP Irp)
438 {
439     PPDO_DEVICE_EXTENSION PDODeviceExtension;
440     PFDO_DEVICE_EXTENSION FDODeviceExtension;
441     CHAR Buffer[100] = {0};
442     ULONG Length, Offset;
443     LPWSTR InstanceId;
444     LPCSTR DeviceType;
445 
446     PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
447     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension;
448     ASSERT(FDODeviceExtension->DeviceDescriptor);
449     DeviceType = USBSTOR_GetDeviceType(PDODeviceExtension->InquiryData, PDODeviceExtension->IsFloppy);
450 
451     // format instance id
452     Length = sprintf(Buffer, "USBSTOR\\%s", DeviceType) + 1;
453     Length += sprintf(&Buffer[Length], "USBSTOR\\%s", "RAW") + 2;
454 
455     InstanceId = (LPWSTR)AllocateItem(PagedPool, Length * sizeof(WCHAR));
456     if (!InstanceId)
457     {
458         Irp->IoStatus.Information = 0;
459         return STATUS_INSUFFICIENT_RESOURCES;
460     }
461 
462     USBSTOR_ConvertToUnicodeString(Buffer, Length, 0, InstanceId, &Offset);
463     USBSTOR_ConvertToUnicodeString(&Buffer[Offset], Length, Offset, InstanceId, &Offset);
464 
465     DPRINT("USBSTOR_PdoHandleQueryCompatibleId %S\n", InstanceId);
466 
467     Irp->IoStatus.Information = (ULONG_PTR)InstanceId;
468     return STATUS_SUCCESS;
469 }
470 
471 NTSTATUS
472 USBSTOR_PdoHandleQueryInstanceId(
473     IN PDEVICE_OBJECT DeviceObject,
474     IN OUT PIRP Irp)
475 {
476     PPDO_DEVICE_EXTENSION PDODeviceExtension;
477     PFDO_DEVICE_EXTENSION FDODeviceExtension;
478     WCHAR Buffer[100];
479     ULONG Length;
480     LPWSTR InstanceId;
481 
482     PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
483     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension;
484 
485     // format instance id
486     if (FDODeviceExtension->SerialNumber)
487     {
488         // using serial number from device
489         swprintf(Buffer, L"%s&%c", FDODeviceExtension->SerialNumber->bString, PDODeviceExtension->LUN);
490     }
491     else
492     {
493         // use instance count and LUN
494         swprintf(Buffer, L"%04lu&%c", FDODeviceExtension->InstanceCount, PDODeviceExtension->LUN);
495     }
496 
497     Length = wcslen(Buffer) + 1;
498 
499     InstanceId = (LPWSTR)AllocateItem(PagedPool, Length * sizeof(WCHAR));
500     if (!InstanceId)
501     {
502         Irp->IoStatus.Information = 0;
503         return STATUS_INSUFFICIENT_RESOURCES;
504     }
505 
506     wcscpy(InstanceId, Buffer);
507 
508     DPRINT("USBSTOR_PdoHandleQueryInstanceId %S\n", InstanceId);
509 
510     Irp->IoStatus.Information = (ULONG_PTR)InstanceId;
511     return STATUS_SUCCESS;
512 }
513 
514 NTSTATUS
515 USBSTOR_PdoHandleDeviceRelations(
516     IN PDEVICE_OBJECT DeviceObject,
517     IN OUT PIRP Irp)
518 {
519     PDEVICE_RELATIONS DeviceRelations;
520     PIO_STACK_LOCATION IoStack;
521 
522     DPRINT("USBSTOR_PdoHandleDeviceRelations\n");
523 
524     IoStack = IoGetCurrentIrpStackLocation(Irp);
525 
526     // check if relation type is BusRelations
527     if (IoStack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
528     {
529         // PDO handles only target device relation
530         return Irp->IoStatus.Status;
531     }
532 
533     DeviceRelations = (PDEVICE_RELATIONS)AllocateItem(PagedPool, sizeof(DEVICE_RELATIONS));
534     if (!DeviceRelations)
535     {
536         return STATUS_INSUFFICIENT_RESOURCES;
537     }
538 
539     // initialize device relations
540     DeviceRelations->Count = 1;
541     DeviceRelations->Objects[0] = DeviceObject;
542     ObReferenceObject(DeviceObject);
543 
544     Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
545     return STATUS_SUCCESS;
546 }
547 
548 NTSTATUS
549 USBSTOR_PdoHandlePnp(
550     IN PDEVICE_OBJECT DeviceObject,
551     IN OUT PIRP Irp)
552 {
553     PIO_STACK_LOCATION IoStack;
554     PPDO_DEVICE_EXTENSION DeviceExtension;
555     NTSTATUS Status;
556     PDEVICE_CAPABILITIES Caps;
557     ULONG bDelete;
558 
559     IoStack = IoGetCurrentIrpStackLocation(Irp);
560     DeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
561     ASSERT(DeviceExtension->Common.IsFDO == FALSE);
562 
563     switch(IoStack->MinorFunction)
564     {
565        case IRP_MN_QUERY_DEVICE_RELATIONS:
566        {
567            Status = USBSTOR_PdoHandleDeviceRelations(DeviceObject, Irp);
568            break;
569        }
570        case IRP_MN_QUERY_DEVICE_TEXT:
571        {
572            Status = USBSTOR_PdoHandleQueryDeviceText(DeviceObject, Irp);
573            break;
574        }
575        case IRP_MN_QUERY_ID:
576        {
577            if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID)
578            {
579                Status = USBSTOR_PdoHandleQueryDeviceId(DeviceObject, Irp);
580                break;
581            }
582            else if (IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs)
583            {
584                Status = USBSTOR_PdoHandleQueryHardwareId(DeviceObject, Irp);
585                break;
586            }
587            else if (IoStack->Parameters.QueryId.IdType == BusQueryInstanceID)
588            {
589                Status = USBSTOR_PdoHandleQueryInstanceId(DeviceObject, Irp);
590                break;
591            }
592            else if (IoStack->Parameters.QueryId.IdType == BusQueryCompatibleIDs)
593            {
594                Status = USBSTOR_PdoHandleQueryCompatibleId(DeviceObject, Irp);
595                break;
596            }
597 
598            DPRINT1("USBSTOR_PdoHandlePnp: IRP_MN_QUERY_ID IdType %x unimplemented\n", IoStack->Parameters.QueryId.IdType);
599            Status = STATUS_NOT_SUPPORTED;
600            Irp->IoStatus.Information = 0;
601            break;
602        }
603        case IRP_MN_REMOVE_DEVICE:
604        {
605            DPRINT("IRP_MN_REMOVE_DEVICE\n");
606 
607            if(*DeviceExtension->PDODeviceObject != NULL)
608            {
609                *DeviceExtension->PDODeviceObject = NULL;
610                bDelete = TRUE;
611            }
612            else
613            {
614                // device object already marked for deletion
615                bDelete = FALSE;
616            }
617 
618            // clean up the device extension
619            ASSERT(DeviceExtension->InquiryData);
620            ExFreePoolWithTag(DeviceExtension->InquiryData, USB_STOR_TAG);
621 
622            Irp->IoStatus.Status = STATUS_SUCCESS;
623            IoCompleteRequest(Irp, IO_NO_INCREMENT);
624 
625            if (bDelete)
626            {
627                IoDeleteDevice(DeviceObject);
628            }
629            return STATUS_SUCCESS;
630        }
631        case IRP_MN_QUERY_CAPABILITIES:
632        {
633            // just forward irp to lower device
634            Status = USBSTOR_SyncForwardIrp(DeviceExtension->LowerDeviceObject, Irp);
635            ASSERT(Status == STATUS_SUCCESS);
636 
637            if (NT_SUCCESS(Status))
638            {
639                // check if no unique id
640                Caps = (PDEVICE_CAPABILITIES)IoStack->Parameters.DeviceCapabilities.Capabilities;
641                Caps->UniqueID = FALSE; // no unique id is supported
642                Caps->Removable = TRUE; //FIXME
643            }
644            break;
645        }
646        case IRP_MN_QUERY_REMOVE_DEVICE:
647        case IRP_MN_QUERY_STOP_DEVICE:
648        {
649 #if 0
650            //
651            // if we're not claimed it's ok
652            //
653            if (DeviceExtension->Claimed)
654 #else
655            if (TRUE)
656 #endif
657            {
658                Status = STATUS_UNSUCCESSFUL;
659                DPRINT1("[USBSTOR] Request %x fails because device is still claimed\n", IoStack->MinorFunction);
660            }
661            else
662                Status = STATUS_SUCCESS;
663            break;
664        }
665        case IRP_MN_START_DEVICE:
666        {
667            // no-op for PDO
668            Status = STATUS_SUCCESS;
669            break;
670        }
671        case IRP_MN_SURPRISE_REMOVAL:
672        {
673            Status = STATUS_SUCCESS;
674            break;
675        }
676        default:
677         {
678             // do nothing
679             Status = Irp->IoStatus.Status;
680         }
681     }
682 
683     if (Status != STATUS_PENDING)
684     {
685         Irp->IoStatus.Status = Status;
686         IoCompleteRequest(Irp, IO_NO_INCREMENT);
687     }
688 
689     return Status;
690 }
691 
692 NTSTATUS
693 NTAPI
694 USBSTOR_SyncCompletionRoutine(
695     IN PDEVICE_OBJECT DeviceObject,
696     IN PIRP Irp,
697     IN PVOID Ctx)
698 {
699     KeSetEvent((PKEVENT)Ctx, IO_NO_INCREMENT, FALSE);
700     return STATUS_MORE_PROCESSING_REQUIRED;
701 }
702 
703 /*
704 * @name USBSTOR_SendInternalCdb
705 *
706 * Issues an internal SCSI request to device.
707 * The request is sent in a synchronous way.
708 */
709 static
710 NTSTATUS
711 USBSTOR_SendInternalCdb(
712     IN PDEVICE_OBJECT PdoDevice,
713     IN PCDB Cdb,
714     IN UCHAR CdbLength,
715     IN ULONG TimeOutValue,
716     OUT PVOID OutDataBuffer,
717     OUT PULONG OutDataTransferLength)
718 {
719     PSCSI_REQUEST_BLOCK Srb;
720     PSENSE_DATA SenseBuffer;
721     PIO_STACK_LOCATION IoStack;
722     KEVENT Event;
723     PIRP Irp = NULL;
724     PMDL Mdl = NULL;
725     ULONG ix = 0;
726     NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
727     UCHAR SrbStatus;
728 
729     DPRINT("USBSTOR_SendInternalCdb SCSIOP %x\n", Cdb->CDB6GENERIC.OperationCode);
730 
731     Srb = ExAllocatePoolWithTag(NonPagedPool,
732                                 sizeof(SCSI_REQUEST_BLOCK),
733                                 USB_STOR_TAG);
734 
735     if (Srb)
736     {
737         SenseBuffer = ExAllocatePoolWithTag(NonPagedPool,
738                                             SENSE_BUFFER_SIZE,
739                                             USB_STOR_TAG);
740 
741         if (SenseBuffer)
742         {
743             Mdl = IoAllocateMdl(OutDataBuffer,
744                     *OutDataTransferLength,
745                     FALSE,
746                     FALSE,
747                     NULL);
748 
749             if (!Mdl)
750             {
751                 ExFreePoolWithTag(SenseBuffer, USB_STOR_TAG);
752                 ExFreePoolWithTag(Srb, USB_STOR_TAG);
753                 return Status;
754             }
755 
756             MmBuildMdlForNonPagedPool(Mdl);
757 
758             // make 3 attempts - the device may be in STALL state after the first one
759             do
760             {
761                 Irp = IoAllocateIrp(PdoDevice->StackSize, FALSE);
762 
763                 if (!Irp)
764                 {
765                     break;
766                 }
767 
768                 IoStack = IoGetNextIrpStackLocation(Irp);
769                 IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
770                 IoStack->Parameters.Scsi.Srb = Srb;
771 
772                 RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
773 
774                 Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
775                 Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
776                 Srb->CdbLength = CdbLength;
777                 Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
778                 Srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_NO_QUEUE_FREEZE;
779                 Srb->DataTransferLength = *OutDataTransferLength;
780                 Srb->TimeOutValue = TimeOutValue;
781                 Srb->DataBuffer = OutDataBuffer;
782                 Srb->SenseInfoBuffer = SenseBuffer;
783 
784                 RtlCopyMemory(Srb->Cdb, Cdb, CdbLength);
785 
786                 Irp->MdlAddress = Mdl;
787 
788                 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
789 
790                 IoSetCompletionRoutine(Irp,
791                                        USBSTOR_SyncCompletionRoutine,
792                                        &Event,
793                                        TRUE,
794                                        TRUE,
795                                        TRUE);
796 
797                 if (IoCallDriver(PdoDevice, Irp) == STATUS_PENDING)
798                 {
799                     KeWaitForSingleObject(&Event,
800                                           Executive,
801                                           KernelMode,
802                                           FALSE,
803                                           NULL);
804                 }
805 
806                 SrbStatus = SRB_STATUS(Srb->SrbStatus);
807 
808                 IoFreeIrp(Irp);
809                 Irp = NULL;
810 
811                 if (SrbStatus == SRB_STATUS_SUCCESS ||
812                      SrbStatus == SRB_STATUS_DATA_OVERRUN)
813                 {
814                     Status = STATUS_SUCCESS;
815                     *OutDataTransferLength = Srb->DataTransferLength;
816                     break;
817                 }
818 
819                 Status = STATUS_UNSUCCESSFUL;
820 
821                 ++ix;
822             } while (ix < 3);
823 
824             if (Mdl)
825             {
826                 IoFreeMdl(Mdl);
827             }
828 
829             ExFreePoolWithTag(SenseBuffer, USB_STOR_TAG);
830         }
831 
832         ExFreePoolWithTag(Srb, USB_STOR_TAG);
833     }
834 
835     return Status;
836 }
837 
838 /*
839 * @name USBSTOR_FillInquiryData
840 *
841 * Sends a SCSI Inquiry request and fills in the PDODeviceExtension->InquiryData field with a result.
842 */
843 static
844 NTSTATUS
845 USBSTOR_FillInquiryData(
846     IN PDEVICE_OBJECT PDODeviceObject)
847 {
848     NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
849     PPDO_DEVICE_EXTENSION PDODeviceExtension;
850     CDB Cdb;
851     ULONG DataTransferLength = INQUIRYDATABUFFERSIZE;
852     PINQUIRYDATA InquiryData;
853 
854     PDODeviceExtension = (PPDO_DEVICE_EXTENSION)PDODeviceObject->DeviceExtension;
855     InquiryData = ExAllocatePoolWithTag(NonPagedPool, INQUIRYDATABUFFERSIZE, USB_STOR_TAG);
856 
857     if (!InquiryData)
858     {
859         DPRINT1("USBSTOR_FillInquiryData failed with %x\n", Status);
860         return Status;
861     }
862 
863     RtlZeroMemory(&Cdb, sizeof(Cdb));
864     Cdb.CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
865     Cdb.CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;
866 
867     Status = USBSTOR_SendInternalCdb(PDODeviceObject, &Cdb, CDB6GENERIC_LENGTH, 20, InquiryData, &DataTransferLength);
868 
869     if (!NT_SUCCESS(Status))
870     {
871         DPRINT1("USBSTOR_FillInquiryData failed with %x\n", Status);
872         ExFreePoolWithTag(InquiryData, USB_STOR_TAG);
873         return Status;
874     }
875 
876     DPRINT("DeviceType %x\n", InquiryData->DeviceType);
877     DPRINT("DeviceTypeModifier %x\n", InquiryData->DeviceTypeModifier);
878     DPRINT("RemovableMedia %x\n", InquiryData->RemovableMedia);
879     DPRINT("Version %x\n", InquiryData->Versions);
880     DPRINT("Format %x\n", InquiryData->ResponseDataFormat);
881     DPRINT("Length %x\n", InquiryData->AdditionalLength);
882     DPRINT("Reserved %p\n", InquiryData->Reserved);
883     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]);
884     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],
885                                                            InquiryData->ProductId[4], InquiryData->ProductId[5], InquiryData->ProductId[6], InquiryData->ProductId[7],
886                                                            InquiryData->ProductId[8], InquiryData->ProductId[9], InquiryData->ProductId[10], InquiryData->ProductId[11],
887                                                            InquiryData->ProductId[12], InquiryData->ProductId[13], InquiryData->ProductId[14], InquiryData->ProductId[15]);
888 
889     DPRINT("Revision %c%c%c%c\n", InquiryData->ProductRevisionLevel[0], InquiryData->ProductRevisionLevel[1], InquiryData->ProductRevisionLevel[2], InquiryData->ProductRevisionLevel[3]);
890 
891     PDODeviceExtension->InquiryData = InquiryData;
892     return Status;
893 }
894 
895 NTSTATUS
896 USBSTOR_CreatePDO(
897     IN PDEVICE_OBJECT DeviceObject,
898     IN UCHAR LUN)
899 {
900     PDEVICE_OBJECT PDO;
901     NTSTATUS Status;
902     PPDO_DEVICE_EXTENSION PDODeviceExtension;
903     PFDO_DEVICE_EXTENSION FDODeviceExtension;
904 
905     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
906 
907     // create child device object
908     Status = IoCreateDevice(DeviceObject->DriverObject, sizeof(PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_MASS_STORAGE, FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN, FALSE, &PDO);
909     if (!NT_SUCCESS(Status))
910     {
911         DPRINT1("Failed to create PDO, status %x\n", Status);
912         return Status;
913     }
914 
915     // patch the stack size
916     PDO->StackSize = DeviceObject->StackSize;
917 
918     PDODeviceExtension = (PPDO_DEVICE_EXTENSION)PDO->DeviceExtension;
919 
920     // initialize device extension
921     RtlZeroMemory(PDODeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
922     PDODeviceExtension->Common.IsFDO = FALSE;
923     PDODeviceExtension->LowerDeviceObject = DeviceObject;
924     PDODeviceExtension->PDODeviceObject = &FDODeviceExtension->ChildPDO[LUN];
925     PDODeviceExtension->Self = PDO;
926     PDODeviceExtension->LUN = LUN;
927 
928     PDO->Flags |= DO_DIRECT_IO;
929 
930     // device is initialized
931     PDO->Flags &= ~DO_DEVICE_INITIALIZING;
932 
933     // output device object
934     FDODeviceExtension->ChildPDO[LUN] = PDO;
935 
936     // send inquiry command by irp
937     Status = USBSTOR_FillInquiryData(PDO);
938 
939     if (!NT_SUCCESS(Status))
940     {
941         return Status;
942     }
943 
944     if (PDODeviceExtension->InquiryData->DeviceType == DIRECT_ACCESS_DEVICE || PDODeviceExtension->InquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE)
945     {
946         PDODeviceExtension->IsFloppy = FALSE; // TODO: implement the actual check
947     }
948     else
949     {
950         // we work only with DIRECT_ACCESS_DEVICE for now
951         return STATUS_NOT_SUPPORTED;
952     }
953 
954     return Status;
955 }
956