diff --git a/reactos/base/services/umpnpmgr/umpnpmgr.c b/reactos/base/services/umpnpmgr/umpnpmgr.c index 17b2a37a982..2a5697315d9 100644 --- a/reactos/base/services/umpnpmgr/umpnpmgr.c +++ b/reactos/base/services/umpnpmgr/umpnpmgr.c @@ -2811,7 +2811,7 @@ InstallDevice(PCWSTR DeviceInstance, BOOL ShowWizard) &DeviceKey) == ERROR_SUCCESS) { if (RegQueryValueExW(DeviceKey, - L"ClassGUID", + L"Class", NULL, NULL, NULL, diff --git a/reactos/base/setup/usetup/interface/devinst.c b/reactos/base/setup/usetup/interface/devinst.c index 0f3f1ed7fed..3c1e43d2696 100644 --- a/reactos/base/setup/usetup/interface/devinst.c +++ b/reactos/base/setup/usetup/interface/devinst.c @@ -63,6 +63,7 @@ InstallDriver( if (!SetupFindFirstLineW(hInf, L"BootBusExtenders.Load", Driver, &Context) && !SetupFindFirstLineW(hInf, L"BusExtenders.Load", Driver, &Context) && !SetupFindFirstLineW(hInf, L"SCSI.Load", Driver, &Context) + && !SetupFindFirstLineW(hInf, L"InputDevicesSupport.Load", Driver, &Context) && !SetupFindFirstLineW(hInf, L"Keyboard.Load", Driver, &Context)) return FALSE; if (!INF_GetDataField(&Context, 1, &ImagePath)) diff --git a/reactos/base/setup/usetup/interface/usetup.c b/reactos/base/setup/usetup/interface/usetup.c index 984d287b299..9b49f89d328 100644 --- a/reactos/base/setup/usetup/interface/usetup.c +++ b/reactos/base/setup/usetup/interface/usetup.c @@ -728,19 +728,19 @@ LanguagePage(PINPUT_RECORD Ir) static PAGE_NUMBER SetupStartPage(PINPUT_RECORD Ir) { - SYSTEM_DEVICE_INFORMATION Sdi; + //SYSTEM_DEVICE_INFORMATION Sdi; NTSTATUS Status; WCHAR FileNameBuffer[MAX_PATH]; INFCONTEXT Context; PWCHAR Value; UINT ErrorLine; - ULONG ReturnSize; + //ULONG ReturnSize; PGENERIC_LIST_ENTRY ListEntry; INT IntValue; CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT)); - +#if 0 /* Check whether a harddisk is available */ Status = NtQuerySystemInformation(SystemDeviceInformation, &Sdi, @@ -759,6 +759,7 @@ SetupStartPage(PINPUT_RECORD Ir) MUIDisplayError(ERROR_NO_HDD, Ir, POPUP_WAIT_ENTER); return QUIT_PAGE; } +#endif /* Get the source path and source root path */ Status = GetSourcePaths(&SourcePath, @@ -1465,8 +1466,6 @@ SelectPartitionPage(PINPUT_RECORD Ir) } } - CheckActiveBootPartition(PartitionList); - DrawPartitionList(PartitionList); /* Warn about partitions created by Linux Fdisk */ @@ -2381,8 +2380,6 @@ FormatPartitionPage(PINPUT_RECORD Ir) else if (!FileSystemList->Selected->FormatFunc) return QUIT_PAGE; - CheckActiveBootPartition(PartitionList); - #ifndef NDEBUG CONSOLE_PrintTextXY(6, 12, "Disk: %I64u Cylinder: %I64u Track: %I64u", @@ -2442,19 +2439,6 @@ FormatPartitionPage(PINPUT_RECORD Ir) PathBuffer); DPRINT("DestinationRootPath: %wZ\n", &DestinationRootPath); - - /* Set SystemRootPath */ - RtlFreeUnicodeString(&SystemRootPath); - swprintf(PathBuffer, - L"\\Device\\Harddisk%lu\\Partition%lu", - PartitionList->ActiveBootDisk->DiskNumber, - PartitionList->ActiveBootPartition-> - PartInfo[PartitionList->ActiveBootPartitionNumber].PartitionNumber); - RtlCreateUnicodeString(&SystemRootPath, - PathBuffer); - DPRINT("SystemRootPath: %wZ\n", &SystemRootPath); - - if (FileSystemList->Selected->FormatFunc) { Status = FormatPartition(&DestinationRootPath, @@ -2505,15 +2489,6 @@ CheckFileSystemPage(PINPUT_RECORD Ir) RtlCreateUnicodeString(&DestinationRootPath, PathBuffer); DPRINT("DestinationRootPath: %wZ\n", &DestinationRootPath); - /* Set SystemRootPath */ - RtlFreeUnicodeString(&SystemRootPath); - swprintf(PathBuffer, - L"\\Device\\Harddisk%lu\\Partition%lu", - PartitionList->ActiveBootDisk->DiskNumber, - PartitionList->ActiveBootPartition->PartInfo[PartNum].PartitionNumber); - RtlCreateUnicodeString(&SystemRootPath, PathBuffer); - DPRINT("SystemRootPath: %wZ\n", &SystemRootPath); - CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_CHECKINGPART)); CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT)); @@ -3371,9 +3346,31 @@ BootLoaderPage(PINPUT_RECORD Ir) UCHAR PartitionType; BOOLEAN InstallOnFloppy; USHORT Line = 12; + WCHAR PathBuffer[MAX_PATH]; CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT)); + /* Find or set the active partition */ + CheckActiveBootPartition(PartitionList); + + /* Update the partition table because we may have changed the active partition */ + if (WritePartitionsToDisk(PartitionList) == FALSE) + { + DPRINT("WritePartitionsToDisk() failed\n"); + MUIDisplayError(ERROR_WRITE_PTABLE, Ir, POPUP_WAIT_ENTER); + return QUIT_PAGE; + } + + RtlFreeUnicodeString(&SystemRootPath); + swprintf(PathBuffer, + L"\\Device\\Harddisk%lu\\Partition%lu", + PartitionList->ActiveBootDisk->DiskNumber, + PartitionList->ActiveBootPartition-> + PartInfo[PartitionList->ActiveBootPartitionNumber].PartitionNumber); + RtlCreateUnicodeString(&SystemRootPath, + PathBuffer); + DPRINT("SystemRootPath: %wZ\n", &SystemRootPath); + PartitionType = PartitionList->ActiveBootPartition-> PartInfo[PartitionList->ActiveBootPartitionNumber].PartitionType; diff --git a/reactos/base/setup/usetup/partlist.c b/reactos/base/setup/usetup/partlist.c index 0fd6fbb1997..3f8a98ab3dd 100644 --- a/reactos/base/setup/usetup/partlist.c +++ b/reactos/base/setup/usetup/partlist.c @@ -838,8 +838,12 @@ AddDiskToList (HANDLE FileHandle, if (!DiskEntry->BiosFound) { +#if 0 RtlFreeHeap(ProcessHeap, 0, DiskEntry); return; +#else + DPRINT1("WARNING: Setup could not find a matching BIOS disk entry. Disk %d is not be bootable by the BIOS!\n", DiskNumber); +#endif } InitializeListHead (&DiskEntry->PartListHead); @@ -871,7 +875,7 @@ AddDiskToList (HANDLE FileHandle, GetDriverName (DiskEntry); - InsertAscendingList(&List->DiskListHead, DiskEntry, DISKENTRY, ListEntry, BiosDiskNumber); + InsertAscendingList(&List->DiskListHead, DiskEntry, DISKENTRY, ListEntry, DiskNumber); /* * Allocate a buffer for 26 logical drives (2 entries each == 52) @@ -2373,9 +2377,8 @@ CheckActiveBootPartition (PPARTLIST List) } #endif - DiskEntry = CONTAINING_RECORD (List->DiskListHead.Flink, - DISKENTRY, - ListEntry); + /* Choose the currently selected disk */ + DiskEntry = List->CurrentDisk; /* Check for empty partition list */ if (IsListEmpty (&DiskEntry->PartListHead)) @@ -2436,7 +2439,7 @@ CheckActiveBootPartition (PPARTLIST List) List->ActiveBootPartitionNumber = i; DPRINT("Found bootable partition disk %d, drive letter %c\n", - DiskEntry->BiosDiskNumber, PartEntry->DriveLetter[i]); + DiskEntry->DiskNumber, PartEntry->DriveLetter[i]); break; } diff --git a/reactos/boot/bootdata/hivesys_amd64.inf b/reactos/boot/bootdata/hivesys_amd64.inf index 5781b805936..d6b654a479c 100644 --- a/reactos/boot/bootdata/hivesys_amd64.inf +++ b/reactos/boot/bootdata/hivesys_amd64.inf @@ -1398,20 +1398,6 @@ HKLM,"SYSTEM\CurrentControlSet\Hardware Profiles\Current\System\CurrentControlSe HKLM,"SYSTEM\CurrentControlSet\Hardware Profiles\Current\System\CurrentControlSet\Services\Vga\Device0","DefaultSettings.XResolution",0x00010001,640 HKLM,"SYSTEM\CurrentControlSet\Hardware Profiles\Current\System\CurrentControlSet\Services\Vga\Device0","DefaultSettings.YResolution",0x00010001,480 -; USB uhci/ehci driver -;HKLM,"SYSTEM\CurrentControlSet\Services\usbdrv","ErrorControl",0x00010001,0x00000000 -;HKLM,"SYSTEM\CurrentControlSet\Services\usbdrv","Group",0x00000000,"Base" -;HKLM,"SYSTEM\CurrentControlSet\Services\usbdrv","ImagePath",0x00020000,"system32\drivers\usbdrv.sys" -;HKLM,"SYSTEM\CurrentControlSet\Services\usbdrv","Start",0x00010001,0x00000001 -;HKLM,"SYSTEM\CurrentControlSet\Services\usbdrv","Type",0x00010001,0x00000001 - -; USB storage driver -;HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","ErrorControl",0x00010001,0x00000000 -;HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","Group",0x00000000,"Extended Base" -;HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","ImagePath",0x00020000,"system32\drivers\usbstor.sys" -;HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","Start",0x00010001,0x00000003 -;HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","Type",0x00010001,0x00000001 - ; Windows Installer Service HKLM,"SYSTEM\CurrentControlSet\Services\MSIserver","DisplayName",0x00000000,"ReactOS Installer" HKLM,"SYSTEM\CurrentControlSet\Services\MSIserver","Description",0x00000000,"Controls installation, maintenance, and removal of software packaged as an msi (Windows Installer)" diff --git a/reactos/boot/bootdata/hivesys_i386.inf b/reactos/boot/bootdata/hivesys_i386.inf index 97d40aeb7a1..7529f53f3a5 100644 --- a/reactos/boot/bootdata/hivesys_i386.inf +++ b/reactos/boot/bootdata/hivesys_i386.inf @@ -8,6 +8,58 @@ HKLM,"SYSTEM\CurrentControlSet\Control","WaitToKillServiceTimeout",2,"20000" HKLM,"SYSTEM\CurrentControlSet\Control\Biosinfo","InfName",2,"biosinfo.inf" HKLM,"SYSTEM\CurrentControlSet\Control\PnP",,0x00000012 +; Critical Device Database + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\acpipic_up","ClassGUID",0x00000000,"{4D36E966-E325-11CE-BFC1-08002BE10318}" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\e_isa_up","ClassGUID",0x00000000,"{4D36E966-E325-11CE-BFC1-08002BE10318}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\*PNP0A03","Service",0x00000000,"pci" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\*PNP0A03","ClassGUID",0x00000000,"{4D36E97D-E325-11CE-BFC1-08002BE10318}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\*PNP0C08","Service",0x00000000,"acpi" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\*PNP0C08","ClassGUID",0x00000000,"{4D36E97D-E325-11CE-BFC1-08002BE10318}" + +;HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\PCI#CC_0C0300","Service",0x00000000,"usbuhci" +;HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\PCI#CC_0C0300","ClassGUID",0x00000000,"{36FC9E60-C465-11CF-8056-444553540000}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\PCI#CC_0C0310","Service",0x00000000,"usbohci" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\PCI#CC_0C0310","ClassGUID",0x00000000,"{36FC9E60-C465-11CF-8056-444553540000}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\PCI#CC_0C0320","Service",0x00000000,"usbehci" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\PCI#CC_0C0320","ClassGUID",0x00000000,"{36FC9E60-C465-11CF-8056-444553540000}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#Class_08&SubClass_06&Prot_50","Service",0x00000000,"usbstor" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#Class_08&SubClass_06&Prot_50","ClassGUID",0x00000000,"{36FC9E60-C465-11CF-8056-444553540000}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#COMPOSITE","Service",0x00000000,"usbccgp" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#COMPOSITE","ClassGUID",0x00000000,"{36FC9E60-C465-11CF-8056-444553540000}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#CLASS_09","Service",0x00000000,"usbhub" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#CLASS_09","ClassGUID",0x00000000,"{36FC9E60-C465-11CF-8056-444553540000}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#ROOT_HUB","Service",0x00000000,"usbhub" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#ROOT_HUB","ClassGUID",0x00000000,"{36FC9E60-C465-11CF-8056-444553540000}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#ROOT_HUB20","Service",0x00000000,"usbhub" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#ROOT_HUB20","ClassGUID",0x00000000,"{36FC9E60-C465-11CF-8056-444553540000}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\GenDisk","Service",0x00000000,"disk" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\GenDisk","ClassGUID",0x00000000,"{4D36E967-E325-11CE-BFC1-08002BE10318}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#Class_03","Service",0x00000000,"hidusb" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\USB#Class_03","ClassGUID",0x00000000,"{745a17a0-74d3-11d0-b6fe-00a0c90f57da}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\GENERIC_HID_DEVICE","Service",0x00000000,"hidusb" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\GENERIC_HID_DEVICE","ClassGUID",0x00000000,"{745a17a0-74d3-11d0-b6fe-00a0c90f57da}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\HID_DEVICE_SYSTEM_KEYBOARD","Service",0x00000000,"kbdhid" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\HID_DEVICE_SYSTEM_KEYBOARD","ClassGUID",0x00000000,"{4D36E96B-E325-11CE-BFC1-08002BE10318}" + +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\HID_DEVICE_SYSTEM_MOUSE","Service",0x00000000,"mouhid" +HKLM,"SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\HID_DEVICE_SYSTEM_MOUSE","ClassGUID",0x00000000,"{4D36E96F-E325-11CE-BFC1-08002BE10318}" + + + HKLM,"SYSTEM\CurrentControlSet\Control\SafeBoot","AlternateShell",2,"cmd.exe" ; Safe Boot drivers @@ -1413,10 +1465,82 @@ HKLM,"SYSTEM\CurrentControlSet\Services\Packet","ImagePath",0x00020000,"system32 HKLM,"SYSTEM\CurrentControlSet\Services\Packet","Start",0x00010001,0x00000004 HKLM,"SYSTEM\CurrentControlSet\Services\Packet","Type",0x00010001,0x00000001 +; USB HID driver +HKLM,"SYSTEM\CurrentControlSet\Services\hidusb","ErrorControl",0x00010001,0x00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\hidusb","Group",0x00000000,"Extended Base" +HKLM,"SYSTEM\CurrentControlSet\Services\hidusb","ImagePath",0x00020000,"system32\drivers\hidusb.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\hidusb","Start",0x00010001,0x00000003 +HKLM,"SYSTEM\CurrentControlSet\Services\hidusb","Type",0x00010001,0x00000001 + +; HID keyboard driver +HKLM,"SYSTEM\CurrentControlSet\Services\kbdhid","ErrorControl",0x00010001,0x00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\kbdhid","Group",0x00000000,"Keyboard Port" +HKLM,"SYSTEM\CurrentControlSet\Services\kbdhid","ImagePath",0x00020000,"system32\drivers\kbdhid.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\kbdhid","Start",0x00010001,0x00000003 +HKLM,"SYSTEM\CurrentControlSet\Services\kbdhid","Type",0x00010001,0x00000001 + +; HID mouse driver +HKLM,"SYSTEM\CurrentControlSet\Services\mouhid","ErrorControl",0x00010001,0x00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\mouhid","Group",0x00000000,"Pointer Port" +HKLM,"SYSTEM\CurrentControlSet\Services\mouhid","ImagePath",0x00020000,"system32\drivers\mouhid.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\mouhid","Start",0x00010001,0x00000003 +HKLM,"SYSTEM\CurrentControlSet\Services\mouhid","Type",0x00010001,0x00000001 + +; USB hub driver +HKLM,"SYSTEM\CurrentControlSet\Services\usbhub","ErrorControl",0x00010001,0x00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\usbhub","Group",0x00000000,"Boot Bus Extender" +HKLM,"SYSTEM\CurrentControlSet\Services\usbhub","ImagePath",0x00020000,"system32\drivers\usbhub.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\usbhub","Start",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\usbhub","Type",0x00010001,0x00000001 + +; EHCI controller driver +HKLM,"SYSTEM\CurrentControlSet\Services\usbehci","ErrorControl",0x00010001,0x00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\usbehci","Group",0x00000000,"Boot Bus Extender" +HKLM,"SYSTEM\CurrentControlSet\Services\usbehci","ImagePath",0x00020000,"system32\drivers\usbehci.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\usbehci","Start",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\usbehci","Type",0x00010001,0x00000001 + +; OHCI controller driver +HKLM,"SYSTEM\CurrentControlSet\Services\usbohci","ErrorControl",0x00010001,0x00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\usbohci","Group",0x00000000,"Boot Bus Extender" +HKLM,"SYSTEM\CurrentControlSet\Services\usbohci","ImagePath",0x00020000,"system32\drivers\usbohci.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\usbohci","Start",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\usbohci","Type",0x00010001,0x00000001 + +; UHCI controller driver +;HKLM,"SYSTEM\CurrentControlSet\Services\usbuhci","ErrorControl",0x00010001,0x00000001 +;HKLM,"SYSTEM\CurrentControlSet\Services\usbuhci","Group",0x00000000,"Boot Bus Extender" +;HKLM,"SYSTEM\CurrentControlSet\Services\usbuhci","ImagePath",0x00020000,"system32\drivers\usbuhci.sys" +;HKLM,"SYSTEM\CurrentControlSet\Services\usbuhci","Start",0x00010001,0x00000000 +;HKLM,"SYSTEM\CurrentControlSet\Services\usbuhci","Type",0x00010001,0x00000001 + +; USB storage driver +HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","ErrorControl",0x00010001,0x00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","Group",0x00000000,"Primary Disk" +HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","ImagePath",0x00020000,"system32\drivers\usbstor.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","Start",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","Type",0x00010001,0x00000001 + +; USB composite generic parent +HKLM,"SYSTEM\CurrentControlSet\Services\usbccgp","ErrorControl",0x00010001,0x00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\usbccgp","Group",0x00000000,"Boot Bus Extender" +HKLM,"SYSTEM\CurrentControlSet\Services\usbccgp","ImagePath",0x00020000,"system32\drivers\usbccgp.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\usbccgp","Start",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\usbccgp","Type",0x00010001,0x00000001 + +; ACPI driver +HKLM,"SYSTEM\CurrentControlSet\Services\acpi","ErrorControl",0x00010001,0x00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\acpi","Group",0x00000000,"Boot Bus Extender" +HKLM,"SYSTEM\CurrentControlSet\Services\acpi","ImagePath",0x00020000,"system32\drivers\acpi.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\acpi","Start",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\acpi","Type",0x00010001,0x00000001 + ; PCI Bus driver HKLM,"SYSTEM\CurrentControlSet\Services\Pci","ErrorControl",0x00010001,0x00000001 -HKLM,"SYSTEM\CurrentControlSet\Services\Pci","Group",0x00000000,"Boot Bus " -HKLM,"SYSTEM\CurrentControlSet\Services\Pci","Tag",0x00010001,0x00000002 +HKLM,"SYSTEM\CurrentControlSet\Services\Pci","Group",0x00000000,"Boot Bus Extender" +HKLM,"SYSTEM\CurrentControlSet\Services\Pci","ImagePath",0x00020000,"system32\drivers\pci.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\Pci","Start",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\Pci","Type",0x00010001,0x00000001 HKLM,"SYSTEM\CurrentControlSet\Services\Pci\Parameters","1045C621",0x00030003,04,00,00,00,00,00,00,00 HKLM,"SYSTEM\CurrentControlSet\Services\Pci\Parameters","10950640",0x00030003,04,00,00,00,00,00,00,00 HKLM,"SYSTEM\CurrentControlSet\Services\Pci\Parameters","80861230",0x00030003,04,00,00,00,00,00,00,00 @@ -1730,20 +1854,6 @@ HKLM,"SYSTEM\CurrentControlSet\Hardware Profiles\Current\System\CurrentControlSe HKLM,"SYSTEM\CurrentControlSet\Hardware Profiles\Current\System\CurrentControlSet\Services\Vga\Device0","DefaultSettings.XResolution",0x00010001,640 HKLM,"SYSTEM\CurrentControlSet\Hardware Profiles\Current\System\CurrentControlSet\Services\Vga\Device0","DefaultSettings.YResolution",0x00010001,480 -; USB uhci/ehci driver -HKLM,"SYSTEM\CurrentControlSet\Services\usbdrv","ErrorControl",0x00010001,0x00000000 -HKLM,"SYSTEM\CurrentControlSet\Services\usbdrv","Group",0x00000000,"Base" -HKLM,"SYSTEM\CurrentControlSet\Services\usbdrv","ImagePath",0x00020000,"system32\drivers\usbdrv.sys" -HKLM,"SYSTEM\CurrentControlSet\Services\usbdrv","Start",0x00010001,0x00000004 -HKLM,"SYSTEM\CurrentControlSet\Services\usbdrv","Type",0x00010001,0x00000001 - -; USB storage driver -;HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","ErrorControl",0x00010001,0x00000000 -;HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","Group",0x00000000,"Extended Base" -;HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","ImagePath",0x00020000,"system32\drivers\usbstor.sys" -;HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","Start",0x00010001,0x00000003 -;HKLM,"SYSTEM\CurrentControlSet\Services\usbstor","Type",0x00010001,0x00000001 - ; Windows Installer Service HKLM,"SYSTEM\CurrentControlSet\Services\MSIserver","DisplayName",0x00000000,"ReactOS Installer" HKLM,"SYSTEM\CurrentControlSet\Services\MSIserver","Description",0x00000000,"Controls installation, maintenance, and removal of software packaged as an msi (Windows Installer)" diff --git a/reactos/boot/bootdata/packages/reactos.dff b/reactos/boot/bootdata/packages/reactos.dff index 912a1c72b74..512ead158e9 100644 --- a/reactos/boot/bootdata/packages/reactos.dff +++ b/reactos/boot/bootdata/packages/reactos.dff @@ -542,11 +542,7 @@ drivers\serial\serial\serial.sys 2 drivers\storage\ide\pciide\pciide.sys 2 drivers\storage\ide\pciidex\pciidex.sys 2 -;drivers\usb\miniport\usbohci\usbohci.sys 2 -;drivers\usb\miniport\usbuhci\usbuhci.sys 2 -;drivers\usb\usbhub\usbhub.sys 2 -;drivers\usb\usbport\usbport.sys 2 -;drivers\usb\nt4compat\usbdriver\usbdriver.sys 2 +drivers\hid\mouhid\mouhid.sys 2 drivers\video\displays\vga\vgaddi.dll 1 drivers\video\displays\framebuf\framebuf.dll 1 @@ -671,6 +667,7 @@ media\inf\font.inf 6 media\inf\fdc.inf 6 media\inf\hal.inf 6 media\inf\hdc.inf 6 +media\inf\input.inf 6 media\inf\intl.inf 6 media\inf\layout.inf 6 media\inf\machine.inf 6 diff --git a/reactos/boot/bootdata/txtsetup.sif b/reactos/boot/bootdata/txtsetup.sif index 1b7f1c771c8..59668ecedf4 100644 --- a/reactos/boot/bootdata/txtsetup.sif +++ b/reactos/boot/bootdata/txtsetup.sif @@ -32,7 +32,17 @@ kdcom.dll=,,,,,,,,,,,,2 disk.sys=,,,,,,x,,,,,,4 floppy.sys=,,,,,,x,,,,,,4 i8042prt.sys=,,,,,,x,,,,,,4 -usbdrv.sys=,,,,,,,,,,,,4 +hidclass.sys=,,,,,,,,,,,,4 +hidparse.sys=,,,,,,,,,,,,4 +hidusb.sys=,,,,,,,,,,,,4 +usbccgp.sys=,,,,,,x,,,,,,4 +usbd.sys=,,,,,,x,,,,,,4 +usbhub.sys=,,,,,,x,,,,,,4 +;usbuhci.sys=,,,,,,x,,,,,,4 +usbohci.sys=,,,,,,x,,,,,,4 +usbehci.sys=,,,,,,x,,,,,,4 +usbstor.sys=,,,,,,x,,,,,,4 +kbdhid.sys=,,,,,,,,,,,,4 kbdclass.sys=,,,,,,x,,,,,,4 l_intl.nls=,,,,,,,,,,,,2 ntfs.sys=,,,,,,,,,,,,4 @@ -62,18 +72,41 @@ PCI\CC_0104 = uniata PCI\CC_0105 = uniata PCI\CC_0106 = uniata *PNP0600 = uniata +USB\CLASS_09 = usbhub +USB\ROOT_HUB = usbhub +USB\ROOT_HUB20 = usbhub +;PCI\CC_0C0300 = usbuhci +PCI\CC_0C0310 = usbohci +PCI\CC_0C0320 = usbehci +USB\Class_08&SubClass_06&Prot_50 = usbstor +HID_DEVICE_SYSTEM_KEYBOARD = kbdhid +USB\COMPOSITE = usbccgp +GenDisk = disk +USB\Class_03 = hidusb +GENERIC_HID_DEVICE = hidusb [BootBusExtenders.Load] acpi = acpi.sys pci = pci.sys isapnp = isapnp.sys +[InputDevicesSupport.Load] +usbehci = usbehci.sys +usbohci = usbohci.sys +;usbuhci = usbuhci.sys +usbhub = usbhub.sys +usbccgp = usbccgp.sys +hidusb = hidusb.sys +usbstor = usbstor.sys +kbdhid = kbdhid.sys + [BusExtenders.Load] pciide = pciide.sys [SCSI.Load] uniata = uniata.sys buslogic = buslogic.sys +disk = disk.sys [Cabinets] Cabinet=reactos.cab diff --git a/reactos/dll/win32/hid/hid.c b/reactos/dll/win32/hid/hid.c index 89cf57c7c1b..bf5bb5fe173 100644 --- a/reactos/dll/win32/hid/hid.c +++ b/reactos/dll/win32/hid/hid.c @@ -70,7 +70,7 @@ HidD_FlushQueue(IN HANDLE HidDeviceObject) return DeviceIoControl(HidDeviceObject, IOCTL_HID_FLUSH_QUEUE, NULL, 0, NULL, 0, - &RetLen, NULL); + &RetLen, NULL) != 0; } @@ -150,7 +150,7 @@ HidD_GetFeature(IN HANDLE HidDeviceObject, return DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_FEATURE, NULL, 0, ReportBuffer, ReportBufferLength, - &RetLen, NULL); + &RetLen, NULL) != 0; } @@ -182,7 +182,7 @@ HidD_GetInputReport(IN HANDLE HidDeviceObject, return DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_INPUT_REPORT, NULL, 0, ReportBuffer, ReportBufferLength, - &RetLen, NULL); + &RetLen, NULL) != 0; } @@ -201,7 +201,7 @@ HidD_GetManufacturerString(IN HANDLE HidDeviceObject, return DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_MANUFACTURER_STRING, NULL, 0, Buffer, BufferLength, - &RetLen, NULL); + &RetLen, NULL) != 0; } @@ -219,7 +219,7 @@ HidD_GetNumInputBuffers(IN HANDLE HidDeviceObject, return DeviceIoControl(HidDeviceObject, IOCTL_GET_NUM_DEVICE_INPUT_BUFFERS, NULL, 0, NumberBuffers, sizeof(ULONG), - &RetLen, NULL); + &RetLen, NULL) != 0; } @@ -238,7 +238,7 @@ HidD_GetPhysicalDescriptor(IN HANDLE HidDeviceObject, return DeviceIoControl(HidDeviceObject, IOCTL_GET_PHYSICAL_DESCRIPTOR, NULL, 0, Buffer, BufferLength, - &RetLen, NULL); + &RetLen, NULL) != 0; } @@ -254,7 +254,7 @@ HidD_GetPreparsedData(IN HANDLE HidDeviceObject, { HID_COLLECTION_INFORMATION hci; DWORD RetLen; - BOOL Ret; + BOOLEAN Ret; if(PreparsedData == NULL) { @@ -279,7 +279,7 @@ HidD_GetPreparsedData(IN HANDLE HidDeviceObject, Ret = DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_COLLECTION_DESCRIPTOR, NULL, 0, *PreparsedData, hci.DescriptorSize, - &RetLen, NULL); + &RetLen, NULL) != 0; if(!Ret) { @@ -312,7 +312,7 @@ HidD_GetProductString(IN HANDLE HidDeviceObject, return DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_PRODUCT_STRING, NULL, 0, Buffer, BufferLength, - &RetLen, NULL); + &RetLen, NULL) != 0; } @@ -331,7 +331,7 @@ HidD_GetSerialNumberString(IN HANDLE HidDeviceObject, return DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_SERIALNUMBER_STRING, NULL, 0, Buffer, BufferLength, - &RetLen, NULL); + &RetLen, NULL) != 0; } @@ -394,7 +394,7 @@ HidD_SetFeature(IN HANDLE HidDeviceObject, return DeviceIoControl(HidDeviceObject, IOCTL_HID_SET_FEATURE, ReportBuffer, ReportBufferLength, NULL, 0, - &RetLen, NULL); + &RetLen, NULL) != 0; } @@ -412,7 +412,7 @@ HidD_SetNumInputBuffers(IN HANDLE HidDeviceObject, return DeviceIoControl(HidDeviceObject, IOCTL_SET_NUM_DEVICE_INPUT_BUFFERS, &NumberBuffers, sizeof(ULONG), NULL, 0, - &RetLen, NULL); + &RetLen, NULL) != 0; } @@ -431,7 +431,112 @@ HidD_SetOutputReport(IN HANDLE HidDeviceObject, return DeviceIoControl(HidDeviceObject, IOCTL_HID_SET_OUTPUT_REPORT, ReportBuffer, ReportBufferLength, NULL, 0, - &RetLen, NULL); + &RetLen, NULL) != 0; } +/* + * HidD_GetIndexedString EXPORTED + * + * @implemented + */ +HIDAPI +BOOLEAN WINAPI +HidD_GetIndexedString(IN HANDLE HidDeviceObject, + IN ULONG StringIndex, + OUT PVOID Buffer, + IN ULONG BufferLength) +{ + DWORD RetLen; + return DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_INDEXED_STRING, + &StringIndex, sizeof(ULONG), + Buffer, BufferLength, + &RetLen, NULL) != 0; +} + +/* + * HidD_GetMsGenreDescriptor EXPORTED + * + * @implemented + */ +HIDAPI +BOOLEAN WINAPI +HidD_GetMsGenreDescriptor(IN HANDLE HidDeviceObject, + OUT PVOID Buffer, + IN ULONG BufferLength) +{ + DWORD RetLen; + return DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_MS_GENRE_DESCRIPTOR, + 0, 0, + Buffer, BufferLength, + &RetLen, NULL) != 0; +} + +/* + * HidD_GetConfiguration EXPORTED + * + * @implemented + */ +HIDAPI +BOOLEAN WINAPI +HidD_GetConfiguration(IN HANDLE HidDeviceObject, + OUT PHIDD_CONFIGURATION Configuration, + IN ULONG ConfigurationLength) +{ + + // magic cookie + Configuration->cookie = (PVOID)HidD_GetConfiguration; + + return DeviceIoControl(HidDeviceObject, IOCTL_HID_GET_DRIVER_CONFIG, + 0, 0, + &Configuration->size, ConfigurationLength - sizeof(ULONG), + (PULONG)&Configuration->cookie, NULL) != 0; +} + +/* + * HidD_SetConfiguration EXPORTED + * + * @implemented + */ +HIDAPI +BOOLEAN WINAPI +HidD_SetConfiguration(IN HANDLE HidDeviceObject, + IN PHIDD_CONFIGURATION Configuration, + IN ULONG ConfigurationLength) +{ + BOOLEAN Ret = FALSE; + + if (Configuration->cookie == (PVOID)HidD_GetConfiguration) + { + Ret = DeviceIoControl(HidDeviceObject, IOCTL_HID_SET_DRIVER_CONFIG, + 0, 0, + (PVOID)&Configuration->size, ConfigurationLength - sizeof(ULONG), + (PULONG)&Configuration->cookie, NULL) != 0; + } + else + { + SetLastError(ERROR_INVALID_PARAMETER); + } + + return Ret; +} + +/* + * HidP_GetUsagesEx EXPORTED + * + * @implemented + */ +HIDAPI +NTSTATUS WINAPI +HidP_GetUsagesEx(IN HIDP_REPORT_TYPE ReportType, + IN USHORT LinkCollection, + OUT PUSAGE_AND_PAGE ButtonList, + IN OUT ULONG *UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + return HidP_GetUsages(ReportType, ButtonList->UsagePage, LinkCollection, &ButtonList->Usage, UsageLength, PreparsedData, Report, ReportLength); +} + + /* EOF */ diff --git a/reactos/dll/win32/hid/stubs.c b/reactos/dll/win32/hid/stubs.c index cb4543da792..1e37efaa166 100644 --- a/reactos/dll/win32/hid/stubs.c +++ b/reactos/dll/win32/hid/stubs.c @@ -11,63 +11,6 @@ */ #include -/* - * @unimplemented - */ -HIDAPI -BOOLEAN WINAPI -HidD_GetConfiguration(IN HANDLE HidDeviceObject, - OUT PHIDD_CONFIGURATION Configuration, - IN ULONG ConfigurationLength) -{ - UNIMPLEMENTED; - return FALSE; -} - - -/* - * @unimplemented - */ -HIDAPI -BOOLEAN WINAPI -HidD_GetIndexedString(IN HANDLE HidDeviceObject, - IN ULONG StringIndex, - OUT PVOID Buffer, - IN ULONG BufferLength) -{ - UNIMPLEMENTED; - return FALSE; -} - - -/* - * @unimplemented - */ -HIDAPI -BOOLEAN WINAPI -HidD_GetMsGenreDescriptor(IN HANDLE HidDeviceObject, - OUT PVOID Buffer, - IN ULONG BufferLength) -{ - UNIMPLEMENTED; - return FALSE; -} - - -/* - * @unimplemented - */ -HIDAPI -BOOLEAN WINAPI -HidD_SetConfiguration(IN HANDLE HidDeviceObject, - IN PHIDD_CONFIGURATION Configuration, - IN ULONG ConfigurationLength) -{ - UNIMPLEMENTED; - return FALSE; -} - - /* * @unimplemented */ @@ -241,24 +184,6 @@ HidP_GetUsages(IN HIDP_REPORT_TYPE ReportType, } -/* - * @unimplemented - */ -HIDAPI -NTSTATUS WINAPI -HidP_GetUsagesEx(IN HIDP_REPORT_TYPE ReportType, - IN USHORT LinkCollection, - OUT PUSAGE_AND_PAGE ButtonList, - IN OUT ULONG *UsageLength, - IN PHIDP_PREPARSED_DATA PreparsedData, - IN PCHAR Report, - IN ULONG ReportLength) -{ - UNIMPLEMENTED; - return HIDP_STATUS_NOT_IMPLEMENTED; -} - - /* * @unimplemented */ diff --git a/reactos/drivers/CMakeLists.txt b/reactos/drivers/CMakeLists.txt index 3960ee1e993..22fad3a1892 100644 --- a/reactos/drivers/CMakeLists.txt +++ b/reactos/drivers/CMakeLists.txt @@ -4,6 +4,8 @@ add_subdirectory(battery) add_subdirectory(bus) add_subdirectory(directx) add_subdirectory(filesystems) +add_subdirectory(filters) +add_subdirectory(hid) add_subdirectory(input) add_subdirectory(ksfilter) add_subdirectory(network) diff --git a/reactos/drivers/bus/acpi/buspdo.c b/reactos/drivers/bus/acpi/buspdo.c index 0e149b87659..c807f1aa129 100644 --- a/reactos/drivers/bus/acpi/buspdo.c +++ b/reactos/drivers/bus/acpi/buspdo.c @@ -1136,6 +1136,12 @@ Bus_PDO_QueryResourceRequirements( return Irp->IoStatus.Status; } + /* Handle the PCI root manually */ + if (wcsstr(DeviceData->HardwareIDs, L"PNP0A03") != 0) + { + return Irp->IoStatus.Status; + } + /* Get current resources */ while (TRUE) { @@ -1315,7 +1321,7 @@ Bus_PDO_QueryResourceRequirements( RequirementDescriptor->ShareDisposition = CmResourceShareDriverExclusive; RequirementDescriptor->u.Port.Alignment = io_data->Alignment; RequirementDescriptor->u.Port.MinimumAddress.QuadPart = io_data->Minimum; - RequirementDescriptor->u.Port.MaximumAddress.QuadPart = io_data->Maximum; + RequirementDescriptor->u.Port.MaximumAddress.QuadPart = io_data->Maximum + io_data->AddressLength - 1; RequirementDescriptor++; break; @@ -1330,7 +1336,7 @@ Bus_PDO_QueryResourceRequirements( RequirementDescriptor->ShareDisposition = CmResourceShareShared; RequirementDescriptor->Flags = 0; RequirementDescriptor->u.BusNumber.MinBusNumber = addr16_data->Minimum; - RequirementDescriptor->u.BusNumber.MaxBusNumber = addr16_data->Maximum; + RequirementDescriptor->u.BusNumber.MaxBusNumber = addr16_data->Maximum + addr16_data->AddressLength - 1; RequirementDescriptor->u.BusNumber.Length = addr16_data->AddressLength; } else if (addr16_data->ResourceType == ACPI_IO_RANGE) @@ -1341,7 +1347,7 @@ Bus_PDO_QueryResourceRequirements( if (addr16_data->Decode == ACPI_POS_DECODE) RequirementDescriptor->Flags |= CM_RESOURCE_PORT_POSITIVE_DECODE; RequirementDescriptor->u.Port.MinimumAddress.QuadPart = addr16_data->Minimum; - RequirementDescriptor->u.Port.MaximumAddress.QuadPart = addr16_data->Maximum; + RequirementDescriptor->u.Port.MaximumAddress.QuadPart = addr16_data->Maximum + addr16_data->AddressLength - 1; RequirementDescriptor->u.Port.Length = addr16_data->AddressLength; } else @@ -1360,7 +1366,7 @@ Bus_PDO_QueryResourceRequirements( case ACPI_PREFETCHABLE_MEMORY: RequirementDescriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE; break; } RequirementDescriptor->u.Memory.MinimumAddress.QuadPart = addr16_data->Minimum; - RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = addr16_data->Maximum; + RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = addr16_data->Maximum + addr16_data->AddressLength - 1; RequirementDescriptor->u.Memory.Length = addr16_data->AddressLength; } RequirementDescriptor++; @@ -1376,7 +1382,7 @@ Bus_PDO_QueryResourceRequirements( RequirementDescriptor->ShareDisposition = CmResourceShareShared; RequirementDescriptor->Flags = 0; RequirementDescriptor->u.BusNumber.MinBusNumber = addr32_data->Minimum; - RequirementDescriptor->u.BusNumber.MaxBusNumber = addr32_data->Maximum; + RequirementDescriptor->u.BusNumber.MaxBusNumber = addr32_data->Maximum + addr32_data->AddressLength - 1; RequirementDescriptor->u.BusNumber.Length = addr32_data->AddressLength; } else if (addr32_data->ResourceType == ACPI_IO_RANGE) @@ -1387,7 +1393,7 @@ Bus_PDO_QueryResourceRequirements( if (addr32_data->Decode == ACPI_POS_DECODE) RequirementDescriptor->Flags |= CM_RESOURCE_PORT_POSITIVE_DECODE; RequirementDescriptor->u.Port.MinimumAddress.QuadPart = addr32_data->Minimum; - RequirementDescriptor->u.Port.MaximumAddress.QuadPart = addr32_data->Maximum; + RequirementDescriptor->u.Port.MaximumAddress.QuadPart = addr32_data->Maximum + addr32_data->AddressLength - 1; RequirementDescriptor->u.Port.Length = addr32_data->AddressLength; } else @@ -1406,7 +1412,7 @@ Bus_PDO_QueryResourceRequirements( case ACPI_PREFETCHABLE_MEMORY: RequirementDescriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE; break; } RequirementDescriptor->u.Memory.MinimumAddress.QuadPart = addr32_data->Minimum; - RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = addr32_data->Maximum; + RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = addr32_data->Maximum + addr32_data->AddressLength - 1; RequirementDescriptor->u.Memory.Length = addr32_data->AddressLength; } RequirementDescriptor++; @@ -1423,7 +1429,7 @@ Bus_PDO_QueryResourceRequirements( RequirementDescriptor->ShareDisposition = CmResourceShareShared; RequirementDescriptor->Flags = 0; RequirementDescriptor->u.BusNumber.MinBusNumber = (ULONG)addr64_data->Minimum; - RequirementDescriptor->u.BusNumber.MaxBusNumber = (ULONG)addr64_data->Maximum; + RequirementDescriptor->u.BusNumber.MaxBusNumber = (ULONG)addr64_data->Maximum + addr64_data->AddressLength - 1; RequirementDescriptor->u.BusNumber.Length = addr64_data->AddressLength; } else if (addr64_data->ResourceType == ACPI_IO_RANGE) @@ -1434,7 +1440,7 @@ Bus_PDO_QueryResourceRequirements( if (addr64_data->Decode == ACPI_POS_DECODE) RequirementDescriptor->Flags |= CM_RESOURCE_PORT_POSITIVE_DECODE; RequirementDescriptor->u.Port.MinimumAddress.QuadPart = addr64_data->Minimum; - RequirementDescriptor->u.Port.MaximumAddress.QuadPart = addr64_data->Maximum; + RequirementDescriptor->u.Port.MaximumAddress.QuadPart = addr64_data->Maximum + addr64_data->AddressLength - 1; RequirementDescriptor->u.Port.Length = addr64_data->AddressLength; } else @@ -1453,7 +1459,7 @@ Bus_PDO_QueryResourceRequirements( case ACPI_PREFETCHABLE_MEMORY: RequirementDescriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE; break; } RequirementDescriptor->u.Memory.MinimumAddress.QuadPart = addr64_data->Minimum; - RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = addr64_data->Maximum; + RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = addr64_data->Maximum + addr64_data->AddressLength - 1; RequirementDescriptor->u.Memory.Length = addr64_data->AddressLength; } RequirementDescriptor++; @@ -1470,7 +1476,7 @@ Bus_PDO_QueryResourceRequirements( RequirementDescriptor->ShareDisposition = CmResourceShareShared; RequirementDescriptor->Flags = 0; RequirementDescriptor->u.BusNumber.MinBusNumber = (ULONG)addr64_data->Minimum; - RequirementDescriptor->u.BusNumber.MaxBusNumber = (ULONG)addr64_data->Maximum; + RequirementDescriptor->u.BusNumber.MaxBusNumber = (ULONG)addr64_data->Maximum + addr64_data->AddressLength - 1; RequirementDescriptor->u.BusNumber.Length = addr64_data->AddressLength; } else if (addr64_data->ResourceType == ACPI_IO_RANGE) @@ -1481,7 +1487,7 @@ Bus_PDO_QueryResourceRequirements( if (addr64_data->Decode == ACPI_POS_DECODE) RequirementDescriptor->Flags |= CM_RESOURCE_PORT_POSITIVE_DECODE; RequirementDescriptor->u.Port.MinimumAddress.QuadPart = addr64_data->Minimum; - RequirementDescriptor->u.Port.MaximumAddress.QuadPart = addr64_data->Maximum; + RequirementDescriptor->u.Port.MaximumAddress.QuadPart = addr64_data->Maximum + addr64_data->AddressLength - 1; RequirementDescriptor->u.Port.Length = addr64_data->AddressLength; } else @@ -1500,7 +1506,7 @@ Bus_PDO_QueryResourceRequirements( case ACPI_PREFETCHABLE_MEMORY: RequirementDescriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE; break; } RequirementDescriptor->u.Memory.MinimumAddress.QuadPart = addr64_data->Minimum; - RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = addr64_data->Maximum; + RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = addr64_data->Maximum + addr64_data->AddressLength - 1; RequirementDescriptor->u.Memory.Length = addr64_data->AddressLength; } RequirementDescriptor++; @@ -1518,7 +1524,7 @@ Bus_PDO_QueryResourceRequirements( else RequirementDescriptor->Flags |= CM_RESOURCE_MEMORY_READ_WRITE; RequirementDescriptor->u.Memory.MinimumAddress.QuadPart = mem24_data->Minimum; - RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = mem24_data->Maximum; + RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = mem24_data->Maximum + mem24_data->AddressLength - 1; RequirementDescriptor->u.Memory.Length = mem24_data->AddressLength; RequirementDescriptor++; @@ -1536,7 +1542,7 @@ Bus_PDO_QueryResourceRequirements( else RequirementDescriptor->Flags |= CM_RESOURCE_MEMORY_READ_WRITE; RequirementDescriptor->u.Memory.MinimumAddress.QuadPart = mem32_data->Minimum; - RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = mem32_data->Maximum; + RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = mem32_data->Maximum + mem32_data->AddressLength - 1; RequirementDescriptor->u.Memory.Length = mem32_data->AddressLength; RequirementDescriptor++; @@ -1554,7 +1560,7 @@ Bus_PDO_QueryResourceRequirements( else RequirementDescriptor->Flags |= CM_RESOURCE_MEMORY_READ_WRITE; RequirementDescriptor->u.Memory.MinimumAddress.QuadPart = fixedmem32_data->Address; - RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = fixedmem32_data->Address; + RequirementDescriptor->u.Memory.MaximumAddress.QuadPart = fixedmem32_data->Address + fixedmem32_data->AddressLength - 1; RequirementDescriptor->u.Memory.Length = fixedmem32_data->AddressLength; RequirementDescriptor++; diff --git a/reactos/drivers/bus/pci/fdo.c b/reactos/drivers/bus/pci/fdo.c index 94f7a5f8c3a..28153ec88e0 100644 --- a/reactos/drivers/bus/pci/fdo.c +++ b/reactos/drivers/bus/pci/fdo.c @@ -96,7 +96,6 @@ FdoEnumerateDevices( { PFDO_DEVICE_EXTENSION DeviceExtension; PCI_COMMON_CONFIG PciConfig; - PLIST_ENTRY CurrentEntry; PPCI_DEVICE Device; PCI_SLOT_NUMBER SlotNumber; ULONG DeviceNumber; @@ -108,15 +107,6 @@ FdoEnumerateDevices( DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; - /* Mark all devices to be removed. If we don't discover them again during - enumeration, assume that they have been surprise removed */ - CurrentEntry = DeviceExtension->DeviceListHead.Flink; - while (CurrentEntry != &DeviceExtension->DeviceListHead) { - Device = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry); - Device->RemovePending = TRUE; - CurrentEntry = CurrentEntry->Flink; - } - DeviceExtension->DeviceListCount = 0; /* Enumerate devices on the PCI bus */ @@ -190,9 +180,6 @@ FdoEnumerateDevices( &DeviceExtension->DeviceListLock); } - /* Don't remove this device */ - Device->RemovePending = FALSE; - DeviceExtension->DeviceListCount++; /* Skip to next device if the current one is not a multifunction device */ @@ -354,15 +341,13 @@ FdoQueryBusRelations( } } - if (!Device->RemovePending) { - /* Reference the physical device object. The PnP manager - will dereference it again when it is no longer needed */ - ObReferenceObject(Device->Pdo); + /* Reference the physical device object. The PnP manager + will dereference it again when it is no longer needed */ + ObReferenceObject(Device->Pdo); - Relations->Objects[i] = Device->Pdo; + Relations->Objects[i] = Device->Pdo; - i++; - } + i++; CurrentEntry = CurrentEntry->Flink; } diff --git a/reactos/drivers/bus/pci/pci.h b/reactos/drivers/bus/pci/pci.h index f4001514391..605be08c205 100644 --- a/reactos/drivers/bus/pci/pci.h +++ b/reactos/drivers/bus/pci/pci.h @@ -17,8 +17,12 @@ typedef struct _PCI_DEVICE PCI_SLOT_NUMBER SlotNumber; // PCI configuration data PCI_COMMON_CONFIG PciConfig; - // Flag used during enumeration to locate removed devices - BOOLEAN RemovePending; + // Enable memory space + BOOLEAN EnableMemorySpace; + // Enable I/O space + BOOLEAN EnableIoSpace; + // Enable bus master + BOOLEAN EnableBusMaster; } PCI_DEVICE, *PPCI_DEVICE; diff --git a/reactos/drivers/bus/pci/pdo.c b/reactos/drivers/bus/pci/pdo.c index a3391795812..644bffe349f 100644 --- a/reactos/drivers/bus/pci/pdo.c +++ b/reactos/drivers/bus/pci/pdo.c @@ -766,6 +766,9 @@ PdoQueryResources( Descriptor->u.Port.Start.QuadPart = (ULONGLONG)Base; Descriptor->u.Port.Length = Length; + + /* Enable IO space access */ + DeviceExtension->PciDevice->EnableIoSpace = TRUE; } else { @@ -775,6 +778,9 @@ PdoQueryResources( Descriptor->u.Memory.Start.QuadPart = (ULONGLONG)Base; Descriptor->u.Memory.Length = Length; + + /* Enable memory space access */ + DeviceExtension->PciDevice->EnableMemorySpace = TRUE; } Descriptor++; @@ -792,6 +798,9 @@ PdoQueryResources( Descriptor->u.Interrupt.Vector = PciConfig.u.type0.InterruptLine; Descriptor->u.Interrupt.Affinity = 0xFFFFFFFF; } + + /* Allow bus master mode */ + DeviceExtension->PciDevice->EnableBusMaster = TRUE; } else if (PCI_CONFIGURATION_TYPE(&PciConfig) == PCI_BRIDGE_TYPE) { @@ -821,6 +830,9 @@ PdoQueryResources( Descriptor->u.Port.Start.QuadPart = (ULONGLONG)Base; Descriptor->u.Port.Length = Length; + + /* Enable IO space access */ + DeviceExtension->PciDevice->EnableIoSpace = TRUE; } else { @@ -830,6 +842,9 @@ PdoQueryResources( Descriptor->u.Memory.Start.QuadPart = (ULONGLONG)Base; Descriptor->u.Memory.Length = Length; + + /* Enable memory space access */ + DeviceExtension->PciDevice->EnableMemorySpace = TRUE; } Descriptor++; @@ -1206,45 +1221,88 @@ PdoStartDevice( IN PIRP Irp, PIO_STACK_LOCATION IrpSp) { - PCM_RESOURCE_LIST RawResList = IrpSp->Parameters.StartDevice.AllocatedResources; - PCM_FULL_RESOURCE_DESCRIPTOR RawFullDesc; - PCM_PARTIAL_RESOURCE_DESCRIPTOR RawPartialDesc; - ULONG i, ii; - PPDO_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; - UCHAR Irq; + PCM_RESOURCE_LIST RawResList = IrpSp->Parameters.StartDevice.AllocatedResources; + PCM_FULL_RESOURCE_DESCRIPTOR RawFullDesc; + PCM_PARTIAL_RESOURCE_DESCRIPTOR RawPartialDesc; + ULONG i, ii; + PPDO_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; + UCHAR Irq; + USHORT Command; - if (!RawResList) - return STATUS_SUCCESS; + if (!RawResList) + return STATUS_SUCCESS; - /* TODO: Assign the other resources we get to the card */ + /* TODO: Assign the other resources we get to the card */ - for (i = 0; i < RawResList->Count; i++) - { - RawFullDesc = &RawResList->List[i]; + for (i = 0; i < RawResList->Count; i++) + { + RawFullDesc = &RawResList->List[i]; - for (ii = 0; ii < RawFullDesc->PartialResourceList.Count; ii++) - { - RawPartialDesc = &RawFullDesc->PartialResourceList.PartialDescriptors[ii]; + for (ii = 0; ii < RawFullDesc->PartialResourceList.Count; ii++) + { + RawPartialDesc = &RawFullDesc->PartialResourceList.PartialDescriptors[ii]; - if (RawPartialDesc->Type == CmResourceTypeInterrupt) - { - DPRINT1("Assigning IRQ %x to PCI device (%x, %x)\n", - RawPartialDesc->u.Interrupt.Vector, - DeviceExtension->PciDevice->SlotNumber.u.AsULONG, - DeviceExtension->PciDevice->BusNumber); + if (RawPartialDesc->Type == CmResourceTypeInterrupt) + { + DPRINT1("Assigning IRQ %d to PCI device 0x%x on bus 0x%x\n", + RawPartialDesc->u.Interrupt.Vector, + DeviceExtension->PciDevice->SlotNumber.u.AsULONG, + DeviceExtension->PciDevice->BusNumber); - Irq = (UCHAR)RawPartialDesc->u.Interrupt.Vector; - HalSetBusDataByOffset(PCIConfiguration, - DeviceExtension->PciDevice->BusNumber, - DeviceExtension->PciDevice->SlotNumber.u.AsULONG, - &Irq, - 0x3c /* PCI_INTERRUPT_LINE */, - sizeof(UCHAR)); - } - } - } + Irq = (UCHAR)RawPartialDesc->u.Interrupt.Vector; + HalSetBusDataByOffset(PCIConfiguration, + DeviceExtension->PciDevice->BusNumber, + DeviceExtension->PciDevice->SlotNumber.u.AsULONG, + &Irq, + 0x3c /* PCI_INTERRUPT_LINE */, + sizeof(UCHAR)); + } + } + } - return STATUS_SUCCESS; + Command = 0; + + DPRINT1("Enabling command flags for PCI device 0x%x on bus 0x%x: ", + DeviceExtension->PciDevice->SlotNumber.u.AsULONG, + DeviceExtension->PciDevice->BusNumber); + if (DeviceExtension->PciDevice->EnableBusMaster) + { + Command |= PCI_ENABLE_BUS_MASTER; + DbgPrint("[Bus master] "); + } + + if (DeviceExtension->PciDevice->EnableMemorySpace) + { + Command |= PCI_ENABLE_MEMORY_SPACE; + DbgPrint("[Memory space enable] "); + } + + if (DeviceExtension->PciDevice->EnableIoSpace) + { + Command |= PCI_ENABLE_IO_SPACE; + DbgPrint("[I/O space enable] "); + } + + if (Command != 0) + { + DbgPrint("\n"); + + /* OR with the previous value */ + Command |= DeviceExtension->PciDevice->PciConfig.Command; + + HalSetBusDataByOffset(PCIConfiguration, + DeviceExtension->PciDevice->BusNumber, + DeviceExtension->PciDevice->SlotNumber.u.AsULONG, + &Command, + FIELD_OFFSET(PCI_COMMON_CONFIG, Command), + sizeof(USHORT)); + } + else + { + DbgPrint("None\n"); + } + + return STATUS_SUCCESS; } static NTSTATUS @@ -1443,11 +1501,34 @@ PdoPnpControl( case IRP_MN_STOP_DEVICE: case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE: - case IRP_MN_REMOVE_DEVICE: case IRP_MN_SURPRISE_REMOVAL: Status = STATUS_SUCCESS; break; + case IRP_MN_REMOVE_DEVICE: + { + PPDO_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; + PFDO_DEVICE_EXTENSION FdoDeviceExtension = DeviceExtension->Fdo->DeviceExtension; + KIRQL OldIrql; + + /* Remove it from the device list */ + KeAcquireSpinLock(&FdoDeviceExtension->DeviceListLock, &OldIrql); + RemoveEntryList(&DeviceExtension->PciDevice->ListEntry); + FdoDeviceExtension->DeviceListCount--; + KeReleaseSpinLock(&FdoDeviceExtension->DeviceListLock, OldIrql); + + /* Free the device */ + ExFreePool(DeviceExtension->PciDevice); + + /* Complete the IRP */ + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + /* Delete the DO */ + IoDeleteDevice(DeviceObject); + return STATUS_SUCCESS; + } + case IRP_MN_QUERY_INTERFACE: DPRINT("IRP_MN_QUERY_INTERFACE received\n"); Status = PdoQueryInterface(DeviceObject, Irp, IrpSp); diff --git a/reactos/drivers/bus/pcix/enum.c b/reactos/drivers/bus/pcix/enum.c index 5fb207005da..3544527e005 100644 --- a/reactos/drivers/bus/pcix/enum.c +++ b/reactos/drivers/bus/pcix/enum.c @@ -1196,71 +1196,79 @@ PciGetEnhancedCapabilities(IN PPCI_PDO_EXTENSION PdoExtension, CapPtr = PciData->u.type0.CapabilitiesPtr; } - /* Make sure the pointer is spec-aligned and located, and save it */ - DPRINT1("Device has capabilities at: %lx\n", CapPtr); - ASSERT(((CapPtr & 0x3) == 0) && (CapPtr >= PCI_COMMON_HDR_LENGTH)); - PdoExtension->CapabilitiesPtr = CapPtr; - - /* Check for PCI-to-PCI Bridges and AGP bridges */ - if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && - ((PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) || - (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI))) + /* Skip garbage capabilities pointer */ + if (((CapPtr & 0x3) != 0) || (CapPtr < PCI_COMMON_HDR_LENGTH)) { - /* Query either the raw AGP capabilitity, or the Target AGP one */ - TargetAgpCapabilityId = (PdoExtension->SubClass == - PCI_SUBCLASS_BR_PCI_TO_PCI) ? - PCI_CAPABILITY_ID_AGP_TARGET : - PCI_CAPABILITY_ID_AGP; - if (PciReadDeviceCapability(PdoExtension, - PdoExtension->CapabilitiesPtr, - TargetAgpCapabilityId, - &AgpCapability, - sizeof(PCI_CAPABILITIES_HEADER))) - { - /* AGP target ID was found, store it */ - DPRINT1("AGP ID: %lx\n", TargetAgpCapabilityId); - PdoExtension->TargetAgpCapabilityId = TargetAgpCapabilityId; - } + /* Report no extended capabilities */ + PdoExtension->CapabilitiesPtr = 0; + PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; } - - /* Check for devices that are known not to have proper power management */ - if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)) + else { - /* Query if this device supports power management */ - if (!PciReadDeviceCapability(PdoExtension, - PdoExtension->CapabilitiesPtr, - PCI_CAPABILITY_ID_POWER_MANAGEMENT, - &PowerCapabilities.Header, - sizeof(PCI_PM_CAPABILITY))) - { - /* No power management, so act as if it had the hackflag set */ - DPRINT1("No PM caps, disabling PM\n"); - PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; - } - else - { - /* Otherwise, pick the highest wake level that is supported */ - WakeLevel = PowerDeviceUnspecified; - if (PowerCapabilities.PMC.Capabilities.Support.PMED0) - WakeLevel = PowerDeviceD0; - if (PowerCapabilities.PMC.Capabilities.Support.PMED1) - WakeLevel = PowerDeviceD1; - if (PowerCapabilities.PMC.Capabilities.Support.PMED2) - WakeLevel = PowerDeviceD2; - if (PowerCapabilities.PMC.Capabilities.Support.PMED3Hot) - WakeLevel = PowerDeviceD3; - if (PowerCapabilities.PMC.Capabilities.Support.PMED3Cold) - WakeLevel = PowerDeviceD3; - PdoExtension->PowerState.DeviceWakeLevel = WakeLevel; + DPRINT1("Device has capabilities at: %lx\n", CapPtr); + PdoExtension->CapabilitiesPtr = CapPtr; - /* Convert the PCI power state to the NT power state */ - PdoExtension->PowerState.CurrentDeviceState = + /* Check for PCI-to-PCI Bridges and AGP bridges */ + if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && + ((PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) || + (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI))) + { + /* Query either the raw AGP capabilitity, or the Target AGP one */ + TargetAgpCapabilityId = (PdoExtension->SubClass == + PCI_SUBCLASS_BR_PCI_TO_PCI) ? + PCI_CAPABILITY_ID_AGP_TARGET : + PCI_CAPABILITY_ID_AGP; + if (PciReadDeviceCapability(PdoExtension, + PdoExtension->CapabilitiesPtr, + TargetAgpCapabilityId, + &AgpCapability, + sizeof(PCI_CAPABILITIES_HEADER))) + { + /* AGP target ID was found, store it */ + DPRINT1("AGP ID: %lx\n", TargetAgpCapabilityId); + PdoExtension->TargetAgpCapabilityId = TargetAgpCapabilityId; + } + } + + /* Check for devices that are known not to have proper power management */ + if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)) + { + /* Query if this device supports power management */ + if (!PciReadDeviceCapability(PdoExtension, + PdoExtension->CapabilitiesPtr, + PCI_CAPABILITY_ID_POWER_MANAGEMENT, + &PowerCapabilities.Header, + sizeof(PCI_PM_CAPABILITY))) + { + /* No power management, so act as if it had the hackflag set */ + DPRINT1("No PM caps, disabling PM\n"); + PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; + } + else + { + /* Otherwise, pick the highest wake level that is supported */ + WakeLevel = PowerDeviceUnspecified; + if (PowerCapabilities.PMC.Capabilities.Support.PMED0) + WakeLevel = PowerDeviceD0; + if (PowerCapabilities.PMC.Capabilities.Support.PMED1) + WakeLevel = PowerDeviceD1; + if (PowerCapabilities.PMC.Capabilities.Support.PMED2) + WakeLevel = PowerDeviceD2; + if (PowerCapabilities.PMC.Capabilities.Support.PMED3Hot) + WakeLevel = PowerDeviceD3; + if (PowerCapabilities.PMC.Capabilities.Support.PMED3Cold) + WakeLevel = PowerDeviceD3; + PdoExtension->PowerState.DeviceWakeLevel = WakeLevel; + + /* Convert the PCI power state to the NT power state */ + PdoExtension->PowerState.CurrentDeviceState = PowerCapabilities.PMCSR.ControlStatus.PowerState + 1; - /* Save all the power capabilities */ - PdoExtension->PowerCapabilities = PowerCapabilities.PMC.Capabilities; - DPRINT1("PM Caps Found! Wake Level: %d Power State: %d\n", - WakeLevel, PdoExtension->PowerState.CurrentDeviceState); + /* Save all the power capabilities */ + PdoExtension->PowerCapabilities = PowerCapabilities.PMC.Capabilities; + DPRINT1("PM Caps Found! Wake Level: %d Power State: %d\n", + WakeLevel, PdoExtension->PowerState.CurrentDeviceState); + } } } } diff --git a/reactos/drivers/bus/pcix/fdo.c b/reactos/drivers/bus/pcix/fdo.c index 847585e4483..785dfca3c52 100644 --- a/reactos/drivers/bus/pcix/fdo.c +++ b/reactos/drivers/bus/pcix/fdo.c @@ -127,7 +127,6 @@ PciFdoIrpQueryRemoveDevice(IN PIRP Irp, IN PPCI_FDO_EXTENSION DeviceExtension) { UNIMPLEMENTED; - while (TRUE); return STATUS_NOT_SUPPORTED; } diff --git a/reactos/drivers/bus/pcix/pdo.c b/reactos/drivers/bus/pcix/pdo.c index 11961c4b40c..4bbc524dec0 100644 --- a/reactos/drivers/bus/pcix/pdo.c +++ b/reactos/drivers/bus/pcix/pdo.c @@ -91,7 +91,6 @@ PciPdoSetPowerState(IN PIRP Irp, IN PPCI_PDO_EXTENSION DeviceExtension) { UNIMPLEMENTED; - while (TRUE); return STATUS_NOT_SUPPORTED; } @@ -210,7 +209,6 @@ PciPdoIrpQueryRemoveDevice(IN PIRP Irp, IN PPCI_PDO_EXTENSION DeviceExtension) { UNIMPLEMENTED; - while (TRUE); return STATUS_NOT_SUPPORTED; } @@ -429,7 +427,6 @@ PciPdoIrpQueryDeviceState(IN PIRP Irp, IN PPCI_PDO_EXTENSION DeviceExtension) { UNIMPLEMENTED; - while (TRUE); return STATUS_NOT_SUPPORTED; } diff --git a/reactos/drivers/drivers.rbuild b/reactos/drivers/drivers.rbuild index 4e3c60fa548..15b36eaeead 100644 --- a/reactos/drivers/drivers.rbuild +++ b/reactos/drivers/drivers.rbuild @@ -19,6 +19,12 @@ + + + + + diff --git a/reactos/drivers/filters/CMakeLists.txt b/reactos/drivers/filters/CMakeLists.txt new file mode 100644 index 00000000000..6e279b484dc --- /dev/null +++ b/reactos/drivers/filters/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory(mountmgr) diff --git a/reactos/drivers/filters/directory.rbuild b/reactos/drivers/filters/directory.rbuild new file mode 100644 index 00000000000..b3b19c440f4 --- /dev/null +++ b/reactos/drivers/filters/directory.rbuild @@ -0,0 +1,7 @@ + + + + + + + diff --git a/reactos/drivers/filters/mountmgr/CMakeLists.txt b/reactos/drivers/filters/mountmgr/CMakeLists.txt new file mode 100644 index 00000000000..4a99868931c --- /dev/null +++ b/reactos/drivers/filters/mountmgr/CMakeLists.txt @@ -0,0 +1,18 @@ + +list(APPEND SOURCE + database.c + device.c + mountmgr.c + notify.c + point.c + symlink.c + uniqueid.c + mountmgr.rc) + +add_library(mountmgr SHARED ${SOURCE}) +target_link_libraries(mountmgr wdmguid ioevent) +allow_warnings(mountmgr) +set_module_type(mountmgr kernelmodedriver) +add_importlibs(mountmgr ntoskrnl hal) +add_pch(mountmgr mntmgr.h) +add_cd_file(TARGET mountmgr DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/reactos/drivers/filters/mountmgr/database.c b/reactos/drivers/filters/mountmgr/database.c new file mode 100644 index 00000000000..badab174b4c --- /dev/null +++ b/reactos/drivers/filters/mountmgr/database.c @@ -0,0 +1,1561 @@ +/* + * ReactOS kernel + * Copyright (C) 2011-2012 ReactOS Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/filesystem/mountmgr/mountmgr.c + * PURPOSE: Mount Manager - remote/local database handler + * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) + */ + +/* INCLUDES *****************************************************************/ + +#include "mntmgr.h" + +#define NDEBUG +#include + +PWSTR DatabasePath = L"\\Registry\\Machine\\System\\MountedDevices"; +PWSTR OfflinePath = L"\\Registry\\Machine\\System\\MountedDevices\\Offline"; + +UNICODE_STRING RemoteDatabase = RTL_CONSTANT_STRING(L"\\System Volume Information\\MountPointManagerRemoteDatabase"); +UNICODE_STRING RemoteDatabaseFile = RTL_CONSTANT_STRING(L"\\:$MountMgrRemoteDatabase"); + +/* + * @implemented + */ +LONG +GetRemoteDatabaseSize(IN HANDLE Database) +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + FILE_STANDARD_INFORMATION StandardInfo; + + /* Just query the size */ + Status = ZwQueryInformationFile(Database, + &IoStatusBlock, + &StandardInfo, + sizeof(FILE_STANDARD_INFORMATION), + FileStandardInformation); + if (NT_SUCCESS(Status)) + { + return StandardInfo.EndOfFile.LowPart; + } + + return 0; +} + +/* + * @implemented + */ +NTSTATUS +AddRemoteDatabaseEntry(IN HANDLE Database, + IN PDATABASE_ENTRY Entry) +{ + LARGE_INTEGER Size; + IO_STATUS_BLOCK IoStatusBlock; + + /* Get size to append data */ + Size.QuadPart = GetRemoteDatabaseSize(Database); + + return ZwWriteFile(Database, 0, NULL, NULL, + &IoStatusBlock, Entry, + Entry->EntrySize, &Size, NULL); +} + +/* + * @implemented + */ +NTSTATUS +CloseRemoteDatabase(IN HANDLE Database) +{ + return ZwClose(Database); +} + +/* + * @implemented + */ +NTSTATUS +TruncateRemoteDatabase(IN HANDLE Database, + IN LONG NewSize) +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + FILE_END_OF_FILE_INFORMATION EndOfFile; + FILE_ALLOCATION_INFORMATION Allocation; + + EndOfFile.EndOfFile.QuadPart = NewSize; + Allocation.AllocationSize.QuadPart = NewSize; + + /* First set EOF */ + Status = ZwSetInformationFile(Database, + &IoStatusBlock, + &EndOfFile, + sizeof(FILE_END_OF_FILE_INFORMATION), + FileEndOfFileInformation); + if (NT_SUCCESS(Status)) + { + /* And then, properly set allocation information */ + Status = ZwSetInformationFile(Database, + &IoStatusBlock, + &Allocation, + sizeof(FILE_ALLOCATION_INFORMATION), + FileAllocationInformation); + } + + return Status; +} + +/* + * @implemented + */ +PDATABASE_ENTRY +GetRemoteDatabaseEntry(IN HANDLE Database, + IN LONG StartingOffset) +{ + NTSTATUS Status; + ULONG EntrySize; + PDATABASE_ENTRY Entry; + LARGE_INTEGER ByteOffset; + IO_STATUS_BLOCK IoStatusBlock; + + /* Get the entry at the given position */ + ByteOffset.QuadPart = StartingOffset; + Status = ZwReadFile(Database, + NULL, + NULL, + NULL, + &IoStatusBlock, + &EntrySize, + sizeof(EntrySize), + &ByteOffset, + NULL); + if (!NT_SUCCESS(Status)) + { + return NULL; + } + + /* If entry doesn't exist, truncate database */ + if (!EntrySize) + { + TruncateRemoteDatabase(Database, StartingOffset); + return NULL; + } + + /* Allocate the entry */ + Entry = AllocatePool(EntrySize); + if (!Entry) + { + return NULL; + } + + /* Effectively read the entry */ + Status = ZwReadFile(Database, + NULL, + NULL, + NULL, + &IoStatusBlock, + Entry, + EntrySize, + &ByteOffset, + NULL); + /* If it fails or returns inconsistent data, drop it (= truncate) */ + if (!NT_SUCCESS(Status) || IoStatusBlock.Information != EntrySize || EntrySize < sizeof(DATABASE_ENTRY)) + { + TruncateRemoteDatabase(Database, StartingOffset); + FreePool(Entry); + return NULL; + } + + /* Validate entry */ + if (MAX(Entry->SymbolicNameOffset + Entry->SymbolicNameLength, + Entry->UniqueIdOffset + Entry->UniqueIdLength) > EntrySize) + { + TruncateRemoteDatabase(Database, StartingOffset); + FreePool(Entry); + return NULL; + } + + return Entry; +} + +/* + * @implemented + */ +NTSTATUS +DeleteRemoteDatabaseEntry(IN HANDLE Database, + IN LONG StartingOffset) +{ + LONG EndSize; + PVOID TmpBuffer; + NTSTATUS Status; + LONG DatabaseSize; + PDATABASE_ENTRY Entry; + IO_STATUS_BLOCK IoStatusBlock; + LARGE_INTEGER EndEntriesOffset; + + /* First, get database size */ + DatabaseSize = GetRemoteDatabaseSize(Database); + if (!DatabaseSize) + { + return STATUS_INVALID_PARAMETER; + } + + /* Then, get the entry to remove */ + Entry = GetRemoteDatabaseEntry(Database, StartingOffset); + if (!Entry) + { + return STATUS_INVALID_PARAMETER; + } + + /* Validate parameters: ensure we won't get negative size */ + if (Entry->EntrySize + StartingOffset > DatabaseSize) + { + /* If we get invalid parameters, truncate the whole database + * starting the wrong entry. We can't rely on the rest + */ + FreePool(Entry); + return TruncateRemoteDatabase(Database, StartingOffset); + } + + /* Now, get the size of the remaining entries (those after the one to remove) */ + EndSize = DatabaseSize - Entry->EntrySize - StartingOffset; + /* Allocate a buffer big enough to hold them */ + TmpBuffer = AllocatePool(EndSize); + if (!TmpBuffer) + { + FreePool(Entry); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Get the offset of the entry right after the one to delete */ + EndEntriesOffset.QuadPart = Entry->EntrySize + StartingOffset; + /* We don't need the entry any more */ + FreePool(Entry); + + /* Read the ending entries */ + Status = ZwReadFile(Database, NULL, NULL, NULL, &IoStatusBlock, + TmpBuffer, EndSize, &EndEntriesOffset, NULL); + if (!NT_SUCCESS(Status)) + { + FreePool(TmpBuffer); + return Status; + } + + /* Ensure nothing went wrong - we don't want to corrupt the DB */ + if (IoStatusBlock.Information != EndSize) + { + FreePool(TmpBuffer); + return STATUS_INVALID_PARAMETER; + } + + /* Remove the entry */ + Status = TruncateRemoteDatabase(Database, StartingOffset + EndSize); + if (!NT_SUCCESS(Status)) + { + FreePool(TmpBuffer); + return Status; + } + + /* Now, shift the ending entries to erase the entry */ + EndEntriesOffset.QuadPart = StartingOffset; + Status = ZwWriteFile(Database, NULL, NULL, NULL, &IoStatusBlock, + TmpBuffer, EndSize, &EndEntriesOffset, NULL); + + FreePool(TmpBuffer); + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +DeleteFromLocalDatabaseRoutine(IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext) +{ + PMOUNTDEV_UNIQUE_ID UniqueId = Context; + + /* Ensure it matches, and delete */ + if ((UniqueId->UniqueIdLength == ValueLength) && + (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) == + ValueLength)) + { + RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + ValueName); + } + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +VOID +DeleteFromLocalDatabase(IN PUNICODE_STRING SymbolicLink, + IN PMOUNTDEV_UNIQUE_ID UniqueId) +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].QueryRoutine = DeleteFromLocalDatabaseRoutine; + QueryTable[0].Name = SymbolicLink->Buffer; + + RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + QueryTable, + UniqueId, + NULL); +} + +/* + * @implemented + */ +NTSTATUS +WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension) +{ + NTSTATUS Status; + LARGE_INTEGER Timeout; + + /* Wait for 7 minutes */ + Timeout.QuadPart = 0xFA0A1F00; + Status = KeWaitForSingleObject(&(DeviceExtension->RemoteDatabaseLock), Executive, KernelMode, FALSE, &Timeout); + if (Status != STATUS_TIMEOUT) + { + return Status; + } + + return STATUS_IO_TIMEOUT; +} + +/* + * @implemented + */ +VOID +ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION DeviceExtension) +{ + KeReleaseSemaphore(&(DeviceExtension->RemoteDatabaseLock), IO_NO_INCREMENT, 1, FALSE); +} + +VOID +NTAPI +ReconcileThisDatabaseWithMasterWorker(IN PVOID Parameter) +{ + return; +} + +/* + * @implemented + */ +VOID +NTAPI +WorkerThread(IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context) +{ + ULONG i; + KEVENT Event; + KIRQL OldIrql; + NTSTATUS Status; + HANDLE SafeEvent; + PLIST_ENTRY Entry; + LARGE_INTEGER Timeout; + PRECONCILE_WORK_ITEM WorkItem; + PDEVICE_EXTENSION DeviceExtension; + OBJECT_ATTRIBUTES ObjectAttributes; + + InitializeObjectAttributes(&ObjectAttributes, + &SafeVolumes, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Timeout.LowPart = 0xFFFFFFFF; + Timeout.HighPart = 0xFF676980; + + /* Try to wait as long as possible */ + for (i = (Unloading ? 999 : 0); i < 1000; i++) + { + Status = ZwOpenEvent(&SafeEvent, EVENT_ALL_ACCESS, &ObjectAttributes); + if (NT_SUCCESS(Status)) + { + break; + } + + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout); + } + + if (i < 1000) + { + do + { + Status = ZwWaitForSingleObject(SafeEvent, FALSE, &Timeout); + } + while (Status == STATUS_TIMEOUT && !Unloading); + + ZwClose(SafeEvent); + } + + DeviceExtension = Context; + + InterlockedExchange(&(DeviceExtension->WorkerThreadStatus), 1); + + /* Acquire workers lock */ + KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL); + + OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock)); + /* Ensure there are workers */ + while (!IsListEmpty(&(DeviceExtension->WorkerQueueListHead))) + { + /* Unqueue a worker */ + Entry = RemoveHeadList(&(DeviceExtension->WorkerQueueListHead)); + WorkItem = CONTAINING_RECORD(Entry, + RECONCILE_WORK_ITEM, + WorkerQueueListEntry); + + KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql); + + /* Call it */ + WorkItem->WorkerRoutine(WorkItem->Context); + + IoFreeWorkItem(WorkItem->WorkItem); + FreePool(WorkItem); + + if (InterlockedDecrement(&(DeviceExtension->WorkerReferences)) == 0) + { + return; + } + + KeWaitForSingleObject(&(DeviceExtension->WorkerSemaphore), Executive, KernelMode, FALSE, NULL); + OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock)); + } + KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql); + + InterlockedDecrement(&(DeviceExtension->WorkerReferences)); + + /* Reset event */ + KeSetEvent(&UnloadEvent, IO_NO_INCREMENT, FALSE); +} + +/* + * @implemented + */ +NTSTATUS +QueueWorkItem(IN PDEVICE_EXTENSION DeviceExtension, + IN PRECONCILE_WORK_ITEM WorkItem, + IN PVOID Context) +{ + KIRQL OldIrql; + + WorkItem->Context = Context; + + /* When called, lock is already acquired */ + + /* If noone, start to work */ + if (InterlockedIncrement(&(DeviceExtension->WorkerReferences))) + { + IoQueueWorkItem(WorkItem->WorkItem, WorkerThread, DelayedWorkQueue, DeviceExtension); + } + + /* Otherwise queue worker for delayed execution */ + OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock)); + InsertTailList(&(DeviceExtension->WorkerQueueListHead), + &(WorkItem->WorkerQueueListEntry)); + KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql); + + KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), IO_NO_INCREMENT, 1, FALSE); + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +NTSTATUS +QueryVolumeName(IN HANDLE RootDirectory, + IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation, + IN PUNICODE_STRING FileName OPTIONAL, + OUT PUNICODE_STRING SymbolicName, + OUT PUNICODE_STRING VolumeName) +{ + HANDLE Handle; + NTSTATUS Status; + ULONG NeededLength; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + PFILE_NAME_INFORMATION FileNameInfo; + PREPARSE_DATA_BUFFER ReparseDataBuffer; + + if (!FileName) + { + InitializeObjectAttributes(&ObjectAttributes, + NULL, + OBJ_KERNEL_HANDLE, + RootDirectory, + NULL); + } + else + { + InitializeObjectAttributes(&ObjectAttributes, + FileName, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + NULL, + NULL); + } + + /* Open volume */ + Status = ZwOpenFile(&Handle, + SYNCHRONIZE | FILE_READ_ATTRIBUTES, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + (FileName) ? FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT : + FILE_OPEN_BY_FILE_ID | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Get the reparse point data */ + ReparseDataBuffer = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + if (!ReparseDataBuffer) + { + ZwClose(Handle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = ZwFsControlFile(Handle, + 0, + NULL, + NULL, + &IoStatusBlock, + FSCTL_GET_REPARSE_POINT, + NULL, + 0, + ReparseDataBuffer, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + if (!NT_SUCCESS(Status)) + { + FreePool(ReparseDataBuffer); + ZwClose(Handle); + return Status; + } + + /* Check that name can fit in buffer */ + if (ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL) > SymbolicName->MaximumLength) + { + FreePool(ReparseDataBuffer); + ZwClose(Handle); + return STATUS_BUFFER_TOO_SMALL; + } + + /* Copy symoblic name */ + SymbolicName->Length = ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength; + RtlCopyMemory(SymbolicName->Buffer, + (PWSTR)((ULONG_PTR)ReparseDataBuffer->MountPointReparseBuffer.PathBuffer + + ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameOffset), + ReparseDataBuffer->MountPointReparseBuffer.SubstituteNameLength); + + FreePool(ReparseDataBuffer); + + /* Name has to \ terminated */ + if (SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR) - 1] != L'\\') + { + ZwClose(Handle); + return STATUS_INVALID_PARAMETER; + } + + /* So that we can delete it, and match mountmgr requirements */ + SymbolicName->Length -= sizeof(WCHAR); + SymbolicName->Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL; + + /* Also ensure it's really a volume name... */ + if (!MOUNTMGR_IS_VOLUME_NAME(SymbolicName)) + { + ZwClose(Handle); + return STATUS_INVALID_PARAMETER; + } + + /* Now prepare to really get the name */ + FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR)); + if (!FileNameInfo) + { + ZwClose(Handle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = ZwQueryInformationFile(Handle, + &IoStatusBlock, + FileNameInfo, + sizeof(FILE_NAME_INFORMATION) + 2 * sizeof(WCHAR), + FileNameInformation); + if (Status == STATUS_BUFFER_OVERFLOW) + { + /* As expected... Reallocate with proper size */ + NeededLength = FileNameInfo->FileNameLength; + FreePool(FileNameInfo); + + FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + NeededLength); + if (!FileNameInfo) + { + ZwClose(Handle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* And query name */ + Status = ZwQueryInformationFile(Handle, + &IoStatusBlock, + FileNameInfo, + sizeof(FILE_NAME_INFORMATION) + NeededLength, + FileNameInformation); + } + + ZwClose(Handle); + + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Return the volume name */ + VolumeName->Length = FileNameInfo->FileNameLength; + VolumeName->MaximumLength = FileNameInfo->FileNameLength + sizeof(WCHAR); + VolumeName->Buffer = AllocatePool(VolumeName->MaximumLength); + if (!VolumeName->Buffer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(VolumeName->Buffer, FileNameInfo->FileName, FileNameInfo->FileNameLength); + VolumeName->Buffer[FileNameInfo->FileNameLength / sizeof(WCHAR)] = UNICODE_NULL; + + FreePool(FileNameInfo); + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +VOID +OnlineMountedVolumes(IN PDEVICE_EXTENSION DeviceExtension, + IN PDEVICE_INFORMATION DeviceInformation) +{ + HANDLE Handle; + NTSTATUS Status; + BOOLEAN RestartScan; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + PDEVICE_INFORMATION VolumeDeviceInformation; + WCHAR FileNameBuffer[0x8], SymbolicNameBuffer[0x64]; + UNICODE_STRING ReparseFile, FileName, SymbolicName, VolumeName; + FILE_REPARSE_POINT_INFORMATION ReparsePointInformation, SavedReparsePointInformation; + + /* Removable devices don't have remote database on them */ + if (DeviceInformation->Removable) + { + return; + } + + /* Prepare a string with reparse point index */ + ReparseFile.Length = DeviceInformation->DeviceName.Length + ReparseIndex.Length; + ReparseFile.MaximumLength = ReparseFile.Length + sizeof(UNICODE_NULL); + ReparseFile.Buffer = AllocatePool(ReparseFile.MaximumLength); + if (!ReparseFile.Buffer) + { + return; + } + + RtlCopyMemory(ReparseFile.Buffer, DeviceInformation->DeviceName.Buffer, + DeviceInformation->DeviceName.Length); + RtlCopyMemory((PVOID)((ULONG_PTR)ReparseFile.Buffer + DeviceInformation->DeviceName.Length), + ReparseFile.Buffer, ReparseFile.Length); + ReparseFile.Buffer[ReparseFile.Length / sizeof(WCHAR)] = UNICODE_NULL; + + InitializeObjectAttributes(&ObjectAttributes, + &ReparseFile, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + /* Open reparse point */ + Status = ZwOpenFile(&Handle, + FILE_GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_REPARSE_POINT); + FreePool(ReparseFile.Buffer); + if (!NT_SUCCESS(Status)) + { + DeviceInformation->NoDatabase = FALSE; + return; + } + + /* Query reparse point information + * We only pay attention to mout point + */ + RtlZeroMemory(FileNameBuffer, sizeof(FileNameBuffer)); + FileName.Buffer = FileNameBuffer; + FileName.Length = sizeof(FileNameBuffer); + FileName.MaximumLength = sizeof(FileNameBuffer); + ((PULONG)FileNameBuffer)[0] = IO_REPARSE_TAG_MOUNT_POINT; + Status = ZwQueryDirectoryFile(Handle, + NULL, + NULL, + NULL, + &IoStatusBlock, + &ReparsePointInformation, + sizeof(FILE_REPARSE_POINT_INFORMATION), + FileReparsePointInformation, + TRUE, + &FileName, + FALSE); + if (!NT_SUCCESS(Status)) + { + ZwClose(Handle); + return; + } + + /* Query mount points */ + while (TRUE) + { + RestartScan = TRUE; + SymbolicName.Length = 0; + SymbolicName.MaximumLength = sizeof(SymbolicNameBuffer); + SymbolicName.Buffer = SymbolicNameBuffer; + RtlCopyMemory(&SavedReparsePointInformation, &ReparsePointInformation, sizeof(FILE_REPARSE_POINT_INFORMATION)); + + Status = ZwQueryDirectoryFile(Handle, + NULL, + NULL, + NULL, + &IoStatusBlock, + &ReparsePointInformation, + sizeof(FILE_REPARSE_POINT_INFORMATION), + FileReparsePointInformation, + TRUE, + (RestartScan) ? &FileName : NULL, + RestartScan); + if (!RestartScan) + { + if (ReparsePointInformation.FileReference == SavedReparsePointInformation.FileReference && + ReparsePointInformation.Tag == SavedReparsePointInformation.Tag) + { + ZwClose(Handle); + return; + } + } + else + { + RestartScan = FALSE; + } + + if (!NT_SUCCESS(Status) || ReparsePointInformation.Tag != IO_REPARSE_TAG_MOUNT_POINT) + { + ZwClose(Handle); + return; + } + + /* Get the volume name associated to the mount point */ + Status = QueryVolumeName(Handle, + &ReparsePointInformation, + NULL, &SymbolicName, + &VolumeName); + if (!NT_SUCCESS(Status)) + { + continue; + } + + FreePool(VolumeName.Buffer); + + /* Get its information */ + Status = FindDeviceInfo(DeviceExtension, &SymbolicName, + FALSE, &VolumeDeviceInformation); + if (!NT_SUCCESS(Status)) + { + DeviceInformation->NoDatabase = TRUE; + continue; + } + + /* If notification are enabled, mark it online */ + if (!DeviceInformation->SkipNotifications) + { + PostOnlineNotification(DeviceExtension, &VolumeDeviceInformation->SymbolicName); + } + } +} + +/* + * @implemented + */ +VOID +ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION DeviceExtension, + IN PDEVICE_INFORMATION DeviceInformation) +{ + PRECONCILE_WORK_ITEM WorkItem; + + /* Removable devices don't have remote database */ + if (DeviceInformation->Removable) + { + return; + } + + /* Allocate a work item */ + WorkItem = AllocatePool(sizeof(RECONCILE_WORK_ITEM)); + if (!WorkItem) + { + return; + } + + WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject); + if (!WorkItem->WorkItem) + { + FreePool(WorkItem); + return; + } + + /* And queue it */ + WorkItem->WorkerRoutine = ReconcileThisDatabaseWithMasterWorker; + WorkItem->DeviceExtension = DeviceExtension; + WorkItem->DeviceInformation = DeviceInformation; + QueueWorkItem(DeviceExtension, WorkItem, &(WorkItem->DeviceExtension)); + + /* If there's no automount, and automatic letters + * all volumes to find those online and notify there presence + */ + if (DeviceExtension->WorkerThreadStatus == 0 && + DeviceExtension->AutomaticDriveLetter == 1 && + DeviceExtension->NoAutoMount == FALSE) + { + OnlineMountedVolumes(DeviceExtension, DeviceInformation); + } +} + +/* + * @implemented + */ +VOID +ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION DeviceExtension) +{ + PLIST_ENTRY NextEntry; + PDEVICE_INFORMATION DeviceInformation; + + /* Browse all the devices */ + for (NextEntry = DeviceExtension->DeviceListHead.Flink; + NextEntry != &(DeviceExtension->DeviceListHead); + NextEntry = NextEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(NextEntry, + DEVICE_INFORMATION, + DeviceListEntry); + /* If it's not removable, then, it might have a database to sync */ + if (!DeviceInformation->Removable) + { + ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation); + } + } +} + +/* + * @implemented + */ +VOID +NTAPI +MigrateRemoteDatabaseWorker(IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context) +{ + ULONG Length; + NTSTATUS Status; + PVOID TmpBuffer; + CHAR Disposition; + LARGE_INTEGER ByteOffset; + PMIGRATE_WORK_ITEM WorkItem; + IO_STATUS_BLOCK IoStatusBlock; + HANDLE Migrate = 0, Database = 0; + PDEVICE_INFORMATION DeviceInformation; + BOOLEAN PreviousMode, Complete = FALSE; + UNICODE_STRING DatabaseName, DatabaseFile; + OBJECT_ATTRIBUTES ObjectAttributes, MigrateAttributes; +#define TEMP_BUFFER_SIZE 0x200 + + /* Extract context */ + WorkItem = Context; + DeviceInformation = WorkItem->DeviceInformation; + + /* Reconstruct appropriate string */ + DatabaseName.Length = DeviceInformation->DeviceName.Length + RemoteDatabase.Length; + DatabaseName.MaximumLength = DatabaseName.Length + sizeof(WCHAR); + + DatabaseFile.Length = DeviceInformation->DeviceName.Length + RemoteDatabaseFile.Length; + DatabaseFile.MaximumLength = DatabaseFile.Length + sizeof(WCHAR); + + DatabaseName.Buffer = AllocatePool(DatabaseName.MaximumLength); + DatabaseFile.Buffer = AllocatePool(DatabaseFile.MaximumLength); + /* Allocate buffer that will be used to swap contents */ + TmpBuffer = AllocatePool(TEMP_BUFFER_SIZE); + if (!DatabaseName.Buffer || !DatabaseFile.Buffer || !TmpBuffer) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + /* Create the required folder (in which the database will be stored + * \System Volume Information at root of the volume + */ + Status = RtlCreateSystemVolumeInformationFolder(&(DeviceInformation->DeviceName)); + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + + /* Finish initating strings */ + RtlCopyMemory(DatabaseName.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length); + RtlCopyMemory(DatabaseFile.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length); + RtlCopyMemory(DatabaseName.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)), + RemoteDatabase.Buffer, RemoteDatabase.Length); + RtlCopyMemory(DatabaseFile.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)), + RemoteDatabaseFile.Buffer, RemoteDatabaseFile.Length); + DatabaseName.Buffer[DatabaseName.Length / sizeof(WCHAR)] = UNICODE_NULL; + DatabaseFile.Buffer[DatabaseFile.Length / sizeof(WCHAR)] = UNICODE_NULL; + + /* Create database */ + InitializeObjectAttributes(&ObjectAttributes, + &DatabaseName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateFile(&Database, + SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | + FILE_READ_ATTRIBUTES | FILE_WRITE_PROPERTIES | FILE_READ_PROPERTIES | + FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, + 0, + FILE_CREATE, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, + NULL, + 0); + if (!NT_SUCCESS(Status)) + { + Database = 0; + goto Cleanup; + } + + InitializeObjectAttributes(&MigrateAttributes, + &DatabaseFile, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + /* Disable hard errors and open the database that will be copied */ + PreviousMode = IoSetThreadHardErrorMode(FALSE); + Status = ZwCreateFile(&Migrate, + SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | + FILE_READ_ATTRIBUTES | FILE_WRITE_PROPERTIES | FILE_READ_PROPERTIES | + FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA, + &MigrateAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, + 0, + FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, + NULL, + 0); + IoSetThreadHardErrorMode(PreviousMode); + if (!NT_SUCCESS(Status)) + { + Migrate = 0; + } + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) + { + Status == STATUS_SUCCESS; + Complete = TRUE; + } + if (!NT_SUCCESS(Status) || Complete) + { + goto Cleanup; + } + + ByteOffset.QuadPart = 0LL; + PreviousMode = IoSetThreadHardErrorMode(FALSE); + /* Now, loop as long it's possible */ + while (Status == STATUS_SUCCESS) + { + /* Read data from existing database */ + Status = ZwReadFile(Migrate, + NULL, + NULL, + NULL, + &IoStatusBlock, + TmpBuffer, + TEMP_BUFFER_SIZE, + &ByteOffset, + NULL); + if (!NT_SUCCESS(Status)) + { + break; + } + + /* And write them into new database */ + Length = IoStatusBlock.Information; + Status = ZwWriteFile(Database, + NULL, + NULL, + NULL, + &IoStatusBlock, + TmpBuffer, + Length, + &ByteOffset, + NULL); + ByteOffset.QuadPart += Length; + } + IoSetThreadHardErrorMode(PreviousMode); + + /* Delete old databse if it was well copied */ + if (Status == STATUS_END_OF_FILE) + { + Disposition = 1; + Status = ZwSetInformationFile(Migrate, + &IoStatusBlock, + &Disposition, + sizeof(Disposition), + FileDispositionInformation); + } + + /* Migration is over */ + +Cleanup: + if (TmpBuffer) + { + FreePool(TmpBuffer); + } + + if (DatabaseFile.Buffer) + { + FreePool(DatabaseFile.Buffer); + } + + if (DatabaseName.Buffer) + { + FreePool(DatabaseName.Buffer); + } + + if (Migrate) + { + ZwClose(Migrate); + } + + if (NT_SUCCESS(Status)) + { + DeviceInformation->Migrated = 1; + } + else if (Database) + { + ZwClose(Database); + } + + IoFreeWorkItem(WorkItem->WorkItem); + + WorkItem->WorkItem = NULL; + WorkItem->Status = Status; + WorkItem->Database = Database; + + KeSetEvent(WorkItem->Event, 0, FALSE); +#undef TEMP_BUFFER_SIZE +} + +/* + * @implemented + */ +NTSTATUS +MigrateRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation, + IN OUT PHANDLE Database) +{ + KEVENT Event; + NTSTATUS Status; + PMIGRATE_WORK_ITEM WorkItem; + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + /* Allocate a work item dedicated to migration */ + WorkItem = AllocatePool(sizeof(MIGRATE_WORK_ITEM)); + if (!WorkItem) + { + *Database = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(WorkItem, sizeof(MIGRATE_WORK_ITEM)); + WorkItem->Event = &Event; + WorkItem->DeviceInformation = DeviceInformation; + WorkItem->WorkItem = IoAllocateWorkItem(DeviceInformation->DeviceExtension->DeviceObject); + if (!WorkItem->WorkItem) + { + FreePool(WorkItem); + *Database = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* And queue it */ + IoQueueWorkItem(WorkItem->WorkItem, + MigrateRemoteDatabaseWorker, + DelayedWorkQueue, + WorkItem); + + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = WorkItem->Status; + + *Database = (NT_SUCCESS(Status) ? WorkItem->Database : 0); + + FreePool(WorkItem); + return Status; +} + +/* + * @implemented + */ +HANDLE +OpenRemoteDatabase(IN PDEVICE_INFORMATION DeviceInformation, + IN BOOLEAN MigrateDatabase) +{ + HANDLE Database; + NTSTATUS Status; + BOOLEAN PreviousMode; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING DeviceRemoteDatabase; + + Database = 0; + + /* Get database name */ + DeviceRemoteDatabase.Length = DeviceInformation->DeviceName.Length + RemoteDatabase.Length; + DeviceRemoteDatabase.MaximumLength = DeviceRemoteDatabase.Length + sizeof(WCHAR); + DeviceRemoteDatabase.Buffer = AllocatePool(DeviceRemoteDatabase.MaximumLength); + if (!DeviceRemoteDatabase.Buffer) + { + return 0; + } + + RtlCopyMemory(DeviceRemoteDatabase.Buffer, DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length); + RtlCopyMemory(DeviceRemoteDatabase.Buffer + (DeviceInformation->DeviceName.Length / sizeof(WCHAR)), + RemoteDatabase.Buffer, RemoteDatabase.Length); + DeviceRemoteDatabase.Buffer[DeviceRemoteDatabase.Length / sizeof(WCHAR)] = UNICODE_NULL; + + /* Open database */ + InitializeObjectAttributes(&ObjectAttributes, + &DeviceRemoteDatabase, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + /* Disable hard errors */ + PreviousMode = IoSetThreadHardErrorMode(FALSE); + + Status = ZwCreateFile(&Database, + SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | + FILE_READ_ATTRIBUTES | FILE_WRITE_PROPERTIES | FILE_READ_PROPERTIES | + FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, + 0, + (!MigrateDatabase || DeviceInformation->Migrated == 0) ? FILE_OPEN_IF : FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT, + NULL, + 0); + + /* If base it to be migrated and was opened successfully, go ahead */ + if (MigrateDatabase && NT_SUCCESS(Status)) + { + MigrateRemoteDatabase(DeviceInformation, &Database); + } + + IoSetThreadHardErrorMode(PreviousMode); + FreePool(DeviceRemoteDatabase.Buffer); + + return Database; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +QueryUniqueIdQueryRoutine(IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext) +{ + PMOUNTDEV_UNIQUE_ID IntUniqueId; + PMOUNTDEV_UNIQUE_ID * UniqueId; + + /* Sanity check */ + if (ValueLength >= 0x10000) + { + return STATUS_SUCCESS; + } + + /* Allocate the Unique ID */ + IntUniqueId = AllocatePool(sizeof(UniqueId) + ValueLength); + if (IntUniqueId) + { + /* Copy data & return */ + IntUniqueId->UniqueIdLength = ValueLength; + RtlCopyMemory(&(IntUniqueId->UniqueId), ValueData, ValueLength); + + UniqueId = Context; + *UniqueId = IntUniqueId; + } + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +NTSTATUS +QueryUniqueIdFromMaster(IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicName, + OUT PMOUNTDEV_UNIQUE_ID * UniqueId) +{ + NTSTATUS Status; + PDEVICE_INFORMATION DeviceInformation; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + + /* Query the unique ID */ + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].QueryRoutine = QueryUniqueIdQueryRoutine; + QueryTable[0].Name = SymbolicName->Buffer; + + *UniqueId = NULL; + RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + QueryTable, + UniqueId, + NULL); + /* Unique ID found, no need to go farther */ + if (*UniqueId) + { + return STATUS_SUCCESS; + } + + /* Otherwise, find associate device information */ + Status = FindDeviceInfo(DeviceExtension, SymbolicName, FALSE, &DeviceInformation); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + *UniqueId = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); + if (!*UniqueId) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Return this unique ID (better than nothing) */ + (*UniqueId)->UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength; + RtlCopyMemory(&((*UniqueId)->UniqueId), &(DeviceInformation->UniqueId->UniqueId), (*UniqueId)->UniqueIdLength); + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +NTSTATUS +WriteUniqueIdToMaster(IN PDEVICE_EXTENSION DeviceExtension, + IN PDATABASE_ENTRY DatabaseEntry) +{ + NTSTATUS Status; + PWCHAR SymbolicName; + PLIST_ENTRY NextEntry; + UNICODE_STRING SymbolicString; + PDEVICE_INFORMATION DeviceInformation; + + /* Create symbolic name from database entry */ + SymbolicName = AllocatePool(DatabaseEntry->SymbolicNameLength + sizeof(WCHAR)); + if (!SymbolicName) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(SymbolicName, + (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset), + DatabaseEntry->SymbolicNameLength); + SymbolicName[DatabaseEntry->SymbolicNameLength / sizeof(WCHAR)] = UNICODE_NULL; + + /* Associate the unique ID with the name from remote database */ + Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + SymbolicName, + REG_BINARY, + (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), + DatabaseEntry->UniqueIdLength); + FreePool(SymbolicName); + + /* Reget symbolic name */ + SymbolicString.Length = DatabaseEntry->SymbolicNameLength; + SymbolicString.MaximumLength = DatabaseEntry->SymbolicNameLength; + SymbolicString.Buffer = (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset); + + /* Find the device using this unique ID */ + for (NextEntry = DeviceExtension->DeviceListHead.Flink; + NextEntry != &(DeviceExtension->DeviceListHead); + NextEntry = NextEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(NextEntry, + DEVICE_INFORMATION, + DeviceListEntry); + + if (DeviceInformation->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength) + { + continue; + } + + if (RtlCompareMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), + DeviceInformation->UniqueId->UniqueId, + DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength) + { + break; + } + } + + /* If found, create a mount point */ + if (NextEntry != &(DeviceExtension->DeviceListHead)) + { + MountMgrCreatePointWorker(DeviceExtension, &SymbolicString, &(DeviceInformation->DeviceName)); + } + + return Status; +} + +/* + * @implemented + */ +VOID +ChangeRemoteDatabaseUniqueId(IN PDEVICE_INFORMATION DeviceInformation, + IN PMOUNTDEV_UNIQUE_ID OldUniqueId, + IN PMOUNTDEV_UNIQUE_ID NewUniqueId) +{ + LONG Offset = 0; + HANDLE Database; + PDATABASE_ENTRY Entry, NewEntry; + NTSTATUS Status = STATUS_SUCCESS; + + /* Open the remote database */ + Database = OpenRemoteDatabase(DeviceInformation, FALSE); + if (!Database) + { + return; + } + + /* Get all the entries */ + do + { + Entry = GetRemoteDatabaseEntry(Database, Offset); + if (!Entry) + { + break; + } + + /* Not the correct entry, skip it */ + if (Entry->UniqueIdLength != OldUniqueId->UniqueIdLength) + { + Offset += Entry->EntrySize; + FreePool(Entry); + continue; + } + + /* Not the correct entry, skip it */ + if (RtlCompareMemory(OldUniqueId->UniqueId, + (PVOID)((ULONG_PTR)Entry + Entry->UniqueIdOffset), + Entry->UniqueIdLength) != Entry->UniqueIdLength) + { + Offset += Entry->EntrySize; + FreePool(Entry); + continue; + } + + /* Here, we have the correct entry */ + NewEntry = AllocatePool(Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength); + if (!NewEntry) + { + Offset += Entry->EntrySize; + FreePool(Entry); + continue; + } + + /* Recreate the entry from the previous one */ + NewEntry->EntrySize = Entry->EntrySize + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength; + NewEntry->DatabaseOffset = Entry->DatabaseOffset; + NewEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY); + NewEntry->SymbolicNameLength = Entry->SymbolicNameLength; + NewEntry->UniqueIdOffset = Entry->SymbolicNameLength + sizeof(DATABASE_ENTRY); + NewEntry->UniqueIdLength = NewUniqueId->UniqueIdLength; + RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->SymbolicNameOffset), + (PVOID)((ULONG_PTR)Entry + Entry->SymbolicNameOffset), + NewEntry->SymbolicNameLength); + RtlCopyMemory((PVOID)((ULONG_PTR)NewEntry + NewEntry->UniqueIdOffset), + NewUniqueId->UniqueId, NewEntry->UniqueIdLength); + + /* Delete old entry */ + Status = DeleteRemoteDatabaseEntry(Database, Offset); + if (!NT_SUCCESS(Status)) + { + FreePool(Entry); + FreePool(NewEntry); + break; + } + + /* And replace with new one */ + Status = AddRemoteDatabaseEntry(Database, NewEntry); + FreePool(Entry); + FreePool(NewEntry); + } while (NT_SUCCESS(Status)); + + CloseRemoteDatabase(Database); + + return; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +DeleteDriveLetterRoutine(IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext) +{ + PMOUNTDEV_UNIQUE_ID UniqueId; + UNICODE_STRING RegistryEntry; + + if (ValueType != REG_BINARY) + { + return STATUS_SUCCESS; + } + + UniqueId = Context; + + /* First ensure we have the correct data */ + if (UniqueId->UniqueIdLength != ValueLength) + { + return STATUS_SUCCESS; + } + + if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) + { + return STATUS_SUCCESS; + } + + RtlInitUnicodeString(&RegistryEntry, ValueName); + + /* Then, it's a drive letter, erase it */ + if (IsDriveLetter(&RegistryEntry)) + { + RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + ValueName); + } + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +VOID +DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId) +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].QueryRoutine = DeleteDriveLetterRoutine; + + RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + QueryTable, + UniqueId, + NULL); +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +DeleteNoDriveLetterEntryRoutine(IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext) +{ + PMOUNTDEV_UNIQUE_ID UniqueId = Context; + + /* Ensure we have correct input */ + if (ValueName[0] != L'#' || ValueType != REG_BINARY || + UniqueId->UniqueIdLength != ValueLength) + { + return STATUS_SUCCESS; + } + + /* And then, if unique ID matching, delete entry */ + if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) + { + RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + ValueName); + } + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +VOID +DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId) +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine; + + RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + QueryTable, + UniqueId, + NULL); +} diff --git a/reactos/drivers/filters/mountmgr/device.c b/reactos/drivers/filters/mountmgr/device.c new file mode 100644 index 00000000000..6bc7d819da2 --- /dev/null +++ b/reactos/drivers/filters/mountmgr/device.c @@ -0,0 +1,1601 @@ +/* + * ReactOS kernel + * Copyright (C) 2011-2012 ReactOS Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/filesystem/mountmgr/device.c + * PURPOSE: Mount Manager - Device Control + * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) + */ + +/* INCLUDES *****************************************************************/ + +#include "mntmgr.h" + +#define NDEBUG +#include + +/* + * @implemented + */ +NTSTATUS +MountMgrChangeNotify(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + KIRQL OldIrql; + NTSTATUS Status; + PIO_STACK_LOCATION Stack; + PMOUNTMGR_CHANGE_NOTIFY_INFO ChangeNotify; + + /* Get the I/O buffer */ + Stack = IoGetCurrentIrpStackLocation(Irp); + ChangeNotify = (PMOUNTMGR_CHANGE_NOTIFY_INFO)Irp->AssociatedIrp.SystemBuffer; + + /* Validate it */ + if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO) || + Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO)) + { + return STATUS_INVALID_PARAMETER; + } + + /* If epic number doesn't match, just return now one */ + if (DeviceExtension->EpicNumber != ChangeNotify->EpicNumber) + { + ChangeNotify->EpicNumber = DeviceExtension->EpicNumber; + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + } + + /* If IRP is to be canceled, forget about that */ + IoAcquireCancelSpinLock(&OldIrql); + if (Irp->Cancel) + { + Status = STATUS_CANCELLED; + } + /* Otherwise queue the IRP to be notified with the next epic number change */ + else + { + InsertTailList(&(DeviceExtension->IrpListHead), &(Irp->Tail.Overlay.ListEntry)); + IoMarkIrpPending(Irp); + IoSetCancelRoutine(Irp, MountMgrCancel); + Status = STATUS_PENDING; + } + IoReleaseCancelSpinLock(OldIrql); + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +MountmgrWriteNoAutoMount(IN PDEVICE_EXTENSION DeviceExtension) +{ + ULONG Value = DeviceExtension->NoAutoMount; + + return RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DeviceExtension->RegistryPath.Buffer, + L"NoAutoMount", + REG_DWORD, + &Value, + sizeof(Value)); + +} + +/* + * @implemented + */ +NTSTATUS +MountMgrSetAutoMount(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + PIO_STACK_LOCATION Stack; + PMOUNTMGR_SET_AUTO_MOUNT SetState; + + Stack = IoGetCurrentIrpStackLocation(Irp); + + if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_SET_AUTO_MOUNT)) + { + Irp->IoStatus.Information = 0; + return STATUS_INVALID_PARAMETER; + } + + /* Only change if there's a real difference */ + SetState = (PMOUNTMGR_SET_AUTO_MOUNT)Irp->AssociatedIrp.SystemBuffer; + if (SetState->NewState == !DeviceExtension->NoAutoMount) + { + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + } + + /* Set new state; ! on purpose */ + DeviceExtension->NoAutoMount = !SetState->NewState; + Irp->IoStatus.Information = 0; + return MountmgrWriteNoAutoMount(DeviceExtension); +} + +/* + * @implemented + */ +NTSTATUS +MountMgrQueryAutoMount(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + PIO_STACK_LOCATION Stack; + PMOUNTMGR_QUERY_AUTO_MOUNT QueryState; + + Stack = IoGetCurrentIrpStackLocation(Irp); + + if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_QUERY_AUTO_MOUNT)) + { + Irp->IoStatus.Information = 0; + return STATUS_INVALID_PARAMETER; + } + + QueryState = (PMOUNTMGR_QUERY_AUTO_MOUNT)Irp->AssociatedIrp.SystemBuffer; + QueryState->CurrentState = !DeviceExtension->NoAutoMount; + Irp->IoStatus.Information = sizeof(MOUNTMGR_QUERY_AUTO_MOUNT); + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +ScrubRegistryRoutine(IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext) +{ + NTSTATUS Status; + PLIST_ENTRY NextEntry; + PDEVICE_INFORMATION DeviceInfo; + PBOOLEAN Continue = EntryContext; + PDEVICE_EXTENSION DeviceExtension = Context; + + if (ValueType != REG_BINARY) + { + return STATUS_SUCCESS; + } + + /* Delete values for devices that don't have the matching unique ID */ + if (!IsListEmpty(&(DeviceExtension->DeviceListHead))) + { + for (NextEntry = DeviceExtension->DeviceListHead.Flink; + NextEntry != &(DeviceExtension->DeviceListHead); + NextEntry = NextEntry->Flink) + { + DeviceInfo = CONTAINING_RECORD(NextEntry, + DEVICE_INFORMATION, + DeviceListEntry); + + if (!DeviceInfo->UniqueId || DeviceInfo->UniqueId->UniqueIdLength != ValueLength) + { + continue; + } + + if (RtlCompareMemory(DeviceInfo->UniqueId->UniqueId, ValueData, ValueLength) == ValueLength) + { + return STATUS_SUCCESS; + } + } + } + + /* Wrong unique ID, scrub it */ + Status = RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + ValueName); + if (!NT_SUCCESS(Status)) + { + *Continue = TRUE; + return STATUS_UNSUCCESSFUL; + } + + *Continue = FALSE; + return Status; +} + +/* + * @implemented + */ +NTSTATUS +MountMgrScrubRegistry(IN PDEVICE_EXTENSION DeviceExtension) +{ + NTSTATUS Status; + BOOLEAN Continue = TRUE; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + + while (Continue) + { + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].QueryRoutine = ScrubRegistryRoutine; + QueryTable[0].EntryContext = &Continue; + Continue = FALSE; + + Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + QueryTable, + DeviceExtension, + NULL); + } + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +MountMgrCreatePoint(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + ULONG MaxLength; + PIO_STACK_LOCATION Stack; + PMOUNTMGR_CREATE_POINT_INPUT Point; + UNICODE_STRING DeviceName, SymbolicName; + + Stack = IoGetCurrentIrpStackLocation(Irp); + + if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CREATE_POINT_INPUT)) + { + return STATUS_INVALID_PARAMETER; + } + + Point = (PMOUNTMGR_CREATE_POINT_INPUT)Irp->AssociatedIrp.SystemBuffer; + + MaxLength = MAX((Point->DeviceNameOffset + Point->DeviceNameLength), + (Point->SymbolicLinkNameLength + Point->SymbolicLinkNameOffset)); + if (MaxLength >= Stack->Parameters.DeviceIoControl.InputBufferLength) + { + return STATUS_INVALID_PARAMETER; + } + + /* Get all the strings and call the worker */ + SymbolicName.Length = Point->SymbolicLinkNameLength; + SymbolicName.MaximumLength = Point->SymbolicLinkNameLength; + DeviceName.Length = Point->DeviceNameLength; + DeviceName.MaximumLength = Point->DeviceNameLength; + SymbolicName.Buffer = (PVOID)((ULONG_PTR)Point + Point->SymbolicLinkNameOffset); + DeviceName.Buffer = (PVOID)((ULONG_PTR)Point + Point->DeviceNameOffset); + + return MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &DeviceName); +} + +/* + * @implemented + */ +NTSTATUS +MountMgrCheckUnprocessedVolumes(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + PLIST_ENTRY NextEntry; + PDEVICE_INFORMATION DeviceInformation; + NTSTATUS ArrivalStatus, Status = STATUS_SUCCESS; + + /* No offline volumes, nothing more to do */ + if (IsListEmpty(&(DeviceExtension->OfflineDeviceListHead))) + { + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + return STATUS_SUCCESS; + } + + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + + /* Reactivate all the offline volumes */ + while (!IsListEmpty(&(DeviceExtension->OfflineDeviceListHead))) + { + NextEntry = RemoveHeadList(&(DeviceExtension->OfflineDeviceListHead)); + DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + + ArrivalStatus = MountMgrMountedDeviceArrival(DeviceExtension, + &(DeviceInformation->SymbolicName), + DeviceInformation->Volume); + /* Then, remove them dead information */ + MountMgrFreeDeadDeviceInfo(DeviceInformation); + + if (NT_SUCCESS(Status)) + { + Status = ArrivalStatus; + } + } + + return Status; +} + +/* + * @implemented + */ +BOOLEAN +IsFtVolume(IN PUNICODE_STRING SymbolicName) +{ + PIRP Irp; + KEVENT Event; + NTSTATUS Status; + PFILE_OBJECT FileObject; + IO_STATUS_BLOCK IoStatusBlock; + PARTITION_INFORMATION PartitionInfo; + PDEVICE_OBJECT DeviceObject, FileDeviceObject; + + /* Get device object */ + Status = IoGetDeviceObjectPointer(SymbolicName, + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return FALSE; + } + + /* Get attached device */ + FileDeviceObject = FileObject->DeviceObject; + DeviceObject = IoGetAttachedDeviceReference(FileDeviceObject); + + /* FT volume can't be removable */ + if (FileDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) + { + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + return FALSE; + } + + ObfDereferenceObject(FileObject); + + /* Get partition information */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO, + DeviceObject, + NULL, + 0, + &PartitionInfo, + sizeof(PartitionInfo), + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + ObfDereferenceObject(DeviceObject); + return FALSE; + } + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + ObfDereferenceObject(DeviceObject); + if (!NT_SUCCESS(Status)) + { + return FALSE; + } + + /* Check if this is a FT volume */ + return IsRecognizedPartition(PartitionInfo.PartitionType); +} + +/* + * @implemented + */ +VOID +ProcessSuggestedDriveLetters(IN PDEVICE_EXTENSION DeviceExtension) +{ + WCHAR NameBuffer[DRIVE_LETTER_LENGTH / sizeof(WCHAR)]; + PLIST_ENTRY NextEntry; + UNICODE_STRING SymbolicName; + PDEVICE_INFORMATION DeviceInformation; + + /* No devices? Nothing to do! */ + if (IsListEmpty(&(DeviceExtension->DeviceListHead))) + { + return; + } + + /* For all the devices */ + for (NextEntry = DeviceExtension->DeviceListHead.Flink; + NextEntry != &(DeviceExtension->DeviceListHead); + NextEntry = NextEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + + /* If no drive letter */ + if (DeviceInformation->SuggestedDriveLetter == (UCHAR)-1) + { + /* Ensure it has no entry yet */ + if (!HasDriveLetter(DeviceInformation) && + !HasNoDriveLetterEntry(DeviceInformation->UniqueId)) + { + /* And create one */ + CreateNoDriveLetterEntry(DeviceInformation->UniqueId); + } + + DeviceInformation->SuggestedDriveLetter = 0; + } + /* Suggested letter & no entry */ + else if (DeviceInformation->SuggestedDriveLetter && + !HasNoDriveLetterEntry(DeviceInformation->UniqueId)) + { + /* Just create a mount point */ + SymbolicName.Buffer = NameBuffer; + RtlCopyMemory(NameBuffer, DosDevices.Buffer, DosDevices.Length); + NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter; + NameBuffer[COLON_POSITION] = L':'; + SymbolicName.Length = + SymbolicName.MaximumLength = DRIVE_LETTER_LENGTH; + + MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &(DeviceInformation->DeviceName)); + } + } +} + +/* + * @implemented + */ +NTSTATUS +MountMgrNextDriveLetterWorker(IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING DeviceName, + OUT PMOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInfo) +{ + NTSTATUS Status; + UCHAR DriveLetter; + PLIST_ENTRY NextEntry; + PMOUNTDEV_UNIQUE_ID UniqueId; + BOOLEAN Removable, GptDriveLetter; + PDEVICE_INFORMATION DeviceInformation; + WCHAR NameBuffer[DRIVE_LETTER_LENGTH]; + PSYMLINK_INFORMATION SymlinkInformation; + UNICODE_STRING TargetDeviceName, SymbolicName; + + /* First, process suggested letters */ + if (!DeviceExtension->ProcessedSuggestions) + { + ProcessSuggestedDriveLetters(DeviceExtension); + DeviceExtension->ProcessedSuggestions = TRUE; + } + + /* Then, get information about the device */ + Status = QueryDeviceInformation(DeviceName, &TargetDeviceName, NULL, &Removable, &GptDriveLetter, NULL, NULL, NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Ensure we have such device */ + NextEntry = DeviceExtension->DeviceListHead.Flink; + while (NextEntry != &(DeviceExtension->DeviceListHead)) + { + DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + + if (RtlCompareUnicodeString(&(DeviceInformation->DeviceName), &TargetDeviceName, TRUE) == 0) + { + break; + } + + NextEntry = NextEntry->Flink; + } + + if (NextEntry == &(DeviceExtension->DeviceListHead)) + { + FreePool(TargetDeviceName.Buffer); + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + /* Now, mark we have assigned a letter (assumption) */ + DeviceInformation->LetterAssigned = + DriveLetterInfo->DriveLetterWasAssigned = TRUE; + + /* Browse all the symlink to see if there's already a drive letter */ + NextEntry = DeviceInformation->SymbolicLinksListHead.Flink; + while (NextEntry != &(DeviceInformation->SymbolicLinksListHead)) + { + SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + /* This is a driver letter & online one, forget about new drive eltter */ + if (IsDriveLetter(&(SymlinkInformation->Name)) && SymlinkInformation->Online) + { + DriveLetterInfo->DriveLetterWasAssigned = FALSE; + DriveLetterInfo->CurrentDriveLetter = SymlinkInformation->Name.Buffer[LETTER_POSITION]; + break; + } + + NextEntry = NextEntry->Flink; + } + + /* If we didn't find a drive letter online + * ensure there's no GPT drive letter nor no drive entry + */ + if (NextEntry == &(DeviceInformation->SymbolicLinksListHead)) + { + if (GptDriveLetter || HasNoDriveLetterEntry(DeviceInformation->UniqueId)) + { + DriveLetterInfo->DriveLetterWasAssigned = FALSE; + DriveLetterInfo->CurrentDriveLetter = 0; + + goto Release; + } + } + + /* No, ensure that the device is not automonted nor removable */ + if (!DeviceExtension->NoAutoMount && !Removable) + { + if (DriveLetterInfo->DriveLetterWasAssigned) + { + DriveLetterInfo->DriveLetterWasAssigned = FALSE; + DriveLetterInfo->CurrentDriveLetter = 0; + + goto Release; + } + } + + if (!DriveLetterInfo->DriveLetterWasAssigned) + { + goto Release; + } + + /* Now everything is fine, start processing */ + if (RtlPrefixUnicodeString(&DeviceFloppy, &TargetDeviceName, TRUE)) + { + DriveLetter = 'A'; + } + else + { + DriveLetter = 'C' + RtlPrefixUnicodeString(&DeviceCdRom, &TargetDeviceName, TRUE); + } + + /* We cannot set NO drive letter */ + ASSERT(DeviceInformation->SuggestedDriveLetter != (UCHAR)-1); + + /* If we don't have suggested letter but it's a FT volume, fail */ + if (!DeviceInformation->SuggestedDriveLetter && IsFtVolume(&(DeviceInformation->DeviceName))) + { + DriveLetterInfo->DriveLetterWasAssigned = FALSE; + DriveLetterInfo->CurrentDriveLetter = 0; + + goto Release; + } + + /* Prepare buffer */ + RtlCopyMemory(NameBuffer, DosDevices.Buffer, DosDevices.Length); + NameBuffer[COLON_POSITION] = L':'; + SymbolicName.Buffer = NameBuffer; + SymbolicName.Length = + SymbolicName.MaximumLength = DRIVE_LETTER_LENGTH; + + /* It's all prepared, create mount point */ + if (DeviceInformation->SuggestedDriveLetter) + { + DriveLetterInfo->CurrentDriveLetter = DeviceInformation->SuggestedDriveLetter; + NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter; + + Status = MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &TargetDeviceName); + if (NT_SUCCESS(Status)) + { + goto Release; + } + } + + /* It failed with this letter... Try another one! */ + for (DriveLetterInfo->CurrentDriveLetter = DriveLetter; + DriveLetterInfo->CurrentDriveLetter <= L'Z'; + DriveLetterInfo->CurrentDriveLetter++) + { + NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter; + + Status = MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &TargetDeviceName); + if (NT_SUCCESS(Status)) + { + break; + } + } + + /* We failed setting a letter */ + if (DriveLetterInfo->CurrentDriveLetter > L'Z') + { + DriveLetterInfo->DriveLetterWasAssigned = FALSE; + DriveLetterInfo->CurrentDriveLetter = 0; + + /* Try at least to add a no drive letter entry */ + Status = QueryDeviceInformation(&TargetDeviceName, NULL, &UniqueId, NULL, NULL, NULL, NULL, NULL); + if (NT_SUCCESS(Status)) + { + CreateNoDriveLetterEntry(UniqueId); + FreePool(UniqueId); + } + } + +Release: + FreePool(TargetDeviceName.Buffer); + + return STATUS_SUCCESS; +} + + +/* + * @implemented + */ +NTSTATUS +MountMgrNextDriveLetter(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + NTSTATUS Status; + PIO_STACK_LOCATION Stack; + UNICODE_STRING DeviceName; + PMOUNTMGR_DRIVE_LETTER_TARGET DriveLetterTarget; + MOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInformation; + + Stack = IoGetNextIrpStackLocation(Irp); + + /* Validate input */ + if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) || + Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION)) + { + return STATUS_INVALID_PARAMETER; + } + + DriveLetterTarget = (PMOUNTMGR_DRIVE_LETTER_TARGET)Irp->AssociatedIrp.SystemBuffer; + if (DriveLetterTarget->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength) + { + return STATUS_INVALID_PARAMETER; + } + + /* Call the worker */ + DeviceName.Buffer = DriveLetterTarget->DeviceName; + DeviceName.Length = + DeviceName.MaximumLength = DriveLetterTarget->DeviceNameLength; + + Status = MountMgrNextDriveLetterWorker(DeviceExtension, &DeviceName, + &DriveLetterInformation); + if (NT_SUCCESS(Status)) + { + *(PMOUNTMGR_DRIVE_LETTER_INFORMATION)Irp->AssociatedIrp.SystemBuffer = + DriveLetterInformation; + Irp->IoStatus.Information = sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION); + } + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +MountMgrQuerySystemVolumeNameQueryRoutine(IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext) +{ + UNICODE_STRING ValueString; + PUNICODE_STRING SystemVolumeName; + + if (ValueType != REG_SZ) + { + return STATUS_SUCCESS; + } + + RtlInitUnicodeString(&ValueString, ValueData); + SystemVolumeName = Context; + + /* Return a string containing system volume name */ + SystemVolumeName->Length = ValueString.Length; + SystemVolumeName->MaximumLength = ValueString.Length + sizeof(WCHAR); + SystemVolumeName->Buffer = AllocatePool(SystemVolumeName->MaximumLength); + if (SystemVolumeName->Buffer) + { + RtlCopyMemory(SystemVolumeName->Buffer, ValueData, ValueString.Length); + SystemVolumeName->Buffer[ValueString.Length / sizeof(WCHAR)] = UNICODE_NULL; + } + + return STATUS_SUCCESS; + +} + +/* + * @implemented + */ +NTSTATUS +MountMgrQuerySystemVolumeName(OUT PUNICODE_STRING SystemVolumeName) +{ + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].QueryRoutine = MountMgrQuerySystemVolumeNameQueryRoutine; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[0].Name = L"SystemPartition"; + + SystemVolumeName->Buffer = NULL; + + RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + L"\\Registry\\Machine\\System\\Setup", + QueryTable, + SystemVolumeName, + NULL); + + if (SystemVolumeName->Buffer) + { + return STATUS_SUCCESS; + } + + return STATUS_UNSUCCESSFUL; +} + +/* + * @implemented + */ +VOID +MountMgrAssignDriveLetters(IN PDEVICE_EXTENSION DeviceExtension) +{ + NTSTATUS Status; + PLIST_ENTRY NextEntry; + UNICODE_STRING SystemVolumeName; + PDEVICE_INFORMATION DeviceInformation; + MOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInformation; + + /* First, get system volume name */ + Status = MountMgrQuerySystemVolumeName(&SystemVolumeName); + + /* If there are no device, it's all done */ + if (IsListEmpty(&(DeviceExtension->DeviceListHead))) + { + if (NT_SUCCESS(Status)) + { + FreePool(SystemVolumeName.Buffer); + } + + return; + } + + /* Now, for all the devices... */ + for (NextEntry = DeviceExtension->DeviceListHead.Flink; + NextEntry != &(DeviceExtension->DeviceListHead); + NextEntry = NextEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + + /* If the device doesn't have a letter assigned, do it! */ + if (!DeviceInformation->LetterAssigned) + { + MountMgrNextDriveLetterWorker(DeviceExtension, + &(DeviceInformation->DeviceName), + &DriveLetterInformation); + } + + /* If it was the system volume */ + if (NT_SUCCESS(Status) && RtlEqualUnicodeString(&SystemVolumeName, &(DeviceInformation->DeviceName), TRUE)) + { + /* Keep track of it */ + DeviceExtension->DriveLetterData = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength + + sizeof(MOUNTDEV_UNIQUE_ID)); + if (DeviceExtension->DriveLetterData) + { + RtlCopyMemory(DeviceExtension->DriveLetterData, + DeviceInformation->UniqueId, + DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); + } + + /* If it was not automount, ensure it gets mounted */ + if (!DeviceExtension->NoAutoMount) + { + DeviceExtension->NoAutoMount = TRUE; + + MountMgrNextDriveLetterWorker(DeviceExtension, + &(DeviceInformation->DeviceName), + &DriveLetterInformation); + + DeviceExtension->NoAutoMount = FALSE; + } + } + } + + if (NT_SUCCESS(Status)) + { + FreePool(SystemVolumeName.Buffer); + } +} + +NTSTATUS +MountMgrQueryDosVolumePath(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +MountMgrQueryDosVolumePaths(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + return STATUS_NOT_IMPLEMENTED; +} + +/* + * @implemented + */ +NTSTATUS +MountMgrKeepLinksWhenOffline(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + NTSTATUS Status; + PIO_STACK_LOCATION Stack; + UNICODE_STRING SymbolicName; + PMOUNTMGR_TARGET_NAME Target; + PDEVICE_INFORMATION DeviceInformation; + + Stack = IoGetNextIrpStackLocation(Irp); + + /* Validate input */ + if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME)) + { + return STATUS_INVALID_PARAMETER; + } + + Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer; + if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength) + { + return STATUS_INVALID_PARAMETER; + } + + SymbolicName.Length = + SymbolicName.MaximumLength = Target->DeviceNameLength; + SymbolicName.Buffer = Target->DeviceName; + + /* Find the associated device */ + Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Mark we want to keep links */ + DeviceInformation->KeepLinks = TRUE; + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +NTSTATUS +MountMgrVolumeArrivalNotification(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + NTSTATUS Status; + BOOLEAN OldState; + PIO_STACK_LOCATION Stack; + UNICODE_STRING SymbolicName; + PMOUNTMGR_TARGET_NAME Target; + + Stack = IoGetNextIrpStackLocation(Irp); + + /* Validate input */ + if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME)) + { + return STATUS_INVALID_PARAMETER; + } + + Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer; + if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength) + { + return STATUS_INVALID_PARAMETER; + } + + SymbolicName.Length = + SymbolicName.MaximumLength = Target->DeviceNameLength; + SymbolicName.Buffer = Target->DeviceName; + + /* Disable hard errors */ + OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread()); + PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE); + + /* Call real worker */ + Status = MountMgrMountedDeviceArrival(DeviceExtension, &SymbolicName, TRUE); + + PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState); + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +MountMgrQueryPoints(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + NTSTATUS Status; + PIO_STACK_LOCATION Stack; + PMOUNTDEV_UNIQUE_ID UniqueId; + PMOUNTMGR_MOUNT_POINT MountPoint; + UNICODE_STRING SymbolicName, DeviceName; + + Stack = IoGetNextIrpStackLocation(Irp); + + /* Validate input... */ + if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT)) + { + return STATUS_INVALID_PARAMETER; + } + + MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer; + if (!MountPoint->SymbolicLinkNameLength) + { + MountPoint->SymbolicLinkNameOffset = 0; + } + + if (!MountPoint->UniqueIdLength) + { + MountPoint->UniqueIdOffset = 0; + } + + if (!MountPoint->DeviceNameLength) + { + MountPoint->DeviceNameOffset = 0; + } + + /* Addresses can't be odd */ + if ((MountPoint->SymbolicLinkNameOffset & 1) || + (MountPoint->SymbolicLinkNameLength & 1)) + { + return STATUS_INVALID_PARAMETER; + } + + if ((MountPoint->UniqueIdOffset & 1) || + (MountPoint->UniqueIdLength & 1)) + { + return STATUS_INVALID_PARAMETER; + } + + if ((MountPoint->DeviceNameOffset & 1) || + (MountPoint->DeviceNameLength & 1)) + { + return STATUS_INVALID_PARAMETER; + } + + /* We can't go beyond */ + if (MountPoint->SymbolicLinkNameLength + MountPoint->UniqueIdLength + + MountPoint->DeviceNameLength < Stack->Parameters.DeviceIoControl.InputBufferLength) + { + return STATUS_INVALID_PARAMETER; + } + + if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_MOUNT_POINTS)) + { + return STATUS_INVALID_PARAMETER; + } + + /* If caller provided a Symlink, use it */ + if (MountPoint->SymbolicLinkNameLength != 0) + { + if (MountPoint->SymbolicLinkNameLength > MAXSHORT) + { + return STATUS_INVALID_PARAMETER; + } + + SymbolicName.Length = MountPoint->SymbolicLinkNameLength; + SymbolicName.MaximumLength = MountPoint->SymbolicLinkNameLength + sizeof(WCHAR); + SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength); + if (!SymbolicName.Buffer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(SymbolicName.Buffer, + (PWSTR)((ULONG_PTR)MountPoint + MountPoint->SymbolicLinkNameOffset), + SymbolicName.Length); + SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL; + + /* Query links using it */ + Status = QueryPointsFromSymbolicLinkName(DeviceExtension, &SymbolicName, Irp); + FreePool(SymbolicName.Buffer); + } + /* If user provided an unique ID */ + else if (MountPoint->UniqueIdLength != 0) + { + UniqueId = AllocatePool(MountPoint->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); + if (!UniqueId) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + UniqueId->UniqueIdLength = MountPoint->UniqueIdLength; + RtlCopyMemory(UniqueId->UniqueId, + (PVOID)((ULONG_PTR)MountPoint + MountPoint->UniqueIdOffset), + MountPoint->UniqueIdLength); + + /* Query links using it */ + Status = QueryPointsFromMemory(DeviceExtension, Irp, UniqueId, NULL); + FreePool(UniqueId); + } + /* If caller provided a device name */ + else if (MountPoint->DeviceNameLength != 0) + { + if (MountPoint->DeviceNameLength > MAXSHORT) + { + return STATUS_INVALID_PARAMETER; + } + + DeviceName.Length = MountPoint->DeviceNameLength; + DeviceName.MaximumLength = MountPoint->DeviceNameLength + sizeof(WCHAR); + DeviceName.Buffer = AllocatePool(DeviceName.MaximumLength); + if (!DeviceName.Buffer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(DeviceName.Buffer, + (PWSTR)((ULONG_PTR)MountPoint + MountPoint->DeviceNameOffset), + DeviceName.Length); + DeviceName.Buffer[DeviceName.Length / sizeof(WCHAR)] = UNICODE_NULL; + + /* Query links using it */ + Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, &DeviceName); + FreePool(DeviceName.Buffer); + } + else + { + /* Otherwise, query all links */ + Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, NULL); + } + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +MountMgrDeletePoints(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + ULONG Link; + NTSTATUS Status; + BOOLEAN CreateNoDrive; + PIO_STACK_LOCATION Stack; + PMOUNTDEV_UNIQUE_ID UniqueId; + PMOUNTMGR_MOUNT_POINT MountPoint; + PMOUNTMGR_MOUNT_POINTS MountPoints; + UNICODE_STRING SymbolicName, DeviceName; + + Stack = IoGetNextIrpStackLocation(Irp); + + /* Validate input */ + if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT)) + { + return STATUS_INVALID_PARAMETER; + } + + /* Query points */ + MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer; + CreateNoDrive = (MountPoint->SymbolicLinkNameOffset && MountPoint->SymbolicLinkNameLength); + + Status = MountMgrQueryPoints(DeviceExtension, Irp); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* For all the points matching the request */ + MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer; + for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++) + { + SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength; + SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR); + SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength); + if (!SymbolicName.Buffer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(SymbolicName.Buffer, + (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset), + SymbolicName.Length); + SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL; + + /* Create a no drive entry for the drive letters */ + if (CreateNoDrive && IsDriveLetter(&SymbolicName)) + { + UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); + if (UniqueId) + { + UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength; + RtlCopyMemory(UniqueId->UniqueId, + (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset), + MountPoints->MountPoints[Link].UniqueIdLength); + + CreateNoDriveLetterEntry(UniqueId); + FreePool(UniqueId); + } + } + + /* If there are no link any more, and no need to create a no drive entry */ + if (Link == 0 && !CreateNoDrive) + { + /* Then, delete everything */ + UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength); + if (UniqueId) + { + RtlCopyMemory(UniqueId, + (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset), + MountPoints->MountPoints[Link].UniqueIdLength); + + DeleteNoDriveLetterEntry(UniqueId); + FreePool(UniqueId); + } + } + + /* Delete all the information about the mount point */ + GlobalDeleteSymbolicLink(&SymbolicName); + DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, FALSE); + RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer); + FreePool(SymbolicName.Buffer); + + /* Notify the change */ + DeviceName.Length = DeviceName.MaximumLength = + MountPoints->MountPoints[Link].DeviceNameLength; + DeviceName.Buffer = (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].DeviceNameOffset); + MountMgrNotifyNameChange(DeviceExtension, &DeviceName, TRUE); + } + + MountMgrNotify(DeviceExtension); + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +MountMgrDeletePointsDbOnly(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp) +{ + ULONG Link; + NTSTATUS Status; + UNICODE_STRING SymbolicName; + PMOUNTDEV_UNIQUE_ID UniqueId; + PMOUNTMGR_MOUNT_POINTS MountPoints; + + /* Query points */ + Status = MountMgrQueryPoints(DeviceExtension, Irp); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer; + if (MountPoints->NumberOfMountPoints == 0) + { + return Status; + } + + /* For all the mount points */ + for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++) + { + SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength; + SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR); + SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength); + if (!SymbolicName.Buffer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(SymbolicName.Buffer, + (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset), + SymbolicName.Length); + SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL; + + /* If the only mount point is a drive letter, then create a no letter drive entry */ + if (MountPoints->NumberOfMountPoints == 1 && IsDriveLetter(&SymbolicName)) + { + UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); + if (UniqueId) + { + UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength; + RtlCopyMemory(UniqueId->UniqueId, + (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset), + MountPoints->MountPoints[Link].UniqueIdLength); + + CreateNoDriveLetterEntry(UniqueId); + FreePool(UniqueId); + } + } + + /* Simply delete mount point from DB */ + DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, TRUE); + RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer); + FreePool(SymbolicName.Buffer); + } + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +MountMgrVolumeMountPointChanged(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp, + IN NTSTATUS LockStatus, + OUT PUNICODE_STRING SourceDeviceName, + OUT PUNICODE_STRING SourceSymbolicName, + OUT PUNICODE_STRING TargetVolumeName) +{ + HANDLE Handle; + NTSTATUS Status; + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + ULONG Length, SavedLength; + BOOLEAN FOReferenced = FALSE; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + PDEVICE_INFORMATION DeviceInformation; + OBJECT_NAME_INFORMATION ObjectNameInfo; + FILE_FS_DEVICE_INFORMATION FsDeviceInfo; + PFILE_NAME_INFORMATION FileNameInfo = NULL; + PMOUNTMGR_VOLUME_MOUNT_POINT VolumeMountPoint; + POBJECT_NAME_INFORMATION ObjectNameInfoPtr = NULL; + UNICODE_STRING SourceVolumeName, TargetDeviceName; + + Stack = IoGetNextIrpStackLocation(Irp); + + /* Validate input */ + if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_VOLUME_MOUNT_POINT)) + { + return STATUS_INVALID_PARAMETER; + } + + VolumeMountPoint = (PMOUNTMGR_VOLUME_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer; + + if (VolumeMountPoint->SourceVolumeNameLength + VolumeMountPoint->TargetVolumeNameLength < + Stack->Parameters.DeviceIoControl.InputBufferLength) + { + return STATUS_INVALID_PARAMETER; + } + + /* Get source volume name */ + SourceVolumeName.Length = + SourceVolumeName.MaximumLength = VolumeMountPoint->SourceVolumeNameLength; + SourceVolumeName.Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->SourceVolumeNameOffset); + + InitializeObjectAttributes(&ObjectAttributes, + &SourceVolumeName, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + /* Open it */ + Status = ZwOpenFile(&Handle, + SYNCHRONIZE | FILE_READ_ATTRIBUTES, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + TargetDeviceName.Buffer = NULL; + + /* Query its attributes */ + Status = ZwQueryVolumeInformationFile(Handle, + &IoStatusBlock, + &FsDeviceInfo, + sizeof(FsDeviceInfo), + FileFsDeviceInformation); + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + + if (FsDeviceInfo.DeviceType != FILE_DEVICE_DISK && FsDeviceInfo.DeviceType != FILE_DEVICE_VIRTUAL_DISK) + { + goto Cleanup; + } + + if (FsDeviceInfo.Characteristics != (FILE_REMOTE_DEVICE | FILE_REMOVABLE_MEDIA)) + { + goto Cleanup; + } + + /* Reference it */ + Status = ObReferenceObjectByHandle(Handle, 0, IoFileObjectType, KernelMode, (PVOID *)&FileObject, NULL); + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + FOReferenced = TRUE; + + /* Get file name */ + FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION)); + if (!FileNameInfo) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo, + sizeof(FILE_NAME_INFORMATION), + FileNameInformation); + if (Status == STATUS_BUFFER_OVERFLOW) + { + /* Now we have real length, use it */ + Length = FileNameInfo->FileNameLength; + FreePool(FileNameInfo); + + FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + Length); + if (!FileNameInfo) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + /* Really query file name */ + Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo, + sizeof(FILE_NAME_INFORMATION) + Length, + FileNameInformation); + } + + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + + /* Get symbolic name */ + ObjectNameInfoPtr = &ObjectNameInfo; + SavedLength = sizeof(OBJECT_NAME_INFORMATION); + Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, sizeof(OBJECT_NAME_INFORMATION), &Length); + if (Status == STATUS_INFO_LENGTH_MISMATCH) + { + /* Once again, with proper size, it works better */ + ObjectNameInfoPtr = AllocatePool(Length); + if (!ObjectNameInfoPtr) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + SavedLength = Length; + Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, SavedLength, &Length); + } + + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + + /* Now, query the device name */ + Status = QueryDeviceInformation(&ObjectNameInfoPtr->Name, SourceDeviceName, + NULL, NULL, NULL, NULL, NULL, NULL); + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + + /* For target volume name, use input */ + TargetVolumeName->Length = + TargetVolumeName->MaximumLength = VolumeMountPoint->TargetVolumeNameLength; + TargetVolumeName->Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->TargetVolumeNameOffset); + + /* Query its device name */ + Status = QueryDeviceInformation(TargetVolumeName, &TargetDeviceName, + NULL, NULL, NULL, NULL, NULL, NULL); + if (!NT_SUCCESS(Status)) + { + goto Cleanup; + } + + /* Return symbolic name */ + SourceSymbolicName->Length = + SourceSymbolicName->MaximumLength = FileNameInfo->FileNameLength; + SourceSymbolicName->Buffer = (PWSTR)FileNameInfo; + /* memmove allows memory overlap */ + RtlMoveMemory(SourceSymbolicName->Buffer, FileNameInfo->FileName, SourceSymbolicName->Length); + FileNameInfo = NULL; + + /* Notify the change */ + MountMgrNotify(DeviceExtension); + MountMgrNotifyNameChange(DeviceExtension, &TargetDeviceName, TRUE); + + /* If we are locked, sync databases if possible */ + if (NT_SUCCESS(LockStatus)) + { + Status = FindDeviceInfo(DeviceExtension, SourceDeviceName, FALSE, &DeviceInformation); + if (NT_SUCCESS(Status)) + { + ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation); + } + else + { + Status = STATUS_PENDING; + } + } + +Cleanup: + if (TargetDeviceName.Buffer) + { + FreePool(TargetDeviceName.Buffer); + } + + if (ObjectNameInfoPtr && ObjectNameInfoPtr != &ObjectNameInfo) + { + FreePool(ObjectNameInfoPtr); + } + + if (FileNameInfo) + { + FreePool(FileNameInfo); + } + + if (FOReferenced) + { + ObfDereferenceObject(FileObject); + } + + return Status; +} + +NTSTATUS +MountMgrVolumeMountPointCreated(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp, + IN NTSTATUS LockStatus) +{ + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +MountMgrVolumeMountPointDeleted(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp, + IN NTSTATUS LockStatus) +{ + return STATUS_NOT_IMPLEMENTED; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +MountMgrDeviceControl(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION Stack; + NTSTATUS Status, LockStatus; + PDEVICE_EXTENSION DeviceExtension; + + Stack = IoGetNextIrpStackLocation(Irp); + DeviceExtension = DeviceObject->DeviceExtension; + + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); + + switch (Stack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_MOUNTMGR_CREATE_POINT: + Status = MountMgrCreatePoint(DeviceExtension, Irp); + break; + + case IOCTL_MOUNTMGR_DELETE_POINTS: + Status = MountMgrDeletePoints(DeviceExtension, Irp); + + case IOCTL_MOUNTMGR_QUERY_POINTS: + Status = MountMgrQueryPoints(DeviceExtension, Irp); + break; + + case IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY: + Status = MountMgrDeletePointsDbOnly(DeviceExtension, Irp); + break; + + case IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER: + Status = MountMgrNextDriveLetter(DeviceExtension, Irp); + break; + + case IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS: + DeviceExtension->AutomaticDriveLetter = TRUE; + Status = STATUS_SUCCESS; + + MountMgrAssignDriveLetters(DeviceExtension); + ReconcileAllDatabasesWithMaster(DeviceExtension); + WaitForOnlinesToComplete(DeviceExtension); + break; + + case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED: + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + + LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension); + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); + Status = MountMgrVolumeMountPointCreated(DeviceExtension, Irp, LockStatus); + if (NT_SUCCESS(LockStatus)) + { + ReleaseRemoteDatabaseSemaphore(DeviceExtension); + } + + break; + + case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED: + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + + LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension); + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); + Status = MountMgrVolumeMountPointDeleted(DeviceExtension, Irp, LockStatus); + if (NT_SUCCESS(LockStatus)) + { + ReleaseRemoteDatabaseSemaphore(DeviceExtension); + } + + break; + + case IOCTL_MOUNTMGR_CHANGE_NOTIFY: + Status = MountMgrChangeNotify(DeviceExtension, Irp); + break; + + case IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE: + Status = MountMgrKeepLinksWhenOffline(DeviceExtension, Irp); + break; + + case IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES: + Status = MountMgrCheckUnprocessedVolumes(DeviceExtension, Irp); + goto Complete; + + case IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION: + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + Status = MountMgrVolumeArrivalNotification(DeviceExtension, Irp); + goto Complete; + + case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH: + Status = MountMgrQueryDosVolumePath(DeviceExtension, Irp); + break; + + case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS: + Status = MountMgrQueryDosVolumePaths(DeviceExtension, Irp); + break; + + case IOCTL_MOUNTMGR_SCRUB_REGISTRY: + Status = MountMgrScrubRegistry(DeviceExtension); + break; + + case IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT: + Status = MountMgrQueryAutoMount(DeviceExtension, Irp); + break; + + case IOCTL_MOUNTMGR_SET_AUTO_MOUNT: + Status = MountMgrSetAutoMount(DeviceExtension, Irp); + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + + if (Status != STATUS_PENDING) + { + goto Complete; + } + + return Status; + +Complete: + Irp->IoStatus.Status = Status; + IofCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} diff --git a/reactos/drivers/filters/mountmgr/mntmgr.h b/reactos/drivers/filters/mountmgr/mntmgr.h new file mode 100644 index 00000000000..06c61192be2 --- /dev/null +++ b/reactos/drivers/filters/mountmgr/mntmgr.h @@ -0,0 +1,453 @@ +#ifndef _MNTMGR_H_ +#define _MNTMGR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Enter FIXME */ +#ifdef IsEqualGUID +#undef IsEqualGUID +#endif + +#define IsEqualGUID(rguid1, rguid2) (!RtlCompareMemory(rguid1, rguid2, sizeof(GUID))) + +#define FILE_READ_PROPERTIES 0x00000008 +#define FILE_WRITE_PROPERTIES 0x00000010 + +#define GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER 0x80000000 +/* Leave FIXME */ + +typedef struct _DEVICE_EXTENSION +{ + PDEVICE_OBJECT DeviceObject; // 0x0 + PDRIVER_OBJECT DriverObject; // 0x4 + LIST_ENTRY DeviceListHead; // 0x8 + LIST_ENTRY OfflineDeviceListHead; // 0x10 + PVOID NotificationEntry; // 0x18 + KSEMAPHORE DeviceLock; // 0x1C + KSEMAPHORE RemoteDatabaseLock; // 0x30 + ULONG AutomaticDriveLetter; // 0x44 + LIST_ENTRY IrpListHead; // 0x48 + ULONG EpicNumber; // 0x50 + LIST_ENTRY SavedLinksListHead; // 0x54 + BOOLEAN ProcessedSuggestions; // 0x5C + BOOLEAN NoAutoMount; // 0x5D + LIST_ENTRY WorkerQueueListHead; // 0x60 + KSEMAPHORE WorkerSemaphore; // 0x68 + LONG WorkerReferences; // 0x7C + KSPIN_LOCK WorkerLock; // 0x80 + LIST_ENTRY UniqueIdWorkerItemListHead; // 0x84 + PMOUNTDEV_UNIQUE_ID DriveLetterData; // 0x8C + UNICODE_STRING RegistryPath; // 0x90 + LONG WorkerThreadStatus; // 0x98 + LIST_ENTRY OnlineNotificationListHead; // 0x9C + ULONG OnlineNotificationWorkerActive; // 0xA4 + ULONG OnlineNotificationCount; // 0xA8 + KEVENT OnlineNotificationEvent; // 0xAC +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; // 0xBC + +typedef struct _DEVICE_INFORMATION +{ + LIST_ENTRY DeviceListEntry; // 0x00 + LIST_ENTRY SymbolicLinksListHead; // 0x08 + LIST_ENTRY ReplicatedUniqueIdsListHead; // 0x10 + LIST_ENTRY AssociatedDevicesHead; // 0x18 + UNICODE_STRING SymbolicName; // 0x20 + PMOUNTDEV_UNIQUE_ID UniqueId; // 0x28 + UNICODE_STRING DeviceName; // 0x2C + BOOLEAN KeepLinks; // 0x34 + UCHAR SuggestedDriveLetter; // 0x35 + BOOLEAN Volume; // 0x36 + BOOLEAN Removable; // 0x37 + BOOLEAN LetterAssigned; // 0x38 + BOOLEAN NeedsReconcile; // 0x39 + BOOLEAN NoDatabase; // 0x3A + BOOLEAN SkipNotifications; // 0x3B + ULONG Migrated; // 0x3C + LONG MountState; // 0x40 + PVOID TargetDeviceNotificationEntry; // 0x44 + PDEVICE_EXTENSION DeviceExtension; // 0x48 +} DEVICE_INFORMATION, *PDEVICE_INFORMATION; // 0x4C + +typedef struct _SYMLINK_INFORMATION +{ + LIST_ENTRY SymbolicLinksListEntry; // 0x00 + UNICODE_STRING Name; // 0x08 + BOOLEAN Online; // 0x10 +} SYMLINK_INFORMATION, *PSYMLINK_INFORMATION; // 0x14 + +typedef struct _SAVED_LINK_INFORMATION +{ + LIST_ENTRY SavedLinksListEntry; // 0x0 + LIST_ENTRY SymbolicLinksListHead; // 0x8 + PMOUNTDEV_UNIQUE_ID UniqueId; // 0x10 +} SAVED_LINK_INFORMATION, *PSAVED_LINK_INFORMATION; // 0x14 + +typedef struct _UNIQUE_ID_REPLICATE +{ + LIST_ENTRY ReplicatedUniqueIdsListEntry; // 0x0 + PMOUNTDEV_UNIQUE_ID UniqueId; // 0x8 +} UNIQUE_ID_REPLICATE, *PUNIQUE_ID_REPLICATE; // 0xC + +typedef struct _DATABASE_ENTRY +{ + ULONG EntrySize; // 0x00 + ULONG DatabaseOffset; // 0x04 + USHORT SymbolicNameOffset; // 0x08 + USHORT SymbolicNameLength; // 0x0A + USHORT UniqueIdOffset; // 0x0C + USHORT UniqueIdLength; // 0x0E +} DATABASE_ENTRY, *PDATABASE_ENTRY; // 0x10 + +typedef struct _ASSOCIATED_DEVICE_ENTRY +{ + LIST_ENTRY AssociatedDevicesEntry; // 0x00 + PDEVICE_INFORMATION DeviceInformation; // 0x08 + UNICODE_STRING String; // 0x0C +} ASSOCIATED_DEVICE_ENTRY, *PASSOCIATED_DEVICE_ENTRY; // 0x14 + +typedef struct _ONLINE_NOTIFICATION_WORK_ITEM +{ + WORK_QUEUE_ITEM; // 0x00 + PDEVICE_EXTENSION DeviceExtension; // 0x10 + UNICODE_STRING SymbolicName; // 0x14 +} ONLINE_NOTIFICATION_WORK_ITEM, *PONLINE_NOTIFICATION_WORK_ITEM; // 0x1C + +typedef struct _RECONCILE_WORK_ITEM +{ + LIST_ENTRY WorkerQueueListEntry; // 0x00 + PIO_WORKITEM WorkItem; // 0x08 + PWORKER_THREAD_ROUTINE WorkerRoutine; // 0x0C + PVOID Context; // 0x10 + PDEVICE_EXTENSION DeviceExtension; // 0x14 + PDEVICE_INFORMATION DeviceInformation; // 0x18 +} RECONCILE_WORK_ITEM, *PRECONCILE_WORK_ITEM; // 0x1C + +typedef struct _MIGRATE_WORK_ITEM +{ + PIO_WORKITEM WorkItem; // 0x0 + PDEVICE_INFORMATION DeviceInformation; // 0x4 + PKEVENT Event; // 0x8 + NTSTATUS Status; // 0x0C + HANDLE Database; // 0x10 +} MIGRATE_WORK_ITEM, *PMIGRATE_WORK_ITEM; // 0x14 + +typedef struct _UNIQUE_ID_WORK_ITEM +{ + LIST_ENTRY UniqueIdWorkerItemListEntry; // 0x0 + PIO_WORKITEM WorkItem; // 0x8 + PDEVICE_EXTENSION DeviceExtension; // 0xC + PIRP Irp; // 0x10 + PVOID IrpBuffer; // 0x14 + PKEVENT Event; // 0x1C + UNICODE_STRING DeviceName; // 0x20 + ULONG IrpBufferLength; // 0x28 + ULONG StackSize; // 0x2C +} UNIQUE_ID_WORK_ITEM, *PUNIQUE_ID_WORK_ITEM; // 0x30 + +PDEVICE_OBJECT gdeviceObject; + +/* Memory allocation helpers */ +#define AllocatePool(Size) ExAllocatePoolWithTag(PagedPool, Size, 'AtnM') +#define FreePool(P) ExFreePoolWithTag(P, 'AtnM') + +/* Misc macros */ +#define MAX(a, b) ((a > b) ? a : b) + +#define LETTER_POSITION 0xC +#define COLON_POSITION 0xD +#define DRIVE_LETTER_LENGTH 0x1C + +/* mountmgr.c */ + +extern UNICODE_STRING DosDevicesMount; +extern UNICODE_STRING ReparseIndex; +extern UNICODE_STRING DeviceFloppy; +extern UNICODE_STRING DeviceMount; +extern UNICODE_STRING DeviceCdRom; +extern UNICODE_STRING SafeVolumes; +extern UNICODE_STRING DosDevices; +extern UNICODE_STRING DosGlobal; +extern UNICODE_STRING Global; +extern UNICODE_STRING Volume; +extern KEVENT UnloadEvent; +extern LONG Unloading; + +DRIVER_INITIALIZE DriverEntry; + +VOID +NTAPI +MountMgrCancel( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp +); + +NTSTATUS +MountMgrMountedDeviceArrival( + IN PDEVICE_EXTENSION Extension, + IN PUNICODE_STRING SymbolicName, + IN BOOLEAN FromVolume +); + +VOID +MountMgrMountedDeviceRemoval( + IN PDEVICE_EXTENSION Extension, + IN PUNICODE_STRING DeviceName +); + +NTSTATUS +FindDeviceInfo( + IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicName, + IN BOOLEAN DeviceNameGiven, + OUT PDEVICE_INFORMATION * DeviceInformation +); + +VOID +MountMgrFreeDeadDeviceInfo( + IN PDEVICE_INFORMATION DeviceInformation +); + +NTSTATUS +QueryDeviceInformation( + IN PUNICODE_STRING SymbolicName, + OUT PUNICODE_STRING DeviceName OPTIONAL, + OUT PMOUNTDEV_UNIQUE_ID * UniqueId OPTIONAL, + OUT PBOOLEAN Removable OPTIONAL, + OUT PBOOLEAN GptDriveLetter OPTIONAL, + OUT PBOOLEAN HasGuid OPTIONAL, + IN OUT LPGUID StableGuid OPTIONAL, + OUT PBOOLEAN Valid OPTIONAL +); + +BOOLEAN +HasDriveLetter( + IN PDEVICE_INFORMATION DeviceInformation +); + +/* database.c */ + +extern PWSTR DatabasePath; +extern PWSTR OfflinePath; + +VOID +ReconcileThisDatabaseWithMaster( + IN PDEVICE_EXTENSION DeviceExtension, + IN PDEVICE_INFORMATION DeviceInformation +); + +NTSTATUS +WaitForRemoteDatabaseSemaphore( + IN PDEVICE_EXTENSION DeviceExtension +); + +VOID +ReleaseRemoteDatabaseSemaphore( + IN PDEVICE_EXTENSION DeviceExtension +); + +VOID +ChangeRemoteDatabaseUniqueId( + IN PDEVICE_INFORMATION DeviceInformation, + IN PMOUNTDEV_UNIQUE_ID OldUniqueId, + IN PMOUNTDEV_UNIQUE_ID NewUniqueId +); + +VOID +ReconcileAllDatabasesWithMaster( + IN PDEVICE_EXTENSION DeviceExtension +); + +VOID +DeleteFromLocalDatabase( + IN PUNICODE_STRING SymbolicLink, + IN PMOUNTDEV_UNIQUE_ID UniqueId +); + +VOID +DeleteRegistryDriveLetter( + IN PMOUNTDEV_UNIQUE_ID UniqueId +); + +VOID +DeleteNoDriveLetterEntry( + IN PMOUNTDEV_UNIQUE_ID UniqueId +); + +NTSTATUS +QueryVolumeName( + IN HANDLE RootDirectory, + IN PFILE_REPARSE_POINT_INFORMATION ReparsePointInformation, + IN PUNICODE_STRING FileName OPTIONAL, + OUT PUNICODE_STRING SymbolicName, + OUT PUNICODE_STRING VolumeName +); + +/* device.c */ + +DRIVER_DISPATCH MountMgrDeviceControl; + +/* notify.c */ +VOID +IssueUniqueIdChangeNotifyWorker( + IN PUNIQUE_ID_WORK_ITEM WorkItem, + IN PMOUNTDEV_UNIQUE_ID UniqueId +); + +VOID +WaitForOnlinesToComplete( + IN PDEVICE_EXTENSION DeviceExtension +); + +VOID +RegisterForTargetDeviceNotification( + IN PDEVICE_EXTENSION DeviceExtension, + IN PDEVICE_INFORMATION DeviceInformation +); + +VOID +SendOnlineNotification( + IN PUNICODE_STRING SymbolicName +); + +VOID +IssueUniqueIdChangeNotify( + IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING DeviceName, + IN PMOUNTDEV_UNIQUE_ID UniqueId +); + +VOID +PostOnlineNotification( + IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicName +); + +VOID +MountMgrNotify( + IN PDEVICE_EXTENSION DeviceExtension +); + +VOID +MountMgrNotifyNameChange( + IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING DeviceName, + IN BOOLEAN ValidateVolume +); + +/* uniqueid.c */ +VOID +MountMgrUniqueIdChangeRoutine( + IN PDEVICE_EXTENSION DeviceExtension, + IN PMOUNTDEV_UNIQUE_ID OldUniqueId, + IN PMOUNTDEV_UNIQUE_ID NewUniqueId +); + +VOID +CreateNoDriveLetterEntry( + IN PMOUNTDEV_UNIQUE_ID UniqueId +); + +BOOLEAN +HasNoDriveLetterEntry( + IN PMOUNTDEV_UNIQUE_ID UniqueId +); + +/* point.c */ +NTSTATUS +MountMgrCreatePointWorker( + IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicLinkName, + IN PUNICODE_STRING DeviceName +); + +NTSTATUS +QueryPointsFromSymbolicLinkName( + IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicName, + IN PIRP Irp +); + +NTSTATUS +QueryPointsFromMemory( + IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp, + IN PMOUNTDEV_UNIQUE_ID UniqueId OPTIONAL, + IN PUNICODE_STRING SymbolicName OPTIONAL +); + +/* symlink.c */ +NTSTATUS +GlobalCreateSymbolicLink( + IN PUNICODE_STRING DosName, + IN PUNICODE_STRING DeviceName +); + +NTSTATUS +GlobalDeleteSymbolicLink( + IN PUNICODE_STRING DosName +); + +NTSTATUS +QuerySuggestedLinkName( + IN PUNICODE_STRING SymbolicName, + OUT PUNICODE_STRING SuggestedLinkName, + OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks +); + +NTSTATUS +QuerySymbolicLinkNamesFromStorage( + IN PDEVICE_EXTENSION DeviceExtension, + IN PDEVICE_INFORMATION DeviceInformation, + IN PUNICODE_STRING SuggestedLinkName, + IN BOOLEAN UseOnlyIfThereAreNoOtherLinks, + OUT PUNICODE_STRING * SymLinks, + OUT PULONG SymLinkCount, + IN BOOLEAN HasGuid, + IN LPGUID Guid +); + +PSAVED_LINK_INFORMATION +RemoveSavedLinks( + IN PDEVICE_EXTENSION DeviceExtension, + IN PMOUNTDEV_UNIQUE_ID UniqueId +); + +BOOLEAN +RedirectSavedLink( + IN PSAVED_LINK_INFORMATION SavedLinkInformation, + IN PUNICODE_STRING DosName, + IN PUNICODE_STRING NewLink +); + +VOID +SendLinkCreated( + IN PUNICODE_STRING SymbolicName +); + +NTSTATUS +CreateNewVolumeName( + OUT PUNICODE_STRING VolumeName, + IN PGUID VolumeGuid OPTIONAL +); + +BOOLEAN +IsDriveLetter( + PUNICODE_STRING SymbolicName +); + +VOID +DeleteSymbolicLinkNameFromMemory( + IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicLink, + IN BOOLEAN MarkOffline +); + +#endif /* _MNTMGR_H_ */ diff --git a/reactos/drivers/filters/mountmgr/mountmgr.c b/reactos/drivers/filters/mountmgr/mountmgr.c new file mode 100644 index 00000000000..b805a2076ea --- /dev/null +++ b/reactos/drivers/filters/mountmgr/mountmgr.c @@ -0,0 +1,1896 @@ +/* + * ReactOS kernel + * Copyright (C) 2011 ReactOS Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/filesystem/mountmgr/mountmgr.c + * PURPOSE: Mount Manager + * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) + * Alex Ionescu (alex.ionescu@reactos.org) + */ + +/* INCLUDES *****************************************************************/ + +#include "mntmgr.h" + +#define NDEBUG +#include + +/* FIXME */ +GUID MountedDevicesGuid = {0x53F5630D, 0xB6BF, 0x11D0, {0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B}}; + +KEVENT UnloadEvent; +LONG Unloading; + +PWSTR Cunc = L"\\??\\C:"; + +/* + * TODO: + * - MountMgrQueryDosVolumePath + * - MountMgrQueryDosVolumePaths + * - MountMgrQueryVolumePaths + * - MountMgrValidateBackPointer + * - MountMgrVolumeMountPointCreated + * - MountMgrVolumeMountPointDeleted + * - ReconcileThisDatabaseWithMasterWorker + */ + +/* + * @implemented + */ +BOOLEAN +IsOffline(PUNICODE_STRING SymbolicName) +{ + NTSTATUS Status; + ULONG IsOffline, Default; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + + /* Prepare to look in the registry to see if + * given volume is offline + */ + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + QueryTable[0].Name = SymbolicName->Buffer; + QueryTable[0].EntryContext = &IsOffline; + QueryTable[0].DefaultType = REG_DWORD; + QueryTable[0].DefaultLength = sizeof(ULONG); + QueryTable[0].DefaultData = &Default; + + Default = 0; + + /* Query status */ + Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + OfflinePath, + QueryTable, + NULL, + NULL); + if (!NT_SUCCESS(Status)) + { + IsOffline = 0; + } + + return (IsOffline != 0); +} + +/* + * @implemented + */ +BOOLEAN +HasDriveLetter(IN PDEVICE_INFORMATION DeviceInformation) +{ + PLIST_ENTRY NextEntry; + PSYMLINK_INFORMATION SymlinkInfo; + + /* To have a drive letter, a device must have symbolic links */ + if (IsListEmpty(&(DeviceInformation->SymbolicLinksListHead))) + { + return FALSE; + } + + /* Browse all the links untill a drive letter is found */ + NextEntry = &(DeviceInformation->SymbolicLinksListHead); + do + { + SymlinkInfo = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + if (SymlinkInfo->Online) + { + if (IsDriveLetter(&(SymlinkInfo->Name))) + { + return TRUE; + } + } + + NextEntry = NextEntry->Flink; + } while (NextEntry != &(DeviceInformation->SymbolicLinksListHead)); + + return FALSE; +} + +/* + * @implemented + */ +NTSTATUS +CreateNewDriveLetterName(OUT PUNICODE_STRING DriveLetter, + IN PUNICODE_STRING DeviceName, + IN UCHAR Letter, + IN PMOUNTDEV_UNIQUE_ID UniqueId OPTIONAL) +{ + NTSTATUS Status; + + /* Allocate a big enough buffer to contain the symbolic link */ + DriveLetter->MaximumLength = sizeof(DosDevices.Buffer) + 3 * sizeof(WCHAR); + DriveLetter->Buffer = AllocatePool(sizeof(DosDevices.Buffer) + 3 * sizeof(WCHAR)); + if (!DriveLetter->Buffer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Copy prefix */ + RtlCopyUnicodeString(DriveLetter, &DosDevices); + + /* Update string to reflect real contents */ + DriveLetter->Length = sizeof(DosDevices.Buffer) + 2 * sizeof(WCHAR); + DriveLetter->Buffer[(sizeof(DosDevices.Buffer) + 2 * sizeof(WCHAR)) / sizeof (WCHAR)] = UNICODE_NULL; + DriveLetter->Buffer[(sizeof(DosDevices.Buffer) + sizeof(WCHAR)) / sizeof (WCHAR)] = L':'; + + /* If caller wants a no drive entry */ + if (Letter == (UCHAR)-1) + { + /* Then, create a no letter entry */ + CreateNoDriveLetterEntry(UniqueId); + FreePool(DriveLetter->Buffer); + return STATUS_UNSUCCESSFUL; + } + else if (Letter) + { + /* Use the letter given by the caller */ + DriveLetter->Buffer[sizeof(DosDevices.Buffer) / sizeof(WCHAR)] = (WCHAR)Letter; + Status = GlobalCreateSymbolicLink(DriveLetter, DeviceName); + if (NT_SUCCESS(Status)) + { + return Status; + } + } + + /* If caller didn't provide a letter, let's find one for him. + * If device is a floppy, start with letter A + */ + if (RtlPrefixUnicodeString(&DeviceFloppy, DeviceName, TRUE)) + { + Letter = 'A'; + } + else + { + /* Otherwise, if device is a cd rom, then, start with D. + * Finally, if a disk, use C + */ + Letter = RtlPrefixUnicodeString(&DeviceCdRom, DeviceName, TRUE) + 'C'; + } + + /* Try to affect a letter (up to Z, ofc) until it's possible */ + for (; Letter <= 'Z'; Letter++) + { + DriveLetter->Buffer[sizeof(DosDevices.Buffer) / sizeof(WCHAR)] = (WCHAR)Letter; + Status = GlobalCreateSymbolicLink(DriveLetter, DeviceName); + if (NT_SUCCESS(Status)) + { + return Status; + } + } + + /* We failed to allocate a letter */ + FreePool(DriveLetter->Buffer); + return Status; +} + +/* + * @implemented + */ +NTSTATUS +QueryDeviceInformation(IN PUNICODE_STRING SymbolicName, + OUT PUNICODE_STRING DeviceName OPTIONAL, + OUT PMOUNTDEV_UNIQUE_ID * UniqueId OPTIONAL, + OUT PBOOLEAN Removable OPTIONAL, + OUT PBOOLEAN GptDriveLetter OPTIONAL, + OUT PBOOLEAN HasGuid OPTIONAL, + IN OUT LPGUID StableGuid OPTIONAL, + OUT PBOOLEAN Valid OPTIONAL) +{ + PIRP Irp; + USHORT Size; + KEVENT Event; + NTSTATUS Status; + BOOLEAN IsRemovable; + PMOUNTDEV_NAME Name; + PMOUNTDEV_UNIQUE_ID Id; + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + PDEVICE_OBJECT DeviceObject; + IO_STATUS_BLOCK IoStatusBlock; + PARTITION_INFORMATION_EX PartitionInfo; + STORAGE_DEVICE_NUMBER StorageDeviceNumber; + VOLUME_GET_GPT_ATTRIBUTES_INFORMATION GptAttributes; + + /* Get device associated with the symbolic name */ + Status = IoGetDeviceObjectPointer(SymbolicName, + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* The associate FO can't have a file name */ + if (FileObject->FileName.Length) + { + ObfDereferenceObject(FileObject); + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + /* Check if it's removable & return to the user (if asked to) */ + IsRemovable = (FileObject->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA); + if (Removable) + { + *Removable = IsRemovable; + } + + /* Get the attached device */ + DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); + + /* If we've been asked for a GPT drive letter */ + if (GptDriveLetter) + { + /* Consider it has one */ + *GptDriveLetter = TRUE; + + if (!IsRemovable) + { + /* Query the GPT attributes */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_VOLUME_GET_GPT_ATTRIBUTES, + DeviceObject, + NULL, + 0, + &GptAttributes, + sizeof(GptAttributes), + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + /* In case of failure, don't fail, that's no vital */ + if (!NT_SUCCESS(Status)) + { + Status = STATUS_SUCCESS; + } + /* Check if it has a drive letter */ + else if (!(GptAttributes.GptAttributes & + GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER)) + { + *GptDriveLetter = FALSE; + } + } + } + + /* If caller wants to know if there's valid contents */ + if (Valid) + { + /* Suppose it's not OK */ + *Valid = FALSE; + + if (!IsRemovable) + { + /* Query partitions information */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX, + DeviceObject, + NULL, + 0, + &PartitionInfo, + sizeof(PartitionInfo), + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + /* Once again here, failure isn't major */ + if (!NT_SUCCESS(Status)) + { + Status = STATUS_SUCCESS; + } + /* Verify we know something in */ + else if (PartitionInfo.PartitionStyle == PARTITION_STYLE_MBR && + IsRecognizedPartition(PartitionInfo.Mbr.PartitionType)) + { + *Valid = TRUE; + } + + /* It looks correct, ensure it is & query device number */ + if (*Valid) + { + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER, + DeviceObject, + NULL, + 0, + &StorageDeviceNumber, + sizeof(StorageDeviceNumber), + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + if (!NT_SUCCESS(Status)) + { + Status = STATUS_SUCCESS; + } + else + { + *Valid = FALSE; + } + } + } + } + + /* If caller needs device name */ + if (DeviceName) + { + /* Allocate a buffer just to request length */ + Name = AllocatePool(sizeof(MOUNTDEV_NAME)); + if (!Name) + { + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Query device name */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, + DeviceObject, + NULL, + 0, + Name, + sizeof(MOUNTDEV_NAME), + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + FreePool(Name); + ObDereferenceObject(DeviceObject); + ObDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Stack = IoGetNextIrpStackLocation(Irp); + Stack->FileObject = FileObject; + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + /* Now, we've got the correct length */ + if (Status == STATUS_BUFFER_OVERFLOW) + { + Size = Name->NameLength + sizeof(MOUNTDEV_NAME); + + FreePool(Name); + + /* Allocate proper size */ + Name = AllocatePool(Size); + if (!Name) + { + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* And query name (for real that time) */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, + DeviceObject, + NULL, + 0, + Name, + Size, + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + FreePool(Name); + ObDereferenceObject(DeviceObject); + ObDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Stack = IoGetNextIrpStackLocation(Irp); + Stack->FileObject = FileObject; + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + } + + /* Here we can't fail and assume default value */ + if (!NT_SUCCESS(Status)) + { + FreePool(Name); + ObDereferenceObject(DeviceObject); + ObDereferenceObject(FileObject); + return Status; + } + + /* Copy back found name to the caller */ + DeviceName->Length = Name->NameLength; + DeviceName->MaximumLength = Name->NameLength + sizeof(WCHAR); + DeviceName->Buffer = AllocatePool(DeviceName->MaximumLength); + if (!DeviceName->Buffer) + { + FreePool(Name); + ObDereferenceObject(DeviceObject); + ObDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(DeviceName->Buffer, Name->Name, Name->NameLength); + DeviceName->Buffer[Name->NameLength / sizeof(WCHAR)] = UNICODE_NULL; + FreePool(Name); + } + + /* If caller wants device unique ID */ + if (UniqueId) + { + /* Prepare buffer to probe length */ + Id = AllocatePool(sizeof(MOUNTDEV_UNIQUE_ID)); + if (!Id) + { + ObDereferenceObject(DeviceObject); + ObDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Query unique ID length */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_UNIQUE_ID, + DeviceObject, + NULL, + 0, + Id, + sizeof(MOUNTDEV_UNIQUE_ID), + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + FreePool(Id); + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Stack = IoGetNextIrpStackLocation(Irp); + Stack->FileObject = FileObject; + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + /* Retry with appropriate length */ + if (Status == STATUS_BUFFER_OVERFLOW) + { + Size = Id->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID); + + FreePool(Id); + + /* Allocate the correct buffer */ + Id = AllocatePool(Size); + if (!Id) + { + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Query unique ID */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, + DeviceObject, + NULL, + 0, + Id, + Size, + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + FreePool(Id); + ObDereferenceObject(DeviceObject); + ObDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Stack = IoGetNextIrpStackLocation(Irp); + Stack->FileObject = FileObject; + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + } + + /* Hands back unique ID */ + if (NT_SUCCESS(Status)) + { + *UniqueId = Id; + } + else + { + /* In case of failure, also free the rest */ + FreePool(Id); + if (DeviceName->Length) + { + FreePool(DeviceName->Buffer); + } + + ObDereferenceObject(DeviceObject); + ObDereferenceObject(FileObject); + + return Status; + } + } + + /* If user wants to know about GUID */ + if (HasGuid) + { + /* Query device stable GUID */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_STABLE_GUID, + DeviceObject, + NULL, + 0, + StableGuid, + sizeof(GUID), + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Stack = IoGetNextIrpStackLocation(Irp); + Stack->FileObject = FileObject; + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + *HasGuid = NT_SUCCESS(Status); + } + + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + return Status; +} + +/* + * @implemented + */ +NTSTATUS +FindDeviceInfo(IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicName, + IN BOOLEAN DeviceNameGiven, + OUT PDEVICE_INFORMATION * DeviceInformation) +{ + NTSTATUS Status; + PLIST_ENTRY NextEntry; + UNICODE_STRING DeviceName; + PDEVICE_INFORMATION DeviceInfo = NULL; + + /* If a device name was given, use it */ + if (DeviceNameGiven) + { + DeviceName.Length = SymbolicName->Length; + DeviceName.Buffer = SymbolicName->Buffer; + } + else + { + /* Otherwise, query it */ + Status = QueryDeviceInformation(SymbolicName, + &DeviceName, + NULL, NULL, + NULL, NULL, + NULL, NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + } + + /* Look for device information matching devive */ + for (NextEntry = DeviceExtension->DeviceListHead.Flink; + NextEntry != &(DeviceExtension->DeviceListHead); + NextEntry = NextEntry->Flink) + { + DeviceInfo = CONTAINING_RECORD(NextEntry, + DEVICE_INFORMATION, + DeviceListEntry); + + if (RtlEqualUnicodeString(&DeviceName, &(DeviceInfo->DeviceName), TRUE)) + { + break; + } + } + + /* Release our buffer if required */ + if (!DeviceNameGiven) + { + FreePool(DeviceName.Buffer); + } + + /* Return found intormation */ + if (NextEntry == &(DeviceExtension->DeviceListHead)) + { + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + *DeviceInformation = DeviceInfo; + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +VOID +MountMgrFreeDeadDeviceInfo(IN PDEVICE_INFORMATION DeviceInformation) +{ + FreePool(DeviceInformation->SymbolicName.Buffer); + FreePool(DeviceInformation); +} + +/* + * @implemented + */ +VOID +MountMgrFreeMountedDeviceInfo(IN PDEVICE_INFORMATION DeviceInformation) +{ + PLIST_ENTRY NextEntry; + PSYMLINK_INFORMATION SymLink; + PUNIQUE_ID_REPLICATE UniqueId; + PASSOCIATED_DEVICE_ENTRY AssociatedDevice; + + /* Purge symbolic links list */ + while (!IsListEmpty(&(DeviceInformation->SymbolicLinksListHead))) + { + NextEntry = RemoveHeadList(&(DeviceInformation->SymbolicLinksListHead)); + SymLink = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + GlobalDeleteSymbolicLink(&(SymLink->Name)); + FreePool(SymLink->Name.Buffer); + } + + /* Purge replicated unique IDs list */ + while (!IsListEmpty(&(DeviceInformation->ReplicatedUniqueIdsListHead))) + { + NextEntry = RemoveHeadList(&(DeviceInformation->ReplicatedUniqueIdsListHead)); + UniqueId = CONTAINING_RECORD(NextEntry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry); + + FreePool(UniqueId->UniqueId); + FreePool(UniqueId); + } + + while (!IsListEmpty(&(DeviceInformation->AssociatedDevicesHead))) + { + NextEntry = RemoveHeadList(&(DeviceInformation->AssociatedDevicesHead)); + AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry); + + FreePool(AssociatedDevice->String.Buffer); + FreePool(AssociatedDevice); + } + + /* Free the rest of the buffers */ + FreePool(DeviceInformation->SymbolicName.Buffer); + if (DeviceInformation->KeepLinks) + { + FreePool(DeviceInformation->UniqueId); + } + FreePool(DeviceInformation->DeviceName.Buffer); + + /* Finally, stop waiting for notifications for this device */ + if (DeviceInformation->TargetDeviceNotificationEntry) + { + IoUnregisterPlugPlayNotification(DeviceInformation->TargetDeviceNotificationEntry); + } +} + +/* + * @implemented + */ +VOID +MountMgrFreeSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation) +{ + PLIST_ENTRY NextEntry; + PSYMLINK_INFORMATION SymlinkInformation; + + /* For all the saved links */ + while (!IsListEmpty(&(SavedLinkInformation->SymbolicLinksListHead))) + { + NextEntry = RemoveHeadList(&(SavedLinkInformation->SymbolicLinksListHead)); + SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + /* Remove from system & free */ + GlobalDeleteSymbolicLink(&(SymlinkInformation->Name)); + FreePool(SymlinkInformation->Name.Buffer); + FreePool(SymlinkInformation); + } + + /* And free unique ID & entry */ + FreePool(SavedLinkInformation->UniqueId); + FreePool(SavedLinkInformation); +} + + +/* + * @implemented + */ +VOID +NTAPI +MountMgrUnload(IN struct _DRIVER_OBJECT *DriverObject) +{ + PLIST_ENTRY NextEntry; + PUNIQUE_ID_WORK_ITEM WorkItem; + PDEVICE_EXTENSION DeviceExtension; + PDEVICE_INFORMATION DeviceInformation; + PSAVED_LINK_INFORMATION SavedLinkInformation; + + /* Don't get notification any longer */ + IoUnregisterShutdownNotification(gdeviceObject); + + /* Free registry buffer */ + DeviceExtension = gdeviceObject->DeviceExtension; + if (DeviceExtension->RegistryPath.Buffer) + { + FreePool(DeviceExtension->RegistryPath.Buffer); + DeviceExtension->RegistryPath.Buffer = NULL; + } + + InterlockedExchange(&Unloading, TRUE); + + KeInitializeEvent(&UnloadEvent, NotificationEvent, FALSE); + + /* Wait for workers to finish */ + if (InterlockedIncrement(&DeviceExtension->WorkerReferences)) + { + KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), + IO_NO_INCREMENT, 1, FALSE); + + KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL); + } + else + { + InterlockedDecrement(&(DeviceExtension->WorkerReferences)); + } + + /* Don't get any notification any longer² */ + IoUnregisterPlugPlayNotification(DeviceExtension->NotificationEntry); + + /* Acquire the driver exclusively */ + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, + FALSE, NULL); + + /* Clear offline devices list */ + while (!IsListEmpty(&(DeviceExtension->OfflineDeviceListHead))) + { + NextEntry = RemoveHeadList(&(DeviceExtension->OfflineDeviceListHead)); + DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + MountMgrFreeDeadDeviceInfo(DeviceInformation); + } + + /* Clear saved links list */ + while (!IsListEmpty(&(DeviceExtension->SavedLinksListHead))) + { + NextEntry = RemoveHeadList(&(DeviceExtension->SavedLinksListHead)); + SavedLinkInformation = CONTAINING_RECORD(NextEntry, SAVED_LINK_INFORMATION, SavedLinksListEntry); + MountMgrFreeSavedLink(SavedLinkInformation); + } + + /* Clear workers list */ + while (!IsListEmpty(&(DeviceExtension->UniqueIdWorkerItemListHead))) + { + NextEntry = RemoveHeadList(&(DeviceExtension->UniqueIdWorkerItemListHead)); + WorkItem = CONTAINING_RECORD(NextEntry, UNIQUE_ID_WORK_ITEM, UniqueIdWorkerItemListEntry); + + KeResetEvent(&UnloadEvent); + WorkItem->Event = &UnloadEvent; + + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, + 1, FALSE); + + IoCancelIrp(WorkItem->Irp); + KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL); + + IoFreeIrp(WorkItem->Irp); + FreePool(WorkItem->DeviceName.Buffer); + FreePool(WorkItem->IrpBuffer); + FreePool(WorkItem); + + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, + FALSE, NULL); + } + + /* If we have drive letter data, release */ + if (DeviceExtension->DriveLetterData) + { + FreePool(DeviceExtension->DriveLetterData); + DeviceExtension->DriveLetterData = NULL; + } + + /* Release driver & quit */ + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + + GlobalDeleteSymbolicLink(&DosDevicesMount); + IoDeleteDevice(gdeviceObject); +} + +/* + * @implemented + */ +ULONG +MountmgrReadNoAutoMount(IN PUNICODE_STRING RegistryPath) +{ + NTSTATUS Status; + ULONG Result, Default = 0; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + + /* Simply read data from register */ + QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + QueryTable[0].Name = L"NoAutoMount"; + QueryTable[0].EntryContext = &Result; + QueryTable[0].DefaultType = REG_NONE; + QueryTable[0].DefaultData = &Default; + QueryTable[0].DefaultLength = sizeof(ULONG); + + Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + RegistryPath->Buffer, + QueryTable, + NULL, + NULL); + if (!NT_SUCCESS(Status)) + { + return Default; + } + + return Result; +} + +/* + * @implemented + */ +NTSTATUS +MountMgrMountedDeviceArrival(IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicName, + IN BOOLEAN FromVolume) +{ + WCHAR Letter; + GUID StableGuid; + HANDLE LinkHandle; + ULONG SymLinkCount, i; + PLIST_ENTRY NextEntry; + PUNICODE_STRING SymLinks; + NTSTATUS Status, IntStatus; + OBJECT_ATTRIBUTES ObjectAttributes; + PSYMLINK_INFORMATION SymlinkInformation; + PMOUNTDEV_UNIQUE_ID UniqueId, NewUniqueId; + PSAVED_LINK_INFORMATION SavedLinkInformation; + PDEVICE_INFORMATION DeviceInformation, CurrentDevice; + WCHAR CSymLinkBuffer[MAX_PATH], LinkTargetBuffer[MAX_PATH]; + UNICODE_STRING TargetDeviceName, SuggestedLinkName, DeviceName, VolumeName, DriveLetter, LinkTarget, CSymLink; + BOOLEAN HasGuid, HasGptDriveLetter, Valid, UseOnlyIfThereAreNoOtherLinks, IsDrvLetter, IsOff, IsVolumeName, LinkError; + + /* New device = new structure to represent it */ + DeviceInformation = AllocatePool(sizeof(DEVICE_INFORMATION)); + if (!DeviceInformation) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Initialise device structure */ + RtlZeroMemory(DeviceInformation, sizeof(DEVICE_INFORMATION)); + InitializeListHead(&(DeviceInformation->SymbolicLinksListHead)); + InitializeListHead(&(DeviceInformation->ReplicatedUniqueIdsListHead)); + InitializeListHead(&(DeviceInformation->AssociatedDevicesHead)); + DeviceInformation->SymbolicName.Length = SymbolicName->Length; + DeviceInformation->SymbolicName.MaximumLength = SymbolicName->Length + sizeof(UNICODE_NULL); + DeviceInformation->SymbolicName.Buffer = AllocatePool(DeviceInformation->SymbolicName.MaximumLength); + if (!DeviceInformation->SymbolicName.Buffer) + { + FreePool(DeviceInformation); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Copy symbolic name */ + RtlCopyMemory(DeviceInformation->SymbolicName.Buffer, SymbolicName->Buffer, SymbolicName->Length); + DeviceInformation->SymbolicName.Buffer[DeviceInformation->SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL; + DeviceInformation->Volume = FromVolume; + DeviceInformation->DeviceExtension = DeviceExtension; + + /* Query as much data as possible about device */ + Status = QueryDeviceInformation(SymbolicName, + &TargetDeviceName, + &UniqueId, + &(DeviceInformation->Removable), + &HasGptDriveLetter, + &HasGuid, + &StableGuid, + &Valid); + if (!NT_SUCCESS(Status)) + { + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); + + for (NextEntry = DeviceExtension->OfflineDeviceListHead.Flink; + NextEntry != &(DeviceExtension->OfflineDeviceListHead); + NextEntry = NextEntry->Flink) + { + CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + + if (RtlEqualUnicodeString(&(DeviceInformation->SymbolicName), &(CurrentDevice->SymbolicName), TRUE)) + { + break; + } + } + + if (NextEntry != &(DeviceExtension->OfflineDeviceListHead)) + { + MountMgrFreeDeadDeviceInfo(DeviceInformation); + } + else + { + InsertTailList(&(DeviceExtension->OfflineDeviceListHead), &(DeviceInformation->DeviceListEntry)); + } + + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + + return Status; + } + + /* Save gathered data */ + DeviceInformation->UniqueId = UniqueId; + DeviceInformation->DeviceName = TargetDeviceName; + DeviceInformation->KeepLinks = FALSE; + + /* If we found system partition, mark it */ + if (DeviceExtension->DriveLetterData && UniqueId->UniqueIdLength == DeviceExtension->DriveLetterData->UniqueIdLength) + { + if (RtlCompareMemory(UniqueId->UniqueId, DeviceExtension->DriveLetterData->UniqueId, UniqueId->UniqueIdLength) + == UniqueId->UniqueIdLength) + { + IoSetSystemPartition(&TargetDeviceName); + } + } + + /* Check suggested link name */ + Status = QuerySuggestedLinkName(&(DeviceInformation->SymbolicName), + &SuggestedLinkName, + &UseOnlyIfThereAreNoOtherLinks); + if (!NT_SUCCESS(Status)) + { + SuggestedLinkName.Buffer = NULL; + } + + /* If it's OK, set it and save its letter (if any) */ + if (SuggestedLinkName.Buffer && IsDriveLetter(&SuggestedLinkName)) + { + DeviceInformation->SuggestedDriveLetter = SuggestedLinkName.Buffer[LETTER_POSITION]; + } + + /* Acquire driver exclusively */ + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); + + /* Check if we already have device in to prevent double registration */ + for (NextEntry = DeviceExtension->DeviceListHead.Flink; + NextEntry != &(DeviceExtension->DeviceListHead); + NextEntry = NextEntry->Flink) + { + CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + + if (RtlEqualUnicodeString(&(DeviceInformation->DeviceName), &TargetDeviceName, TRUE)) + { + break; + } + } + + /* If we found it, clear ours, and return success, all correct */ + if (NextEntry != &(DeviceExtension->DeviceListHead)) + { + if (SuggestedLinkName.Buffer) + { + FreePool(SuggestedLinkName.Buffer); + } + + FreePool(UniqueId); + FreePool(TargetDeviceName.Buffer); + FreePool(DeviceInformation->DeviceName.Buffer); + FreePool(DeviceInformation); + + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + + return STATUS_SUCCESS; + } + + /* Check if there are symlinks associated with our device in registry */ + Status = QuerySymbolicLinkNamesFromStorage(DeviceExtension, + DeviceInformation, + (SuggestedLinkName.Buffer) ? &SuggestedLinkName : NULL, + UseOnlyIfThereAreNoOtherLinks, + &SymLinks, + &SymLinkCount, + HasGuid, + &StableGuid); + + /* If our device is a CD-ROM */ + if (RtlPrefixUnicodeString(&DeviceCdRom, &TargetDeviceName, TRUE)) + { + LinkTarget.Length = 0; + LinkTarget.MaximumLength = sizeof(LinkTargetBuffer); + LinkTarget.Buffer = LinkTargetBuffer; + + RtlCopyMemory(CSymLinkBuffer, Cunc, sizeof(Cunc)); + RtlInitUnicodeString(&CSymLink, CSymLinkBuffer); + + /* Start checking all letters that could have been associated */ + for (Letter = L'D'; Letter <= L'Z'; Letter++) + { + CSymLink.Buffer[LETTER_POSITION] = Letter; + + InitializeObjectAttributes(&ObjectAttributes, + &CSymLink, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + /* Try to open the associated symlink */ + Status = ZwOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + continue; + } + + /* And query its target */ + Status = ZwQuerySymbolicLinkObject(LinkHandle, &LinkTarget, NULL); + ZwClose(LinkHandle); + + if (!NT_SUCCESS(Status)) + { + continue; + } + + IntStatus = STATUS_UNSUCCESSFUL; + if (!RtlEqualUnicodeString(&LinkTarget, &DeviceInformation->DeviceName, FALSE)) + { + continue; + } + + /* This link is matching our device, whereas it's not supposed to have any + * symlink associated. + * Delete it + */ + if (!SymLinkCount) + { + IoDeleteSymbolicLink(&CSymLink); + continue; + } + + /* Now, for all the symlinks, check for ours */ + for (i = 0; i < SymLinkCount; i++) + { + if (IsDriveLetter(&(SymLinks[i]))) + { + /* If it exists, that's correct */ + if (SymLinks[i].Buffer[LETTER_POSITION] == Letter) + { + IntStatus = STATUS_SUCCESS; + } + } + } + + /* Useless link, delete it */ + if (IntStatus == STATUS_UNSUCCESSFUL) + { + IoDeleteSymbolicLink(&CSymLink); + } + } + } + + /* Suggested name is no longer required */ + if (SuggestedLinkName.Buffer) + { + FreePool(SuggestedLinkName.Buffer); + } + + /* If if failed, ensure we don't take symlinks into account */ + if (!NT_SUCCESS(Status)) + { + SymLinks = NULL; + SymLinkCount = 0; + } + + /* Now we queried them, remove the symlinks */ + SavedLinkInformation = RemoveSavedLinks(DeviceExtension, UniqueId); + + IsDrvLetter = FALSE; + IsOff = FALSE; + IsVolumeName = FALSE; + /* For all the symlinks */ + for (i = 0; i < SymLinkCount; i++) + { + /* Check if our device is a volume */ + if (MOUNTMGR_IS_VOLUME_NAME(&(SymLinks[i]))) + { + IsVolumeName = TRUE; + } + /* If it has a drive letter */ + else if (IsDriveLetter(&(SymLinks[i]))) + { + if (IsDrvLetter) + { + DeleteFromLocalDatabase(&(SymLinks[i]), UniqueId); + continue; + } + else + { + IsDrvLetter = TRUE; + } + } + + /* And recreate the symlink to our device */ + Status = GlobalCreateSymbolicLink(&(SymLinks[i]), &TargetDeviceName); + if (!NT_SUCCESS(Status)) + { + LinkError = TRUE; + + if ((SavedLinkInformation && !RedirectSavedLink(SavedLinkInformation, &(SymLinks[i]), &TargetDeviceName)) || + !SavedLinkInformation) + { + Status = QueryDeviceInformation(&(SymLinks[i]), &DeviceName, NULL, NULL, NULL, NULL, NULL, NULL); + if (NT_SUCCESS(Status)) + { + LinkError = RtlEqualUnicodeString(&TargetDeviceName, &DeviceName, TRUE); + FreePool(DeviceName.Buffer); + } + + if (!LinkError) + { + if (IsDriveLetter(&(SymLinks[i]))) + { + IsDrvLetter = FALSE; + DeleteFromLocalDatabase(&(SymLinks[i]), UniqueId); + } + + FreePool(SymLinks[i].Buffer); + continue; + } + } + } + + /* Check if was offline */ + if (IsOffline(&(SymLinks[i]))) + { + IsOff = TRUE; + } + + /* Finally, associate this symlink with the device */ + SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION)); + if (!SymlinkInformation) + { + GlobalDeleteSymbolicLink(&(SymLinks[i])); + FreePool(SymLinks[i].Buffer); + continue; + } + + SymlinkInformation->Name = SymLinks[i]; + SymlinkInformation->Online = TRUE; + + InsertTailList(&(DeviceInformation->SymbolicLinksListHead), + &(SymlinkInformation->SymbolicLinksListEntry)); + } + + /* Now, for all the recreated symlinks, notify their recreation */ + for (NextEntry = DeviceInformation->SymbolicLinksListHead.Flink; + NextEntry != &(DeviceInformation->SymbolicLinksListHead); + NextEntry = NextEntry->Flink) + { + SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + SendLinkCreated(&(SymlinkInformation->Name)); + } + + /* If we had saved links, it's time to free them */ + if (SavedLinkInformation) + { + MountMgrFreeSavedLink(SavedLinkInformation); + } + + /* If our device doesn't have a volume name */ + if (!IsVolumeName) + { + /* It's time to create one */ + Status = CreateNewVolumeName(&VolumeName, NULL); + if (NT_SUCCESS(Status)) + { + /* Write it to global database */ + RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + VolumeName.Buffer, + REG_BINARY, + UniqueId->UniqueId, + UniqueId->UniqueIdLength); + + /* And create the symlink */ + GlobalCreateSymbolicLink(&VolumeName, &TargetDeviceName); + + SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION)); + if (!SymlinkInformation) + { + FreePool(VolumeName.Buffer); + } + /* Finally, associate it with the device and notify creation */ + else + { + SymlinkInformation->Name = VolumeName; + SymlinkInformation->Online = TRUE; + InsertTailList(&(DeviceInformation->SymbolicLinksListHead), + &(SymlinkInformation->SymbolicLinksListEntry)); + + SendLinkCreated(&VolumeName); + } + } + } + + /* If we found a drive letter, then, ignore the suggested one */ + if (IsDrvLetter) + { + DeviceInformation->SuggestedDriveLetter = 0; + } + /* Else, it's time to set up one */ + else if (!DeviceExtension->NoAutoMount && !DeviceInformation->Removable && + DeviceExtension->AutomaticDriveLetter && HasGptDriveLetter && + DeviceInformation->SuggestedDriveLetter && + !HasNoDriveLetterEntry(UniqueId)) + { + /* Create a new drive letter */ + Status = CreateNewDriveLetterName(&DriveLetter, &TargetDeviceName, + DeviceInformation->SuggestedDriveLetter, + NULL); + if (!NT_SUCCESS(Status)) + { + CreateNoDriveLetterEntry(UniqueId); + } + else + { + /* Save it to global database */ + RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + DriveLetter.Buffer, + REG_BINARY, + UniqueId->UniqueId, + UniqueId->UniqueIdLength); + + /* Associate it with the device and notify creation */ + SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION)); + if (!SymlinkInformation) + { + FreePool(DriveLetter.Buffer); + } + else + { + SymlinkInformation->Name = DriveLetter; + SymlinkInformation->Online = TRUE; + InsertTailList(&(DeviceInformation->SymbolicLinksListHead), + &(SymlinkInformation->SymbolicLinksListEntry)); + + SendLinkCreated(&DriveLetter); + } + } + } + + /* If required, register for notifications about the device */ + if (!FromVolume) + { + RegisterForTargetDeviceNotification(DeviceExtension, DeviceInformation); + } + + /* Finally, insert the device into our devices list */ + InsertTailList(&(DeviceExtension->DeviceListHead), &(DeviceInformation->DeviceListEntry)); + + /* Copy device unique ID */ + NewUniqueId = AllocatePool(UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); + if (NewUniqueId) + { + NewUniqueId->UniqueIdLength = UniqueId->UniqueIdLength; + RtlCopyMemory(NewUniqueId->UniqueId, UniqueId->UniqueId, UniqueId->UniqueIdLength); + } + + /* If device's offline or valid, skip its notifications */ + if (IsOff || Valid) + { + DeviceInformation->SkipNotifications = TRUE; + } + + /* In case device is valid and is set to no automount, + * set it offline. + */ + if (DeviceExtension->NoAutoMount || IsDrvLetter) + { + IsOff = !DeviceInformation->SkipNotifications; + } + else + { + IsOff = FALSE; + } + + /* Finally, release the exclusive lock */ + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + + /* If device is not offline, notify its arrival */ + if (!IsOff) + { + SendOnlineNotification(SymbolicName); + } + + /* If we had symlinks (from storage), free them */ + if (SymLinks) + { + FreePool(SymLinks); + } + + /* Notify about unique id change */ + if (NewUniqueId) + { + IssueUniqueIdChangeNotify(DeviceExtension, SymbolicName, NewUniqueId); + FreePool(NewUniqueId); + } + + /* If this drive was set to have a drive letter automatically + * Now it's back, local databases sync will be required + */ + if (DeviceExtension->AutomaticDriveLetter) + { + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); + + ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation); + + NextEntry = DeviceExtension->DeviceListHead.Flink; + CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + while (CurrentDevice != DeviceInformation) + { + if (!CurrentDevice->NoDatabase) + { + ReconcileThisDatabaseWithMaster(DeviceExtension, CurrentDevice); + } + + NextEntry = NextEntry->Flink; + CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + } + + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + } + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +VOID +MountMgrMountedDeviceRemoval(IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING DeviceName) +{ + PLIST_ENTRY NextEntry, DeviceEntry; + PUNIQUE_ID_REPLICATE UniqueIdReplicate; + PSYMLINK_INFORMATION SymlinkInformation; + PASSOCIATED_DEVICE_ENTRY AssociatedDevice; + PSAVED_LINK_INFORMATION SavedLinkInformation = NULL; + PDEVICE_INFORMATION DeviceInformation, CurrentDevice; + + /* Acquire device exclusively */ + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); + + /* Look for the leaving device */ + for (NextEntry = DeviceExtension->DeviceListHead.Flink; + NextEntry != &(DeviceExtension->DeviceListHead); + NextEntry = NextEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + + if (!RtlCompareUnicodeString(&(DeviceInformation->SymbolicName), DeviceName, TRUE)) + { + break; + } + } + + /* If we found it */ + if (NextEntry != &(DeviceExtension->DeviceListHead)) + { + /* If it's asked to keep links, then, prepare to save them */ + if (DeviceInformation->KeepLinks) + { + SavedLinkInformation = AllocatePool(sizeof(SAVED_LINK_INFORMATION)); + if (!SavedLinkInformation) + { + DeviceInformation->KeepLinks = FALSE; + } + } + + /* If it's possible (and asked), start to save them */ + if (DeviceInformation->KeepLinks) + { + InsertTailList(&(DeviceExtension->SavedLinksListHead), &(SavedLinkInformation->SavedLinksListEntry)); + InitializeListHead(&(SavedLinkInformation->SymbolicLinksListHead)); + SavedLinkInformation->UniqueId = DeviceInformation->UniqueId; + } + + /* For all the symlinks */ + while (!IsListEmpty(&(DeviceInformation->SymbolicLinksListHead))) + { + NextEntry = RemoveHeadList(&(DeviceInformation->SymbolicLinksListHead)); + SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + /* If we have to, save the link */ + if (DeviceInformation->KeepLinks) + { + InsertTailList(&(SavedLinkInformation->SymbolicLinksListHead), &(SymlinkInformation->SymbolicLinksListEntry)); + } + /* Otherwise, just release it */ + else + { + GlobalDeleteSymbolicLink(&(SymlinkInformation->Name)); + FreePool(SymlinkInformation->Name.Buffer); + FreePool(SymlinkInformation); + } + } + + /* Free all the replicated unique IDs */ + while (!IsListEmpty(&(DeviceInformation->ReplicatedUniqueIdsListHead))) + { + NextEntry = RemoveHeadList(&(DeviceInformation->ReplicatedUniqueIdsListHead)); + UniqueIdReplicate = CONTAINING_RECORD(NextEntry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry); + + + FreePool(UniqueIdReplicate->UniqueId); + FreePool(UniqueIdReplicate); + } + + while (!IsListEmpty(&(DeviceInformation->AssociatedDevicesHead))) + { + NextEntry = RemoveHeadList(&(DeviceInformation->AssociatedDevicesHead)); + AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry); + + DeviceInformation->NoDatabase = TRUE; + FreePool(AssociatedDevice->String.Buffer); + FreePool(AssociatedDevice); + } + + /* Remove device from the device list */ + RemoveEntryList(&(DeviceInformation->DeviceListEntry)); + + /* If there are still devices, check if some were associated with ours */ + if (!IsListEmpty(&(DeviceInformation->DeviceListEntry))) + { + for (NextEntry = DeviceExtension->DeviceListHead.Flink; + NextEntry != &(DeviceExtension->DeviceListHead); + NextEntry = NextEntry->Flink) + { + CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + + /* And then, remove them */ + DeviceEntry = CurrentDevice->AssociatedDevicesHead.Flink; + while (DeviceEntry != &(CurrentDevice->AssociatedDevicesHead)) + { + AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry); + DeviceEntry = DeviceEntry->Flink; + + if (AssociatedDevice->DeviceInformation != DeviceInformation) + { + continue; + } + + RemoveEntryList(&(AssociatedDevice->AssociatedDevicesEntry)); + FreePool(AssociatedDevice->String.Buffer); + FreePool(AssociatedDevice); + } + } + } + + /* Finally, clean up device name, symbolic name */ + FreePool(DeviceInformation->SymbolicName.Buffer); + if (!DeviceInformation->KeepLinks) + { + FreePool(DeviceInformation->UniqueId); + } + FreePool(DeviceInformation->DeviceName.Buffer); + + /* Unregister notifications */ + if (DeviceInformation->TargetDeviceNotificationEntry) + { + IoUnregisterPlugPlayNotification(DeviceInformation->TargetDeviceNotificationEntry); + } + + /* And leave */ + FreePool(DeviceInformation); + } + else + { + /* We didn't find device, perhaps because it was offline */ + for (NextEntry = DeviceExtension->OfflineDeviceListHead.Flink; + NextEntry != &(DeviceExtension->OfflineDeviceListHead); + NextEntry = NextEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + + /* It was, remove it */ + if (RtlCompareUnicodeString(&(DeviceInformation->SymbolicName), DeviceName, TRUE) == 0) + { + RemoveEntryList(&(DeviceInformation->DeviceListEntry)); + MountMgrFreeDeadDeviceInfo(DeviceInformation); + break; + } + } + } + + /* Releave driver */ + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +MountMgrMountedDeviceNotification(IN PVOID NotificationStructure, + IN PVOID Context) +{ + BOOLEAN OldState; + PDEVICE_EXTENSION DeviceExtension; + PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification; + + /* Notification for a device arrived */ + /* Disable hard errors */ + OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread()); + PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE); + + DeviceExtension = Context; + Notification = NotificationStructure; + + /* Dispatch according to the event */ + if (IsEqualGUID(&(Notification->Event), &GUID_DEVICE_INTERFACE_ARRIVAL)) + { + MountMgrMountedDeviceArrival(DeviceExtension, Notification->SymbolicLinkName, FALSE); + } + else if (IsEqualGUID(&(Notification->Event), &GUID_DEVICE_INTERFACE_REMOVAL)) + { + MountMgrMountedDeviceRemoval(DeviceExtension, Notification->SymbolicLinkName); + } + + /* Reset hard errors */ + PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState); + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +MountMgrCreateClose(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION Stack; + NTSTATUS Status = STATUS_SUCCESS; + + Stack = IoGetCurrentIrpStackLocation(Irp); + + /* Allow driver opening for communication + * as long as it's not taken for a directory + */ + if (Stack->MajorFunction == IRP_MJ_CREATE && + Stack->Parameters.Create.Options & FILE_DIRECTORY_FILE) + { + Status = STATUS_NOT_A_DIRECTORY; + } + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; +} + +/* + * @implemented + */ +VOID +NTAPI +MountMgrCancel(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + RemoveEntryList(&(Irp->Tail.Overlay.ListEntry)); + + IoReleaseCancelSpinLock(Irp->CancelIrql); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + IofCompleteRequest(Irp, IO_NO_INCREMENT); +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +MountMgrCleanup(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIRP ListIrp; + KIRQL OldIrql; + PLIST_ENTRY NextEntry; + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + PDEVICE_EXTENSION DeviceExtension; + + DeviceExtension = DeviceObject->DeviceExtension; + Stack = IoGetCurrentIrpStackLocation(Irp); + FileObject = Stack->FileObject; + + IoAcquireCancelSpinLock(&OldIrql); + + /* If IRP list if empty, it's OK */ + if (IsListEmpty(&(DeviceExtension->IrpListHead))) + { + IoReleaseCancelSpinLock(OldIrql); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; + } + + /* Otherwise, cancel all the IRPs */ + NextEntry = &(DeviceExtension->IrpListHead); + do + { + ListIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry); + if (IoGetCurrentIrpStackLocation(ListIrp)->FileObject == FileObject) + { + ListIrp->Cancel = TRUE; + ListIrp->CancelIrql = OldIrql; + ListIrp->CancelRoutine = NULL; + MountMgrCancel(DeviceObject, ListIrp); + + IoAcquireCancelSpinLock(&OldIrql); + } + + NextEntry = NextEntry->Flink; + } + while (NextEntry != &(DeviceExtension->IrpListHead)); + + IoReleaseCancelSpinLock(OldIrql); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +MountMgrShutdown(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PDEVICE_EXTENSION DeviceExtension; + + DeviceExtension = DeviceObject->DeviceExtension; + + InterlockedExchange(&Unloading, TRUE); + + KeInitializeEvent(&UnloadEvent, NotificationEvent, FALSE); + + /* Wait for workers */ + if (InterlockedIncrement(&(DeviceExtension->WorkerReferences))) + { + KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore), + IO_NO_INCREMENT, + 1, + FALSE); + KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL); + } + else + { + InterlockedDecrement(&(DeviceExtension->WorkerReferences)); + } + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + +/* FUNCTIONS ****************************************************************/ + +NTSTATUS +NTAPI +DriverEntry(IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath) +{ + NTSTATUS Status; + PDEVICE_OBJECT DeviceObject; + PDEVICE_EXTENSION DeviceExtension; + + RtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, DatabasePath); + + Status = IoCreateDevice(DriverObject, + sizeof(DEVICE_EXTENSION), + &DeviceMount, + FILE_DEVICE_NETWORK, + FILE_DEVICE_SECURE_OPEN, + FALSE, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + DriverObject->DriverUnload = MountMgrUnload; + + DeviceExtension = DeviceObject->DeviceExtension; + RtlZeroMemory(DeviceExtension, sizeof(DEVICE_EXTENSION)); + DeviceExtension->DeviceObject = DeviceObject; + DeviceExtension->DriverObject = DriverObject; + + InitializeListHead(&(DeviceExtension->DeviceListHead)); + InitializeListHead(&(DeviceExtension->OfflineDeviceListHead)); + + KeInitializeSemaphore(&(DeviceExtension->DeviceLock), 1, 1); + KeInitializeSemaphore(&(DeviceExtension->RemoteDatabaseLock), 1, 1); + + InitializeListHead(&(DeviceExtension->IrpListHead)); + DeviceExtension->EpicNumber = 1; + + InitializeListHead(&(DeviceExtension->SavedLinksListHead)); + + InitializeListHead(&(DeviceExtension->WorkerQueueListHead)); + KeInitializeSemaphore(&(DeviceExtension->WorkerSemaphore), 0, MAXLONG); + DeviceExtension->WorkerReferences = -1; + KeInitializeSpinLock(&(DeviceExtension->WorkerLock)); + + InitializeListHead(&(DeviceExtension->UniqueIdWorkerItemListHead)); + InitializeListHead(&(DeviceExtension->OnlineNotificationListHead)); + DeviceExtension->OnlineNotificationCount = 1; + + DeviceExtension->RegistryPath.Length = RegistryPath->Length; + DeviceExtension->RegistryPath.MaximumLength = RegistryPath->Length + sizeof(WCHAR); + DeviceExtension->RegistryPath.Buffer = AllocatePool(DeviceExtension->RegistryPath.MaximumLength); + if (!DeviceExtension->RegistryPath.Buffer) + { + IoDeleteDevice(DeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyUnicodeString(&(DeviceExtension->RegistryPath), RegistryPath); + + DeviceExtension->NoAutoMount = MountmgrReadNoAutoMount(&(DeviceExtension->RegistryPath)); + + GlobalCreateSymbolicLink(&DosDevicesMount, &DeviceMount); + + /* Register for device arrival & removal. Ask to be notified for already + * present devices + */ + Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + &MountedDevicesGuid, + DriverObject, + MountMgrMountedDeviceNotification, + DeviceObject, + &(DeviceExtension->NotificationEntry)); + + if (!NT_SUCCESS(Status)) + { + IoDeleteDevice(DeviceObject); + return Status; + } + + DriverObject->MajorFunction[IRP_MJ_CREATE] = + DriverObject->MajorFunction[IRP_MJ_CLOSE] = MountMgrCreateClose; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MountMgrDeviceControl; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MountMgrCleanup; + DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = MountMgrShutdown; + + gdeviceObject = DeviceObject; + + Status = IoRegisterShutdownNotification(DeviceObject); + if (!NT_SUCCESS(Status)) + { + IoDeleteDevice(DeviceObject); + } + + return Status; +} diff --git a/reactos/drivers/filters/mountmgr/mountmgr.rbuild b/reactos/drivers/filters/mountmgr/mountmgr.rbuild new file mode 100644 index 00000000000..52aa848418f --- /dev/null +++ b/reactos/drivers/filters/mountmgr/mountmgr.rbuild @@ -0,0 +1,20 @@ + + + + + 0x05020400 + . + ntoskrnl + hal + ioevent + wdmguid + database.c + device.c + mountmgr.c + notify.c + point.c + symlink.c + uniqueid.c + mountmgr.rc + mntmgr.h + diff --git a/reactos/drivers/filters/mountmgr/mountmgr.rc b/reactos/drivers/filters/mountmgr/mountmgr.rc new file mode 100644 index 00000000000..cf38d598d2f --- /dev/null +++ b/reactos/drivers/filters/mountmgr/mountmgr.rc @@ -0,0 +1,6 @@ + +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "Mount Point Manager\0" +#define REACTOS_STR_INTERNAL_NAME "mountmgr.sys\0" +#define REACTOS_STR_ORIGINAL_FILENAME "mountmgr.sys\0" +#include diff --git a/reactos/drivers/filters/mountmgr/notify.c b/reactos/drivers/filters/mountmgr/notify.c new file mode 100644 index 00000000000..2b2acbc2aa4 --- /dev/null +++ b/reactos/drivers/filters/mountmgr/notify.c @@ -0,0 +1,757 @@ +/* + * ReactOS kernel + * Copyright (C) 2011 ReactOS Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/filesystem/mountmgr/notify.c + * PURPOSE: Mount Manager - Notifications handlers + * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) + * Alex Ionescu (alex.ionescu@reactos.org) + */ + +/* INCLUDES *****************************************************************/ + +#include "mntmgr.h" + +#define NDEBUG +#include + +/* + * @implemented + */ +VOID +SendOnlineNotification(IN PUNICODE_STRING SymbolicName) +{ + PIRP Irp; + KEVENT Event; + NTSTATUS Status; + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + PDEVICE_OBJECT DeviceObject; + IO_STATUS_BLOCK IoStatusBlock; + + /* Get device object */ + Status = IoGetDeviceObjectPointer(SymbolicName, + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return; + } + + /* And attached device object */ + DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); + + /* And send VOLUME_ONLINE */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_VOLUME_ONLINE, + DeviceObject, + NULL, 0, + NULL, 0, + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + goto Cleanup; + } + + Stack = IoGetNextIrpStackLocation(Irp); + Stack->FileObject = FileObject; + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + +Cleanup: + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + + return; +} + +/* + * @implemented + */ +VOID +NTAPI +SendOnlineNotificationWorker(IN PVOID Parameter) +{ + KIRQL OldIrql; + PLIST_ENTRY Head; + PDEVICE_EXTENSION DeviceExtension; + PONLINE_NOTIFICATION_WORK_ITEM WorkItem; + PONLINE_NOTIFICATION_WORK_ITEM NewWorkItem; + + WorkItem = (PONLINE_NOTIFICATION_WORK_ITEM)Parameter; + DeviceExtension = WorkItem->DeviceExtension; + + /* First, send the notification */ + SendOnlineNotification(&(WorkItem->SymbolicName)); + + OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock)); + /* If there are no notifications running any longer, reset event */ + if (--DeviceExtension->OnlineNotificationCount == 0) + { + KeSetEvent(&(DeviceExtension->OnlineNotificationEvent), 0, FALSE); + } + + /* If there are still notifications in queue */ + if (!IsListEmpty(&(DeviceExtension->OnlineNotificationListHead))) + { + /* Queue a new one for execution */ + Head = RemoveHeadList(&(DeviceExtension->OnlineNotificationListHead)); + NewWorkItem = CONTAINING_RECORD(Head, ONLINE_NOTIFICATION_WORK_ITEM, List); + KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql); + NewWorkItem->List.Blink = NULL; + NewWorkItem->List.Flink = NULL; + ExQueueWorkItem((PWORK_QUEUE_ITEM)NewWorkItem, DelayedWorkQueue); + } + else + { + /* Mark it's over */ + DeviceExtension->OnlineNotificationWorkerActive = 0; + KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql); + } + + FreePool(WorkItem->SymbolicName.Buffer); + FreePool(WorkItem); + + return; +} + +/* + * @implemented + */ +VOID +PostOnlineNotification(IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicName) +{ + KIRQL OldIrql; + PONLINE_NOTIFICATION_WORK_ITEM WorkItem; + + /* Allocate a notification work item */ + WorkItem = AllocatePool(sizeof(ONLINE_NOTIFICATION_WORK_ITEM)); + if (!WorkItem) + { + return; + } + + WorkItem->List.Flink = NULL; + WorkItem->DeviceExtension = DeviceExtension; + WorkItem->WorkerRoutine = SendOnlineNotificationWorker; + WorkItem->Parameter = WorkItem; + WorkItem->SymbolicName.Length = SymbolicName->Length; + WorkItem->SymbolicName.MaximumLength = SymbolicName->Length + sizeof(WCHAR); + WorkItem->SymbolicName.Buffer = AllocatePool(WorkItem->SymbolicName.MaximumLength); + if (!WorkItem->SymbolicName.Buffer) + { + FreePool(WorkItem); + return; + } + + RtlCopyMemory(WorkItem->SymbolicName.Buffer, SymbolicName->Buffer, SymbolicName->Length); + WorkItem->SymbolicName.Buffer[SymbolicName->Length / sizeof(WCHAR)] = UNICODE_NULL; + + OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock)); + DeviceExtension->OnlineNotificationCount++; + + /* If no worker are active */ + if (DeviceExtension->OnlineNotificationWorkerActive == 0) + { + /* Queue that one for execution */ + DeviceExtension->OnlineNotificationWorkerActive == 1; + ExQueueWorkItem((PWORK_QUEUE_ITEM)WorkItem, DelayedWorkQueue); + } + else + { + /* Otherwise, just put it in the queue list */ + InsertTailList(&(DeviceExtension->OnlineNotificationListHead), &(WorkItem->List)); + } + + KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql); + + return; +} + +/* + * @implemented + */ +VOID +WaitForOnlinesToComplete(IN PDEVICE_EXTENSION DeviceExtension) +{ + KIRQL OldIrql; + + KeInitializeEvent(&(DeviceExtension->OnlineNotificationEvent), NotificationEvent, FALSE); + + OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock)); + + /* Just wait all the worker are done */ + if (DeviceExtension->OnlineNotificationCount != 1) + { + DeviceExtension->OnlineNotificationCount--; + KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql); + + KeWaitForSingleObject(&(DeviceExtension->OnlineNotificationEvent), + Executive, + KernelMode, + FALSE, + NULL); + + OldIrql = KfAcquireSpinLock(&(DeviceExtension->WorkerLock)); + DeviceExtension->OnlineNotificationCount++; + } + + KfReleaseSpinLock(&(DeviceExtension->WorkerLock), OldIrql); +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +MountMgrTargetDeviceNotification(IN PVOID NotificationStructure, + IN PVOID Context) +{ + PDEVICE_EXTENSION DeviceExtension; + PDEVICE_INFORMATION DeviceInformation; + PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification; + + DeviceInformation = Context; + DeviceExtension = DeviceInformation->DeviceExtension; + Notification = NotificationStructure; + + /* If it's to signal that removal is complete, then, execute the function */ + if (IsEqualGUID(&(Notification->Event), &GUID_TARGET_DEVICE_REMOVE_COMPLETE)) + { + MountMgrMountedDeviceRemoval(DeviceExtension, Notification->SymbolicLinkName); + } + /* It it's to signal that a volume has been mounted + * Verify if a database sync is required and execute it + */ + else if (IsEqualGUID(&(Notification->Event), &GUID_IO_VOLUME_MOUNT)) + { + if (InterlockedCompareExchange(&(DeviceInformation->MountState), + FALSE, + TRUE) == TRUE) + { + InterlockedDecrement(&(DeviceInformation->MountState)); + } + else + { + if (DeviceInformation->NeedsReconcile) + { + DeviceInformation->NeedsReconcile = FALSE; + ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation); + } + } + } + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +VOID +RegisterForTargetDeviceNotification(IN PDEVICE_EXTENSION DeviceExtension, + IN PDEVICE_INFORMATION DeviceInformation) +{ + NTSTATUS Status; + PFILE_OBJECT FileObject; + PDEVICE_OBJECT DeviceObject; + + /* Get device object */ + Status = IoGetDeviceObjectPointer(&(DeviceInformation->DeviceName), + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return; + } + + /* And simply register for notifications */ + Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, + 0, FileObject, + DeviceExtension->DriverObject, + MountMgrTargetDeviceNotification, + DeviceInformation, + &(DeviceInformation->TargetDeviceNotificationEntry)); + if (!NT_SUCCESS(Status)) + { + DeviceInformation->TargetDeviceNotificationEntry = NULL; + } + + ObfDereferenceObject(FileObject); + + return; +} + +/* + * @implemented + */ +VOID +MountMgrNotify(IN PDEVICE_EXTENSION DeviceExtension) +{ + PIRP Irp; + KIRQL OldIrql; + LIST_ENTRY CopyList; + PLIST_ENTRY NextEntry; + + /* Increase the epic number */ + DeviceExtension->EpicNumber++; + + InitializeListHead(&CopyList); + + /* Copy all the pending IRPs for notification */ + IoAcquireCancelSpinLock(&OldIrql); + while (!IsListEmpty(&(DeviceExtension->IrpListHead))) + { + NextEntry = RemoveHeadList(&(DeviceExtension->IrpListHead)); + Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry); + InsertTailList(&CopyList, &(Irp->Tail.Overlay.ListEntry)); + } + IoReleaseCancelSpinLock(OldIrql); + + /* Then, notifiy them one by one */ + while (!IsListEmpty(&CopyList)) + { + NextEntry = RemoveHeadList(&CopyList); + Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry); + + *((PULONG)Irp->AssociatedIrp.SystemBuffer) = DeviceExtension->EpicNumber; + Irp->IoStatus.Information = sizeof(DeviceExtension->EpicNumber); + + IofCompleteRequest(Irp, IO_NO_INCREMENT); + } +} + +/* + * @implemented + */ +VOID +MountMgrNotifyNameChange(IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING DeviceName, + IN BOOLEAN ValidateVolume) +{ + PIRP Irp; + KEVENT Event; + NTSTATUS Status; + PLIST_ENTRY NextEntry; + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + PDEVICE_OBJECT DeviceObject; + IO_STATUS_BLOCK IoStatusBlock; + PDEVICE_RELATIONS DeviceRelations; + PDEVICE_INFORMATION DeviceInformation; + TARGET_DEVICE_CUSTOM_NOTIFICATION DeviceNotification; + + /* If we have to validate volume */ + if (ValidateVolume) + { + /* Then, ensure we can find the device */ + NextEntry = DeviceExtension->DeviceListHead.Flink; + while (NextEntry != &(DeviceExtension->DeviceListHead)) + { + DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry); + if (RtlCompareUnicodeString(DeviceName, &(DeviceInformation->DeviceName), TRUE) == 0) + { + break; + } + } + + if (NextEntry == &(DeviceExtension->DeviceListHead) || + !DeviceInformation->Volume) + { + return; + } + } + + /* Then, get device object */ + Status = IoGetDeviceObjectPointer(DeviceName, + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return; + } + + DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + /* Set up empty IRP (yes, yes!) */ + Irp = IoBuildDeviceIoControlRequest(0, + DeviceObject, + NULL, + 0, + NULL, + 0, + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + } + + Stack = IoGetNextIrpStackLocation(Irp); + + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + Irp->IoStatus.Information = 0; + + /* Properly set it, we want to query device relations */ + Stack->MajorFunction = IRP_MJ_PNP; + Stack->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS; + Stack->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation; + Stack->FileObject = FileObject; + + /* And call driver */ + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + + if (!NT_SUCCESS(Status)) + { + return; + } + + /* Validate device return */ + DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information; + if (DeviceRelations->Count < 1) + { + ExFreePool(DeviceRelations); + return; + } + + DeviceObject = DeviceRelations->Objects[0]; + ExFreePool(DeviceRelations); + + /* Set up real notification */ + DeviceNotification.Version = 1; + DeviceNotification.Size = sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION); + DeviceNotification.Event = GUID_IO_VOLUME_NAME_CHANGE; + DeviceNotification.FileObject = NULL; + DeviceNotification.NameBufferOffset = -1; + + /* And report */ + IoReportTargetDeviceChangeAsynchronous(DeviceObject, + &DeviceNotification, + NULL, NULL); + + ObfDereferenceObject(DeviceObject); + + return; +} + +/* + * @implemented + */ +VOID +RemoveWorkItem(IN PUNIQUE_ID_WORK_ITEM WorkItem) +{ + PDEVICE_EXTENSION DeviceExtension; + + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); + + /* If even if being worked, it's too late */ + if (WorkItem->Event) + { + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + KeSetEvent(WorkItem->Event, 0, FALSE); + } + else + { + /* Otherwise, remove it from the list, and delete it */ + RemoveEntryList(&(WorkItem->UniqueIdWorkerItemListEntry)); + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + IoFreeIrp(WorkItem->Irp); + FreePool(WorkItem->DeviceName.Buffer); + FreePool(WorkItem->IrpBuffer); + FreePool(WorkItem); + } +} + +/* + * @implemented + */ +VOID +NTAPI +UniqueIdChangeNotifyWorker(IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context) +{ + PUNIQUE_ID_WORK_ITEM WorkItem = Context; + PMOUNTDEV_UNIQUE_ID OldUniqueId, NewUniqueId; + PMOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT UniqueIdChange; + + /* Validate worker */ + if (!NT_SUCCESS(WorkItem->Irp->IoStatus.Status)) + { + RemoveWorkItem(WorkItem); + return; + } + + UniqueIdChange = WorkItem->Irp->AssociatedIrp.SystemBuffer; + /* Get the old unique ID */ + OldUniqueId = AllocatePool(UniqueIdChange->OldUniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); + if (!OldUniqueId) + { + RemoveWorkItem(WorkItem); + return; + } + + OldUniqueId->UniqueIdLength = UniqueIdChange->OldUniqueIdLength; + RtlCopyMemory(OldUniqueId->UniqueId, + (PVOID)((ULONG_PTR)UniqueIdChange + UniqueIdChange->OldUniqueIdOffset), + UniqueIdChange->OldUniqueIdLength); + + /* Get the new unique ID */ + NewUniqueId = AllocatePool(UniqueIdChange->NewUniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); + if (!NewUniqueId) + { + FreePool(OldUniqueId); + RemoveWorkItem(WorkItem); + return; + } + + NewUniqueId->UniqueIdLength = UniqueIdChange->NewUniqueIdLength; + RtlCopyMemory(NewUniqueId->UniqueId, + (PVOID)((ULONG_PTR)UniqueIdChange + UniqueIdChange->NewUniqueIdOffset), + UniqueIdChange->NewUniqueIdLength); + + /* Call the real worker */ + MountMgrUniqueIdChangeRoutine(WorkItem->DeviceExtension, OldUniqueId, NewUniqueId); + IssueUniqueIdChangeNotifyWorker(WorkItem, NewUniqueId); + + FreePool(NewUniqueId); + FreePool(OldUniqueId); + + return; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +UniqueIdChangeNotifyCompletion(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + PUNIQUE_ID_WORK_ITEM WorkItem = Context; + + /* Simply queue the work item */ + IoQueueWorkItem(WorkItem->WorkItem, + UniqueIdChangeNotifyWorker, + DelayedWorkQueue, + WorkItem); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +/* + * @implemented + */ +VOID +IssueUniqueIdChangeNotifyWorker(IN PUNIQUE_ID_WORK_ITEM WorkItem, + IN PMOUNTDEV_UNIQUE_ID UniqueId) +{ + PIRP Irp; + NTSTATUS Status; + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + PDEVICE_OBJECT DeviceObject; + + /* Get the device object */ + Status = IoGetDeviceObjectPointer(&(WorkItem->DeviceName), + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + RemoveWorkItem(WorkItem); + return; + } + + /* And then, the attached device */ + DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); + + /* Initialize the IRP */ + Irp = WorkItem->Irp; + IoInitializeIrp(Irp, IoSizeOfIrp(WorkItem->StackSize), WorkItem->StackSize); + + if (InterlockedExchange((PLONG)&(WorkItem->Event), 0) != 0) + { + ObfDereferenceObject(FileObject); + ObfDereferenceObject(DeviceObject); + RemoveWorkItem(WorkItem); + return; + } + + Irp->AssociatedIrp.SystemBuffer = WorkItem->IrpBuffer; + Irp->Tail.Overlay.Thread = PsGetCurrentThread(); + RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, UniqueId, UniqueId->UniqueIdLength + sizeof(USHORT)); + + Stack = IoGetNextIrpStackLocation(Irp); + + Stack->Parameters.DeviceIoControl.InputBufferLength = UniqueId->UniqueIdLength + sizeof(USHORT); + Stack->Parameters.DeviceIoControl.OutputBufferLength = WorkItem->IrpBufferLength; + Stack->Parameters.DeviceIoControl.Type3InputBuffer = 0; + Stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY; + Stack->MajorFunction = IRP_MJ_DEVICE_CONTROL; + + Status = IoSetCompletionRoutineEx(WorkItem->DeviceExtension->DeviceObject, + Irp, + UniqueIdChangeNotifyCompletion, + WorkItem, + TRUE, TRUE, TRUE); + if (!NT_SUCCESS(Status)) + { + ObfDereferenceObject(FileObject); + ObfDereferenceObject(DeviceObject); + RemoveWorkItem(WorkItem); + return; + } + + /* Call the driver */ + IofCallDriver(DeviceObject, Irp); + ObfDereferenceObject(FileObject); + ObfDereferenceObject(DeviceObject); +} + +/* + * @implemented + */ +VOID +IssueUniqueIdChangeNotify(IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING DeviceName, + IN PMOUNTDEV_UNIQUE_ID UniqueId) +{ + NTSTATUS Status; + PVOID IrpBuffer = NULL; + PFILE_OBJECT FileObject; + PDEVICE_OBJECT DeviceObject; + PUNIQUE_ID_WORK_ITEM WorkItem = NULL; + + /* Get the associated device object */ + Status = IoGetDeviceObjectPointer(DeviceName, + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return; + } + + /* And then, get attached device */ + DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); + + ObfDereferenceObject(FileObject); + + /* Allocate a work item */ + WorkItem = AllocatePool(sizeof(UNIQUE_ID_WORK_ITEM)); + if (!WorkItem) + { + ObfDereferenceObject(DeviceObject); + return; + } + + WorkItem->Event = NULL; + WorkItem->WorkItem = IoAllocateWorkItem(DeviceExtension->DeviceObject); + if (!WorkItem->WorkItem) + { + ObfDereferenceObject(DeviceObject); + goto Cleanup; + } + + WorkItem->DeviceExtension = DeviceExtension; + WorkItem->StackSize = DeviceObject->StackSize; + /* Already provide the IRP */ + WorkItem->Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + + ObfDereferenceObject(DeviceObject); + + if (!WorkItem->Irp) + { + goto Cleanup; + } + + /* Ensure it has enough space */ + IrpBuffer = AllocatePool(sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024); + if (!IrpBuffer) + { + goto Cleanup; + } + + WorkItem->DeviceName.Length = DeviceName->Length; + WorkItem->DeviceName.MaximumLength = DeviceName->Length + sizeof(WCHAR); + WorkItem->DeviceName.Buffer = AllocatePool(WorkItem->DeviceName.MaximumLength); + if (!WorkItem->DeviceName.Buffer) + { + goto Cleanup; + } + + RtlCopyMemory(WorkItem->DeviceName.Buffer, DeviceName->Buffer, DeviceName->Length); + WorkItem->DeviceName.Buffer[DeviceName->Length / sizeof(WCHAR)] = UNICODE_NULL; + + WorkItem->IrpBuffer = IrpBuffer; + WorkItem->IrpBufferLength = sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024; + + /* Add the worker in the list */ + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); + InsertHeadList(&(DeviceExtension->UniqueIdWorkerItemListHead), &(WorkItem->UniqueIdWorkerItemListEntry)); + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + + /* And call the worker */ + IssueUniqueIdChangeNotifyWorker(WorkItem, UniqueId); + + return; + +Cleanup: + if (IrpBuffer) + { + FreePool(IrpBuffer); + } + + if (WorkItem->Irp) + { + IoFreeIrp(WorkItem->Irp); + } + + if (WorkItem->WorkItem) + { + IoFreeWorkItem(WorkItem->WorkItem); + } + + if (WorkItem) + { + FreePool(WorkItem); + } +} diff --git a/reactos/drivers/filters/mountmgr/point.c b/reactos/drivers/filters/mountmgr/point.c new file mode 100644 index 00000000000..5f8d33f2bc3 --- /dev/null +++ b/reactos/drivers/filters/mountmgr/point.c @@ -0,0 +1,572 @@ +/* + * ReactOS kernel + * Copyright (C) 2011-2012 ReactOS Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/filesystem/mountmgr/point.c + * PURPOSE: Mount Manager - Mount points + * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) + */ + +/* INCLUDES *****************************************************************/ + +#include "mntmgr.h" + +#define NDEBUG +#include + +/* + * @implemented + */ +NTSTATUS +MountMgrCreatePointWorker(IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicLinkName, + IN PUNICODE_STRING DeviceName) +{ + NTSTATUS Status; + PLIST_ENTRY DeviceEntry; + PMOUNTDEV_UNIQUE_ID UniqueId; + PSYMLINK_INFORMATION SymlinkInformation; + UNICODE_STRING SymLink, TargetDeviceName; + PDEVICE_INFORMATION DeviceInformation, DeviceInfo; + + /* Get device name */ + Status = QueryDeviceInformation(SymbolicLinkName, + &TargetDeviceName, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* First of all, try to find device */ + for (DeviceEntry = DeviceExtension->DeviceListHead.Flink; + DeviceEntry != &(DeviceExtension->DeviceListHead); + DeviceEntry = DeviceEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry); + + if (RtlCompareUnicodeString(&TargetDeviceName, &(DeviceInformation->DeviceName), TRUE) == 0) + { + break; + } + } + + /* Copy symbolic link name and null terminate it */ + SymLink.Buffer = AllocatePool(SymbolicLinkName->Length + sizeof(UNICODE_NULL)); + if (!SymLink.Buffer) + { + FreePool(TargetDeviceName.Buffer); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(SymLink.Buffer, SymbolicLinkName->Buffer, SymbolicLinkName->Length); + SymLink.Buffer[SymbolicLinkName->Length / sizeof(WCHAR)] = UNICODE_NULL; + SymLink.Length = SymbolicLinkName->Length; + SymLink.MaximumLength = SymbolicLinkName->Length + sizeof(UNICODE_NULL); + + /* If we didn't find device */ + if (DeviceEntry == &(DeviceExtension->DeviceListHead)) + { + /* Then, try with unique ID */ + Status = QueryDeviceInformation(SymbolicLinkName, + NULL, &UniqueId, + NULL, NULL, NULL, + NULL, NULL); + if (!NT_SUCCESS(Status)) + { + FreePool(TargetDeviceName.Buffer); + FreePool(SymLink.Buffer); + return Status; + } + + /* Create a link to the device */ + Status = GlobalCreateSymbolicLink(&SymLink, &TargetDeviceName); + if (!NT_SUCCESS(Status)) + { + FreePool(UniqueId); + FreePool(TargetDeviceName.Buffer); + FreePool(SymLink.Buffer); + return Status; + } + + /* If caller provided driver letter, delete it */ + if (IsDriveLetter(&SymLink)) + { + DeleteRegistryDriveLetter(UniqueId); + } + + /* Device will be identified with its unique ID */ + Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + SymLink.Buffer, + REG_BINARY, + UniqueId->UniqueId, + UniqueId->UniqueIdLength); + + FreePool(UniqueId); + FreePool(TargetDeviceName.Buffer); + FreePool(SymLink.Buffer); + return Status; + } + + /* If call provided a driver letter whereas device already has one + * fail, this is not doable + */ + if (IsDriveLetter(&SymLink) && HasDriveLetter(DeviceInformation)) + { + FreePool(TargetDeviceName.Buffer); + FreePool(SymLink.Buffer); + return STATUS_INVALID_PARAMETER; + } + + /* Now, create a link */ + Status = GlobalCreateSymbolicLink(&SymLink, &TargetDeviceName); + FreePool(TargetDeviceName.Buffer); + if (!NT_SUCCESS(Status)) + { + FreePool(SymLink.Buffer); + return Status; + } + + /* Associate Unique ID <-> symbolic name */ + UniqueId = DeviceInformation->UniqueId; + Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + SymLink.Buffer, + REG_BINARY, + UniqueId->UniqueId, + UniqueId->UniqueIdLength); + if (!NT_SUCCESS(Status)) + { + GlobalDeleteSymbolicLink(&SymLink); + FreePool(SymLink.Buffer); + return Status; + } + + /* Now, prepare to save the link with the device */ + SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION)); + if (!SymlinkInformation) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + GlobalDeleteSymbolicLink(&SymLink); + FreePool(SymLink.Buffer); + return Status; + } + + SymlinkInformation->Name.Length = SymLink.Length; + SymlinkInformation->Name.MaximumLength = SymLink.Length + sizeof(UNICODE_NULL); + SymlinkInformation->Name.Buffer = AllocatePool(SymlinkInformation->Name.MaximumLength); + if (!SymlinkInformation->Name.Buffer) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + FreePool(SymlinkInformation); + GlobalDeleteSymbolicLink(&SymLink); + FreePool(SymLink.Buffer); + return Status; + } + + /* Save the link and mark it online */ + RtlCopyMemory(SymlinkInformation->Name.Buffer, SymLink.Buffer, SymlinkInformation->Name.Length); + SymlinkInformation->Name.Buffer[SymlinkInformation->Name.Length / sizeof(WCHAR)] = UNICODE_NULL; + SymlinkInformation->Online = TRUE; + InsertTailList(&DeviceInformation->SymbolicLinksListHead, &SymlinkInformation->SymbolicLinksListEntry); + SendLinkCreated(&(SymlinkInformation->Name)); + + /* If we have a drive letter */ + if (IsDriveLetter(&SymLink)) + { + /* Then, delete the no drive letter entry */ + DeleteNoDriveLetterEntry(UniqueId); + + /* And post online notification if asked */ + if (!DeviceInformation->SkipNotifications) + { + PostOnlineNotification(DeviceExtension, &DeviceInformation->SymbolicName); + } + } + + /* If that's a volume with automatic drive letter, it's now time to resync databases */ + if (MOUNTMGR_IS_VOLUME_NAME(&SymLink) && DeviceExtension->AutomaticDriveLetter) + { + for (DeviceEntry = DeviceExtension->DeviceListHead.Flink; + DeviceEntry != &(DeviceExtension->DeviceListHead); + DeviceEntry = DeviceEntry->Flink) + { + DeviceInfo = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry); + + /* If there's one, ofc! */ + if (!DeviceInfo->NoDatabase) + { + ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInfo); + } + } + } + + /* Notify & quit */ + FreePool(SymLink.Buffer); + MountMgrNotify(DeviceExtension); + + if (!DeviceInformation->Volume) + { + MountMgrNotifyNameChange(DeviceExtension, DeviceName, FALSE); + } + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +QueryPointsFromMemory(IN PDEVICE_EXTENSION DeviceExtension, + IN PIRP Irp, + IN PMOUNTDEV_UNIQUE_ID UniqueId OPTIONAL, + IN PUNICODE_STRING SymbolicName OPTIONAL) +{ + NTSTATUS Status; + PIO_STACK_LOCATION Stack; + UNICODE_STRING DeviceName; + ULONG TotalSize, TotalSymLinks; + PMOUNTMGR_MOUNT_POINTS MountPoints; + PDEVICE_INFORMATION DeviceInformation; + PLIST_ENTRY DeviceEntry, SymlinksEntry; + PSYMLINK_INFORMATION SymlinkInformation; + + /* If we got a symbolic link, query device */ + if (SymbolicName) + { + Status = QueryDeviceInformation(SymbolicName, + &DeviceName, + NULL, NULL, + NULL, NULL, + NULL, NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + } + + /* Browse all the links to count number of links & size used */ + TotalSize = 0; + TotalSymLinks = 0; + for (DeviceEntry = DeviceExtension->DeviceListHead.Flink; + DeviceEntry != &(DeviceExtension->DeviceListHead); + DeviceEntry = DeviceEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry); + + /* If we were given an unique ID, it has to match */ + if (UniqueId) + { + if (UniqueId->UniqueIdLength != DeviceInformation->UniqueId->UniqueIdLength) + { + continue; + } + + if (RtlCompareMemory(UniqueId->UniqueId, + DeviceInformation->UniqueId->UniqueId, + UniqueId->UniqueIdLength) != UniqueId->UniqueIdLength) + { + continue; + } + } + /* Or, if we had a symlink, it has to match */ + else if (SymbolicName) + { + if (!RtlEqualUnicodeString(&DeviceName, &(DeviceInformation->DeviceName), TRUE)) + { + continue; + } + } + + /* Once here, it matched, save device name & unique ID size */ + TotalSize += DeviceInformation->DeviceName.Length + DeviceInformation->UniqueId->UniqueIdLength; + + /* And count number of symlinks (and their size) */ + for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink; + SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead); + SymlinksEntry = SymlinksEntry->Flink) + { + SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + TotalSize += SymlinkInformation->Name.Length; + TotalSymLinks++; + } + + /* We had a specific item to find + * if we reach that point, we found it, no need to continue + */ + if (UniqueId || SymbolicName) + { + break; + } + } + + /* If we were looking for specific item, ensure we found it */ + if (UniqueId || SymbolicName) + { + if (DeviceEntry == &(DeviceExtension->DeviceListHead)) + { + if (DeviceName.Buffer) + { + FreePool(DeviceName.Buffer); + } + + return STATUS_INVALID_PARAMETER; + } + } + + /* Now, ensure output buffer can hold everything */ + Stack = IoGetNextIrpStackLocation(Irp); + MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer; + + /* Ensure we set output to let user reallocate! */ + MountPoints->Size = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalSize; + MountPoints->NumberOfMountPoints = TotalSymLinks; + + if (MountPoints->Size > Stack->Parameters.DeviceIoControl.OutputBufferLength) + { + return STATUS_BUFFER_OVERFLOW; + } + + /* Now, start putting mount points */ + TotalSymLinks = 0; + TotalSize = 0; + for (DeviceEntry = DeviceExtension->DeviceListHead.Flink; + DeviceEntry != &(DeviceExtension->DeviceListHead); + DeviceEntry = DeviceEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry); + + /* Find back correct mount point */ + if (UniqueId) + { + if (!UniqueId->UniqueIdLength != DeviceInformation->UniqueId->UniqueIdLength) + { + continue; + } + + if (RtlCompareMemory(UniqueId->UniqueId, + DeviceInformation->UniqueId->UniqueId, + UniqueId->UniqueIdLength) != UniqueId->UniqueIdLength) + { + continue; + } + } + else if (SymbolicName) + { + if (!RtlEqualUnicodeString(&DeviceName, &(DeviceInformation->DeviceName), TRUE)) + { + continue; + } + } + + /* Now we've got it, but all the data */ + for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink; + SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead); + SymlinksEntry = SymlinksEntry->Flink) + { + SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + + MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS) + + TotalSize; + MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameLength = SymlinkInformation->Name.Length; + MountPoints->MountPoints[TotalSymLinks].UniqueIdOffset = sizeof(MOUNTMGR_MOUNT_POINTS) + + SymlinkInformation->Name.Length + + TotalSize; + MountPoints->MountPoints[TotalSymLinks].UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength; + MountPoints->MountPoints[TotalSymLinks].DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS) + + SymlinkInformation->Name.Length + + DeviceInformation->UniqueId->UniqueIdLength + + TotalSize; + MountPoints->MountPoints[TotalSymLinks].DeviceNameLength = DeviceInformation->DeviceName.Length; + + RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameOffset), + SymlinkInformation->Name.Buffer, SymlinkInformation->Name.Length); + RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[TotalSymLinks].UniqueIdOffset), + DeviceInformation->UniqueId->UniqueId, DeviceInformation->UniqueId->UniqueIdLength); + RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[TotalSymLinks].DeviceNameOffset), + DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length); + + /* Update counters */ + TotalSymLinks++; + TotalSize += SymlinkInformation->Name.Length + DeviceInformation->UniqueId->UniqueIdLength + + DeviceInformation->DeviceName.Length; + } + + if (UniqueId || SymbolicName) + { + break; + } + } + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +NTSTATUS +QueryPointsFromSymbolicLinkName(IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicName, + IN PIRP Irp) +{ + NTSTATUS Status; + ULONG TotalLength; + PIO_STACK_LOCATION Stack; + UNICODE_STRING DeviceName; + PMOUNTMGR_MOUNT_POINTS MountPoints; + PDEVICE_INFORMATION DeviceInformation; + PLIST_ENTRY DeviceEntry, SymlinksEntry; + PSYMLINK_INFORMATION SymlinkInformation; + + /* Find device */ + Status = QueryDeviceInformation(SymbolicName, &DeviceName, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (NT_SUCCESS(Status)) + { + /* Look for the device information */ + for (DeviceEntry = DeviceExtension->DeviceListHead.Flink; + DeviceEntry != &(DeviceExtension->DeviceListHead); + DeviceEntry = DeviceEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry); + + if (RtlEqualUnicodeString(&DeviceName, &(DeviceInformation->DeviceName), TRUE) == 0) + { + break; + } + } + + FreePool(DeviceName.Buffer); + + if (DeviceEntry == &(DeviceExtension->DeviceListHead)) + { + return STATUS_INVALID_PARAMETER; + } + + /* Check for the link */ + for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink; + SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead); + SymlinksEntry = DeviceEntry->Flink) + { + SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + if (RtlEqualUnicodeString(SymbolicName, &SymlinkInformation->Name, TRUE) == 0) + { + break; + } + } + + if (SymlinksEntry == &(DeviceInformation->SymbolicLinksListHead)) + { + return STATUS_INVALID_PARAMETER; + } + } + else + { + /* Browse all the devices to try to find the one + * that has the given link... + */ + for (DeviceEntry = DeviceExtension->DeviceListHead.Flink; + DeviceEntry != &(DeviceExtension->DeviceListHead); + DeviceEntry = DeviceEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry); + + for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink; + SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead); + SymlinksEntry = SymlinksEntry->Flink) + { + SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + if (RtlEqualUnicodeString(SymbolicName, &SymlinkInformation->Name, TRUE) == 0) + { + break; + } + } + + if (SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead)) + { + break; + } + } + + /* Even that way we didn't find, give up! */ + if (DeviceEntry == &(DeviceExtension->DeviceListHead)) + { + return STATUS_OBJECT_NAME_NOT_FOUND; + } + } + + /* Get output buffer */ + Stack = IoGetNextIrpStackLocation(Irp); + MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer; + + /* Compute output length */ + TotalLength = DeviceInformation->UniqueId->UniqueIdLength + + SymlinkInformation->Name.Length + DeviceInformation->DeviceName.Length; + + /* Give length to allow reallocation */ + MountPoints->Size = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalLength; + MountPoints->NumberOfMountPoints = 1; + + if (MountPoints->Size > Stack->Parameters.DeviceIoControl.OutputBufferLength) + { + return STATUS_BUFFER_OVERFLOW; + } + + /* Write out data */ + MountPoints->MountPoints[0].SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS); + MountPoints->MountPoints[0].SymbolicLinkNameLength = SymlinkInformation->Name.Length; + /* If link is online write it's unique ID, otherwise, forget about it */ + if (SymlinkInformation->Online) + { + MountPoints->MountPoints[0].UniqueIdOffset = sizeof(MOUNTMGR_MOUNT_POINTS) + + SymlinkInformation->Name.Length; + MountPoints->MountPoints[0].UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength; + } + else + { + MountPoints->MountPoints[0].UniqueIdOffset = 0; + MountPoints->MountPoints[0].UniqueIdLength = 0; + } + + MountPoints->MountPoints[0].DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS) + + SymlinkInformation->Name.Length + + DeviceInformation->UniqueId->UniqueIdLength; + MountPoints->MountPoints[0].DeviceNameLength = DeviceInformation->DeviceName.Length; + + RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].SymbolicLinkNameOffset), + SymlinkInformation->Name.Buffer, SymlinkInformation->Name.Length); + + if (SymlinkInformation->Online) + { + RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].UniqueIdOffset), + DeviceInformation->UniqueId->UniqueId, DeviceInformation->UniqueId->UniqueIdLength); + } + + RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].DeviceNameOffset), + DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length); + + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/filters/mountmgr/symlink.c b/reactos/drivers/filters/mountmgr/symlink.c new file mode 100644 index 00000000000..68a3980a648 --- /dev/null +++ b/reactos/drivers/filters/mountmgr/symlink.c @@ -0,0 +1,1004 @@ +/* + * ReactOS kernel + * Copyright (C) 2011-2012 ReactOS Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/filesystem/mountmgr/symlink.c + * PURPOSE: Mount Manager - Symbolic links functions + * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) + */ + +/* INCLUDES *****************************************************************/ + +#include "mntmgr.h" + +#define NDEBUG +#include + +UNICODE_STRING DeviceMount = RTL_CONSTANT_STRING(MOUNTMGR_DEVICE_NAME); +UNICODE_STRING DosDevicesMount = RTL_CONSTANT_STRING(L"\\DosDevices\\MountPointManager"); +UNICODE_STRING DosDevices = RTL_CONSTANT_STRING(L"\\DosDevices\\"); +UNICODE_STRING DeviceFloppy = RTL_CONSTANT_STRING(L"\\Device\\Floppy"); +UNICODE_STRING DeviceCdRom = RTL_CONSTANT_STRING(L"\\Device\\CdRom"); +UNICODE_STRING DosGlobal = RTL_CONSTANT_STRING(L"\\GLOBAL??\\"); +UNICODE_STRING Global = RTL_CONSTANT_STRING(L"\\??\\"); +UNICODE_STRING SafeVolumes = RTL_CONSTANT_STRING(L"\\Device\\VolumesSafeForWriteAccess"); +UNICODE_STRING Volume = RTL_CONSTANT_STRING(L"\\??\\Volume"); +UNICODE_STRING ReparseIndex = RTL_CONSTANT_STRING(L"\\$Extend\\$Reparse:$R:$INDEX_ALLOCATION"); + +/* + * @implemented + */ +NTSTATUS +CreateStringWithGlobal(IN PUNICODE_STRING DosName, + OUT PUNICODE_STRING GlobalString) +{ + UNICODE_STRING IntGlobal; + + if (RtlPrefixUnicodeString(&DosDevices, DosName, TRUE)) + { + /* DOS device - use DOS global */ + IntGlobal.Length = DosName->Length - DosDevices.Length + DosGlobal.Length; + IntGlobal.MaximumLength = IntGlobal.Length + sizeof(WCHAR); + IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength); + if (!IntGlobal.Buffer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(IntGlobal.Buffer, DosGlobal.Buffer, DosGlobal.Length); + RtlCopyMemory(IntGlobal.Buffer + (DosGlobal.Length / sizeof(WCHAR)), + DosName->Buffer + (DosDevices.Length / sizeof(WCHAR)), + DosName->Length - DosDevices.Length); + IntGlobal.Buffer[IntGlobal.Length / sizeof(WCHAR)] = UNICODE_NULL; + } + else + { + if (RtlPrefixUnicodeString(&Global, DosName, TRUE)) + { + /* Switch to DOS global */ + IntGlobal.Length = DosName->Length - Global.Length + DosGlobal.Length; + IntGlobal.MaximumLength = IntGlobal.Length + sizeof(WCHAR); + IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength); + if (!IntGlobal.Buffer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(IntGlobal.Buffer, DosGlobal.Buffer, DosGlobal.Length); + RtlCopyMemory(IntGlobal.Buffer + (DosGlobal.Length / sizeof(WCHAR)), + DosName->Buffer + (Global.Length / sizeof(WCHAR)), + DosName->Length - Global.Length); + IntGlobal.Buffer[IntGlobal.Length / sizeof(WCHAR)] = UNICODE_NULL; + } + else + { + /* Simply duplicate string */ + IntGlobal.Length = DosName->Length; + IntGlobal.MaximumLength = DosName->MaximumLength; + IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength); + if (!IntGlobal.Buffer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(IntGlobal.Buffer, DosName->Buffer, IntGlobal.MaximumLength); + } + } + + /* Return string */ + GlobalString->Length = IntGlobal.Length; + GlobalString->MaximumLength = IntGlobal.MaximumLength; + GlobalString->Buffer = IntGlobal.Buffer; + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +NTSTATUS +GlobalCreateSymbolicLink(IN PUNICODE_STRING DosName, + IN PUNICODE_STRING DeviceName) +{ + NTSTATUS Status; + UNICODE_STRING GlobalName; + + /* First create the global string */ + Status = CreateStringWithGlobal(DosName, &GlobalName); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Then, create the symlink */ + Status = IoCreateSymbolicLink(&GlobalName, DosName); + + FreePool(GlobalName.Buffer); + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName) +{ + NTSTATUS Status; + UNICODE_STRING GlobalName; + + /* Recreate the string (to find the link) */ + Status = CreateStringWithGlobal(DosName, &GlobalName); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* And delete the link */ + Status = IoDeleteSymbolicLink(&GlobalName); + + FreePool(GlobalName.Buffer); + + return Status; +} + +/* + * @implemented + */ +VOID +SendLinkCreated(IN PUNICODE_STRING SymbolicName) +{ + PIRP Irp; + KEVENT Event; + ULONG NameSize; + NTSTATUS Status; + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + PMOUNTDEV_NAME Name = NULL; + PDEVICE_OBJECT DeviceObject; + IO_STATUS_BLOCK IoStatusBlock; + + /* Get the device associated with the name */ + Status = IoGetDeviceObjectPointer(SymbolicName, + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return; + } + + /* Get attached device (will notify it) */ + DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); + + /* NameSize is the size of the whole MOUNTDEV_NAME struct */ + NameSize = sizeof(USHORT) + SymbolicName->Length; + Name = AllocatePool(NameSize); + if (!Name) + { + goto Cleanup; + } + + /* Initialize struct */ + Name->NameLength = SymbolicName->Length; + RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length); + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + /* Microsoft does it twice... Once with limited access, second with any + * So, first one here + */ + Irp = IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE, 4, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS), + DeviceObject, + Name, + NameSize, + NULL, + 0, + FALSE, + &Event, + &IoStatusBlock); + /* This one can fail, no one matters */ + if (Irp) + { + Stack = IoGetNextIrpStackLocation(Irp); + Stack->FileObject = FileObject; + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + } + + /* Then, second one */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_CREATED, + DeviceObject, + Name, + NameSize, + NULL, + 0, + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + goto Cleanup; + } + + Stack = IoGetNextIrpStackLocation(Irp); + Stack->FileObject = FileObject; + + /* Really notify */ + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + +Cleanup: + if (Name) + { + FreePool(Name); + } + + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + + return; +} + +/* + * @implemented + */ +VOID +SendLinkDeleted(IN PUNICODE_STRING DeviceName, + IN PUNICODE_STRING SymbolicName) +{ + PIRP Irp; + KEVENT Event; + ULONG NameSize; + NTSTATUS Status; + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION Stack; + PMOUNTDEV_NAME Name = NULL; + PDEVICE_OBJECT DeviceObject; + IO_STATUS_BLOCK IoStatusBlock; + + /* Get the device associated with the name */ + Status = IoGetDeviceObjectPointer(DeviceName, + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return; + } + + /* Get attached device (will notify it) */ + DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); + + /* NameSize is the size of the whole MOUNTDEV_NAME struct */ + NameSize = sizeof(USHORT) + SymbolicName->Length; + Name = AllocatePool(NameSize); + if (!Name) + { + goto Cleanup; + } + + /* Initialize struct */ + Name->NameLength = SymbolicName->Length; + RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length); + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + /* Cf: SendLinkCreated comment */ + Irp = IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE, 5, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS), + DeviceObject, + Name, + NameSize, + NULL, + 0, + FALSE, + &Event, + &IoStatusBlock); + /* This one can fail, no one matters */ + if (Irp) + { + Stack = IoGetNextIrpStackLocation(Irp); + Stack->FileObject = FileObject; + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + } + + /* Then, second one */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_DELETED, + DeviceObject, + Name, + NameSize, + NULL, + 0, + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + goto Cleanup; + } + + Stack = IoGetNextIrpStackLocation(Irp); + Stack->FileObject = FileObject; + + /* Really notify */ + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + +Cleanup: + if (Name) + { + FreePool(Name); + } + + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + + return; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext) +{ + UNICODE_STRING ValueNameString; + PMOUNTDEV_UNIQUE_ID UniqueId = Context; + + if (ValueName[0] != L'#' || ValueType != REG_BINARY || + (UniqueId->UniqueIdLength != ValueLength)) + { + return STATUS_SUCCESS; + } + + if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) + { + return STATUS_SUCCESS; + } + + /* That one matched, increase count */ + RtlInitUnicodeString(&ValueNameString, ValueName); + if (ValueNameString.Length) + { + (*((PULONG)EntryContext))++; + } + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext) +{ + UNICODE_STRING ValueNameString; + PMOUNTDEV_UNIQUE_ID UniqueId = Context; + /* Unicode strings table */ + PUNICODE_STRING ReturnString = EntryContext; + + if (ValueName[0] != L'#' || ValueType != REG_BINARY || + (UniqueId->UniqueIdLength != ValueLength)) + { + return STATUS_SUCCESS; + } + + if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength) + { + return STATUS_SUCCESS; + } + + /* Unique ID matches, let's put the symlink */ + RtlInitUnicodeString(&ValueNameString, ValueName); + if (!ValueNameString.Length) + { + return STATUS_SUCCESS; + } + + /* Allocate string to copy */ + ValueNameString.Buffer = AllocatePool(ValueNameString.MaximumLength); + if (!ValueNameString.Buffer) + { + return STATUS_SUCCESS; + } + + /* Copy */ + RtlCopyMemory(ValueNameString.Buffer, ValueName, ValueNameString.Length); + ValueNameString.Buffer[ValueNameString.Length / sizeof(WCHAR)] = UNICODE_NULL; + + while (ReturnString->Length) + { + ReturnString++; + } + + /* And return that string */ + *ReturnString = ValueNameString; + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +NTSTATUS +CreateNewVolumeName(OUT PUNICODE_STRING VolumeName, + IN PGUID VolumeGuid OPTIONAL) +{ + GUID Guid; + NTSTATUS Status; + UNICODE_STRING GuidString; + + /* If no GUID was provided, then create one */ + if (!VolumeGuid) + { + Status = ExUuidCreate(&Guid); + if (!NT_SUCCESS(Status)) + { + return Status; + } + } + else + { + RtlCopyMemory(&Guid, VolumeGuid, sizeof(GUID)); + } + + /* Convert GUID to string */ + Status = RtlStringFromGUID(&Guid, &GuidString); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Size for volume namespace, litteral GUID, and null char */ + VolumeName->MaximumLength = 0x14 + 0x4C + sizeof(UNICODE_NULL); + VolumeName->Buffer = AllocatePool(0x14 + 0x4C + sizeof(UNICODE_NULL)); + if (!VolumeName->Buffer) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + RtlCopyUnicodeString(VolumeName, &Volume); + RtlAppendUnicodeStringToString(VolumeName, &GuidString); + VolumeName->Buffer[VolumeName->Length / sizeof(WCHAR)] = UNICODE_NULL; + Status = STATUS_SUCCESS; + } + + ExFreePoolWithTag(GuidString.Buffer, 0); + + return Status; +} + +/* + * @implemented + */ +NTSTATUS +QuerySymbolicLinkNamesFromStorage(IN PDEVICE_EXTENSION DeviceExtension, + IN PDEVICE_INFORMATION DeviceInformation, + IN PUNICODE_STRING SuggestedLinkName, + IN BOOLEAN UseOnlyIfThereAreNoOtherLinks, + OUT PUNICODE_STRING * SymLinks, + OUT PULONG SymLinkCount, + IN BOOLEAN HasGuid, + IN LPGUID Guid) +{ + NTSTATUS Status; + BOOLEAN WriteNew; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + + /* First of all, count links */ + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount; + QueryTable[0].EntryContext = SymLinkCount; + *SymLinkCount = 0; + + Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + QueryTable, + DeviceInformation->UniqueId, + NULL); + if (!NT_SUCCESS(Status)) + { + *SymLinkCount = 0; + } + + /* Check if we have to write a new one first */ + if (SuggestedLinkName && !IsDriveLetter(SuggestedLinkName) && + UseOnlyIfThereAreNoOtherLinks && *SymLinkCount == 0) + { + WriteNew = TRUE; + } + else + { + WriteNew = FALSE; + } + + /* If has GUID, it makes one more link */ + if (HasGuid) + { + (*SymLinkCount)++; + } + + if (WriteNew) + { + /* Write link */ + RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + SuggestedLinkName->Buffer, + REG_BINARY, + DeviceInformation->UniqueId->UniqueId, + DeviceInformation->UniqueId->UniqueIdLength); + + /* And recount all the needed links */ + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount; + QueryTable[0].EntryContext = SymLinkCount; + *SymLinkCount = 0; + + Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + QueryTable, + DeviceInformation->UniqueId, + NULL); + if (!NT_SUCCESS(Status)) + { + return STATUS_NOT_FOUND; + } + } + + /* Not links found? */ + if (!*SymLinkCount) + { + return STATUS_NOT_FOUND; + } + + /* Allocate a buffer big enough to hold symlinks (table of unicode strings) */ + *SymLinks = AllocatePool(*SymLinkCount * sizeof(UNICODE_STRING)); + if (!*SymLinks) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Prepare to query links */ + RtlZeroMemory(*SymLinks, *SymLinkCount * sizeof(UNICODE_STRING)); + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdQuery; + + /* No GUID? Keep it that way */ + if (!HasGuid) + { + QueryTable[0].EntryContext = *SymLinks; + } + /* Otherwise, first create volume name */ + else + { + Status = CreateNewVolumeName(SymLinks[0], Guid); + if (!NT_SUCCESS(Status)) + { + FreePool(*SymLinks); + return Status; + } + + /* Skip first link (ours) */ + QueryTable[0].EntryContext = *SymLinks + 1; + } + + /* Now, query */ + RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + QueryTable, + DeviceInformation->UniqueId, + NULL); + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +PSAVED_LINK_INFORMATION +RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension, + IN PMOUNTDEV_UNIQUE_ID UniqueId) +{ + PLIST_ENTRY NextEntry; + PSAVED_LINK_INFORMATION SavedLinkInformation; + + /* No saved links? Easy! */ + if (IsListEmpty(&(DeviceExtension->SavedLinksListHead))) + { + return NULL; + } + + /* Now, browse saved links */ + for (NextEntry = DeviceExtension->SavedLinksListHead.Flink; + NextEntry != &(DeviceExtension->SavedLinksListHead); + NextEntry = NextEntry->Flink) + { + SavedLinkInformation = CONTAINING_RECORD(NextEntry, + SAVED_LINK_INFORMATION, + SavedLinksListEntry); + + /* Find the one that matches */ + if (SavedLinkInformation->UniqueId->UniqueIdLength == UniqueId->UniqueIdLength) + { + if (RtlCompareMemory(SavedLinkInformation->UniqueId->UniqueId, + UniqueId->UniqueId, + UniqueId->UniqueIdLength) == + UniqueId->UniqueIdLength) + { + /* Remove it and return it */ + RemoveEntryList(&(SavedLinkInformation->SavedLinksListEntry)); + return SavedLinkInformation; + } + } + } + + /* None found (none removed) */ + return NULL; +} + +/* + * @implemented + */ +NTSTATUS +QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName, + OUT PUNICODE_STRING SuggestedLinkName, + OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks) +{ + PIRP Irp; + KEVENT Event; + NTSTATUS Status; + USHORT NameLength; + PFILE_OBJECT FileObject; + PDEVICE_OBJECT DeviceObject; + IO_STATUS_BLOCK IoStatusBlock; + PIO_STACK_LOCATION IoStackLocation; + PMOUNTDEV_SUGGESTED_LINK_NAME IoCtlSuggested; + + /* First, get device */ + Status = IoGetDeviceObjectPointer(SymbolicName, + FILE_READ_ATTRIBUTES, + &FileObject, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Then, get attached device */ + DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject); + + /* Then, prepare buffer to query suggested name */ + IoCtlSuggested = AllocatePool(sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)); + if (!IoCtlSuggested) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Dereference; + } + + /* Prepare request */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME, + DeviceObject, + NULL, + 0, + IoCtlSuggested, + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME), + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Release; + } + + IoStackLocation = IoGetNextIrpStackLocation(Irp); + IoStackLocation->FileObject = FileObject; + + /* And ask */ + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, + FALSE, NULL); + Status = IoStatusBlock.Status; + } + + /* Overflow? Normal */ + if (Status == STATUS_BUFFER_OVERFLOW) + { + /* Reallocate big enough buffer */ + NameLength = IoCtlSuggested->NameLength + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME); + FreePool(IoCtlSuggested); + + IoCtlSuggested = AllocatePool(NameLength); + if (!IoCtlSuggested) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Dereference; + } + + /* And reask */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME, + DeviceObject, + NULL, + 0, + IoCtlSuggested, + NameLength, + FALSE, + &Event, + &IoStatusBlock); + if (!Irp) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Release; + } + + IoStackLocation = IoGetNextIrpStackLocation(Irp); + IoStackLocation->FileObject = FileObject; + + Status = IofCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, + FALSE, NULL); + Status = IoStatusBlock.Status; + } + } + + if (!NT_SUCCESS(Status)) + { + goto Release; + } + + /* Now we have suggested name, copy it */ + SuggestedLinkName->Length = IoCtlSuggested->NameLength; + SuggestedLinkName->MaximumLength = IoCtlSuggested->NameLength + sizeof(UNICODE_NULL); + SuggestedLinkName->Buffer = AllocatePool(IoCtlSuggested->NameLength + sizeof(UNICODE_NULL)); + if (!SuggestedLinkName->Buffer) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + RtlCopyMemory(SuggestedLinkName->Buffer, IoCtlSuggested->Name, IoCtlSuggested->NameLength); + SuggestedLinkName->Buffer[SuggestedLinkName->Length / sizeof(WCHAR)] = UNICODE_NULL; + } + + /* Also return its priority */ + *UseOnlyIfThereAreNoOtherLinks = IoCtlSuggested->UseOnlyIfThereAreNoOtherLinks; + +Release: + FreePool(IoCtlSuggested); + +Dereference: + ObfDereferenceObject(DeviceObject); + ObfDereferenceObject(FileObject); + + return Status; +} + +/* + * @implemented + */ +BOOLEAN +RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation, + IN PUNICODE_STRING DosName, + IN PUNICODE_STRING NewLink) +{ + PLIST_ENTRY NextEntry; + PSYMLINK_INFORMATION SymlinkInformation; + + /* Find the link */ + for (NextEntry = SavedLinkInformation->SymbolicLinksListHead.Flink; + NextEntry != &(SavedLinkInformation->SymbolicLinksListHead); + NextEntry = NextEntry->Flink) + { + SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + if (!RtlEqualUnicodeString(DosName, &(SymlinkInformation->Name), TRUE)) + { + /* Delete old link */ + GlobalDeleteSymbolicLink(DosName); + /* Set its new location */ + GlobalCreateSymbolicLink(DosName, NewLink); + + /* And remove it from the list (not valid any more) */ + RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry)); + FreePool(SymlinkInformation->Name.Buffer); + FreePool(SymlinkInformation); + + return TRUE; + } + } + + return FALSE; +} + +/* + * @implemented + */ +VOID +DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension, + IN PUNICODE_STRING SymbolicLink, + IN BOOLEAN MarkOffline) +{ + PLIST_ENTRY DeviceEntry, SymbolEntry; + PDEVICE_INFORMATION DeviceInformation; + PSYMLINK_INFORMATION SymlinkInformation; + + /* First of all, ensure we have devices */ + if (IsListEmpty(&(DeviceExtension->DeviceListHead))) + { + return; + } + + /* Then, look for the symbolic name */ + for (DeviceEntry = DeviceExtension->DeviceListHead.Flink; + DeviceEntry != &(DeviceExtension->DeviceListHead); + DeviceEntry = DeviceEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry); + + for (SymbolEntry = DeviceInformation->SymbolicLinksListHead.Flink; + SymbolEntry != &(DeviceInformation->SymbolicLinksListHead); + SymbolEntry = SymbolEntry->Flink) + { + SymlinkInformation = CONTAINING_RECORD(SymbolEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); + + /* One we have found it */ + if (RtlCompareUnicodeString(SymbolicLink, &(SymlinkInformation->Name), TRUE) == 0) + { + /* Check if caller just want it to be offline */ + if (MarkOffline) + { + SymlinkInformation->Online = FALSE; + } + else + { + /* If not, delete it & notify */ + SendLinkDeleted(&(DeviceInformation->SymbolicName), SymbolicLink); + RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry)); + + FreePool(SymlinkInformation->Name.Buffer); + FreePool(SymlinkInformation); + } + + /* No need to go farther */ + return; + } + } + } + + return; +} + +/* + * @implemented + */ +BOOLEAN +IsDriveLetter(PUNICODE_STRING SymbolicName) +{ + WCHAR Letter; + BOOLEAN Result = FALSE; + + /* We must have a precise length */ + if (SymbolicName->Length != sizeof(DosDevices.Buffer) + 2 * sizeof(WCHAR)) + { + return FALSE; + } + + /* Check if len is correct */ + Letter = SymbolicName->Buffer[sizeof(DosDevices.Buffer) / sizeof(WCHAR)]; + if (((Letter >= L'A' && Letter <= L'Z') || Letter == (WCHAR)-1) && + SymbolicName->Buffer[(sizeof(DosDevices.Buffer) + sizeof(WCHAR)) / sizeof (WCHAR)] == L':') + { + /* In case it's not a normal drive letter, check differently */ + SymbolicName->Length = sizeof(DosDevices.Buffer); + Result = RtlEqualUnicodeString(SymbolicName, &DosDevices, TRUE); + SymbolicName->Length = sizeof(DosDevices.Buffer) + 2 * sizeof(WCHAR); + } + + return Result; +} + +/* + * @implemented + */ +NTSTATUS +MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName, + IN OUT PUNICODE_STRING LinkTarget) +{ + NTSTATUS Status; + HANDLE LinkHandle; + OBJECT_ATTRIBUTES ObjectAttributes; + + /* Open the symbolic link */ + InitializeObjectAttributes(&ObjectAttributes, + SymbolicName, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwOpenSymbolicLinkObject(&LinkHandle, + GENERIC_READ, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Query its target */ + Status = ZwQuerySymbolicLinkObject(LinkHandle, + LinkTarget, + NULL); + + ZwClose(LinkHandle); + + if (!NT_SUCCESS(Status)) + { + return Status; + } + + if (LinkTarget->Length <= sizeof(WCHAR)) + { + return Status; + } + + /* If it's not finished by \, just return */ + if (LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR) - 1] != L'\\') + { + return Status; + } + + /* Otherwise, ensure to drop the tailing \ */ + LinkTarget->Length -= sizeof(WCHAR); + LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR)] = UNICODE_NULL; + + return Status; +} diff --git a/reactos/drivers/filters/mountmgr/uniqueid.c b/reactos/drivers/filters/mountmgr/uniqueid.c new file mode 100644 index 00000000000..66160ffb534 --- /dev/null +++ b/reactos/drivers/filters/mountmgr/uniqueid.c @@ -0,0 +1,436 @@ +/* + * ReactOS kernel + * Copyright (C) 2011 ReactOS Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: drivers/filesystem/mountmgr/uniqueid.c + * PURPOSE: Mount Manager - Unique ID + * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) + */ + +/* INCLUDES *****************************************************************/ + +#include "mntmgr.h" + +#define NDEBUG +#include + +/* + * @implemented + */ +NTSTATUS +NTAPI +ChangeUniqueIdRoutine(IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext) +{ + PMOUNTDEV_UNIQUE_ID OldUniqueId = Context; + PMOUNTDEV_UNIQUE_ID NewUniqueId = EntryContext; + + /* Validate parameters not to corrupt registry */ + if ((ValueType != REG_BINARY) || + (OldUniqueId->UniqueIdLength != ValueLength)) + { + return STATUS_SUCCESS; + } + + if (RtlCompareMemory(OldUniqueId->UniqueId, ValueData, ValueLength) == ValueLength) + { + /* Write new data */ + RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + ValueName, + REG_BINARY, + NewUniqueId, + NewUniqueId->UniqueIdLength); + } + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +VOID +MountMgrUniqueIdChangeRoutine(IN PDEVICE_EXTENSION DeviceExtension, + IN PMOUNTDEV_UNIQUE_ID OldUniqueId, + IN PMOUNTDEV_UNIQUE_ID NewUniqueId) +{ + NTSTATUS Status; + BOOLEAN ResyncNeeded; + PUNIQUE_ID_REPLICATE DuplicateId; + PDEVICE_INFORMATION DeviceInformation; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + PMOUNTDEV_UNIQUE_ID UniqueId, NewDuplicateId; + PLIST_ENTRY ListHead, NextEntry, ReplicatedHead, NextReplicated; + + /* Synchronise with remote databases */ + Status = WaitForRemoteDatabaseSemaphore(DeviceExtension); + KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); + + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].QueryRoutine = ChangeUniqueIdRoutine; + QueryTable[0].EntryContext = NewUniqueId; + + /* Write new data */ + RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + QueryTable, + OldUniqueId, + NULL); + + /* Browse all the devices to find the one that + * owns the old unique ID + */ + ListHead = &(DeviceExtension->DeviceListHead); + NextEntry = ListHead->Flink; + while (ListHead != NextEntry) + { + DeviceInformation = CONTAINING_RECORD(NextEntry, + DEVICE_INFORMATION, + DeviceListEntry); + + if (DeviceInformation->UniqueId->UniqueIdLength == OldUniqueId->UniqueIdLength && + RtlCompareMemory(OldUniqueId->UniqueId, + DeviceInformation->UniqueId->UniqueId, + OldUniqueId->UniqueIdLength) == OldUniqueId->UniqueIdLength) + { + break; + } + + NextEntry = NextEntry->Flink; + } + + /* If we didn't find any release everything and quit */ + if (ListHead == NextEntry) + { + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, + 1, FALSE); + + if (NT_SUCCESS(Status)) + { + ReleaseRemoteDatabaseSemaphore(DeviceExtension); + } + + return; + } + + /* If lock failed, then, just update this database */ + if (!NT_SUCCESS(Status)) + { + ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation); + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, + 1, FALSE); + return; + } + + /* Allocate new unique ID */ + UniqueId = AllocatePool(NewUniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); + if (!UniqueId) + { + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, + 1, FALSE); + ReleaseRemoteDatabaseSemaphore(DeviceExtension); + return; + } + + /* Release old one */ + FreePool(DeviceInformation->UniqueId); + /* And set new one */ + DeviceInformation->UniqueId = UniqueId; + UniqueId->UniqueIdLength = NewUniqueId->UniqueIdLength; + RtlCopyMemory(UniqueId->UniqueId, NewUniqueId->UniqueId, NewUniqueId->UniqueIdLength); + + /* Now, check if it's required to update replicated unique IDs as well */ + ListHead = &(DeviceExtension->DeviceListHead); + NextEntry = ListHead->Flink; + while (ListHead != NextEntry) + { + DeviceInformation = CONTAINING_RECORD(NextEntry, + DEVICE_INFORMATION, + DeviceListEntry); + ResyncNeeded = FALSE; + + ReplicatedHead = &(DeviceInformation->ReplicatedUniqueIdsListHead); + NextReplicated = ReplicatedHead->Flink; + while (ReplicatedHead != NextReplicated) + { + DuplicateId = CONTAINING_RECORD(NextReplicated, + UNIQUE_ID_REPLICATE, + ReplicatedUniqueIdsListEntry); + + if (DuplicateId->UniqueId->UniqueIdLength == OldUniqueId->UniqueIdLength) + { + if (RtlCompareMemory(DuplicateId->UniqueId->UniqueId, + OldUniqueId->UniqueId, + OldUniqueId->UniqueIdLength) == OldUniqueId->UniqueIdLength) + { + /* It was our old unique ID */ + NewDuplicateId = AllocatePool(NewUniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); + if (NewDuplicateId) + { + /* Update it */ + ResyncNeeded = TRUE; + FreePool(DuplicateId->UniqueId); + + DuplicateId->UniqueId = NewDuplicateId; + DuplicateId->UniqueId->UniqueIdLength = NewUniqueId->UniqueIdLength; + RtlCopyMemory(NewDuplicateId->UniqueId, NewUniqueId->UniqueId, NewUniqueId->UniqueIdLength); + } + } + } + + NextReplicated = NextReplicated->Flink; + } + + /* If resync is required on this device, do it */ + if (ResyncNeeded) + { + ChangeRemoteDatabaseUniqueId(DeviceInformation, OldUniqueId, NewUniqueId); + } + + NextEntry = NextEntry->Flink; + } + + KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); + ReleaseRemoteDatabaseSemaphore(DeviceExtension); + + return; +} + +/* + * @implemented + */ +BOOLEAN +IsUniqueIdPresent(IN PDEVICE_EXTENSION DeviceExtension, + IN PDATABASE_ENTRY DatabaseEntry) +{ + PLIST_ENTRY NextEntry; + PDEVICE_INFORMATION DeviceInformation; + + /* If no device, no unique ID (O'rly?!) + * ./)/). + * (°-°) + * (___) ORLY? + * " " + */ + if (IsListEmpty(&(DeviceExtension->DeviceListHead))) + { + return FALSE; + } + + /* Now we know that we have devices, find the one */ + for (NextEntry = DeviceExtension->DeviceListHead.Flink; + NextEntry != &(DeviceExtension->DeviceListHead); + NextEntry = NextEntry->Flink) + { + DeviceInformation = CONTAINING_RECORD(NextEntry, + DEVICE_INFORMATION, + DeviceListEntry); + + if (DeviceInformation->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength) + { + continue; + } + + /* It's matching! */ + if (RtlCompareMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), + DeviceInformation->UniqueId->UniqueId, + DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength) + { + return TRUE; + } + } + + /* No luck... */ + return FALSE; +} + +/* + * @implemented + */ +VOID +CreateNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId) +{ + UUID Guid; + PWCHAR String; + UNICODE_STRING GuidString; + + /* Entry with no drive letter are made that way: + * Instead of having a path with the letter, + * you have GUID with the unique ID. + */ + if (!NT_SUCCESS(ExUuidCreate(&Guid))) + { + return; + } + + /* Convert to string */ + if (!NT_SUCCESS(RtlStringFromGUID(&Guid, &GuidString))) + { + return; + } + + /* No letter entries must start with #, so allocate a proper string */ + String = AllocatePool(GuidString.Length + 2 * sizeof(WCHAR)); + if (!String) + { + ExFreePoolWithTag(GuidString.Buffer, 0); + return; + } + + /* Write the complete string */ + String[0] = L'#'; + RtlCopyMemory(String + 1, GuidString.Buffer, GuidString.Length); + String[GuidString.Length / sizeof(WCHAR)] = UNICODE_NULL; + + /* Don't need that one anymore */ + ExFreePoolWithTag(GuidString.Buffer, 0); + + /* Write the entry */ + RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + String, + REG_BINARY, + UniqueId->UniqueId, + UniqueId->UniqueIdLength); + + FreePool(String); + + return; +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +CheckForNoDriveLetterEntry(IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext) +{ + PBOOLEAN EntryPresent = EntryContext; + PMOUNTDEV_UNIQUE_ID UniqueId = Context; + + /* Check if matches no drive letter entry */ + if (ValueName[0] != L'#' || ValueType != REG_BINARY || + UniqueId->UniqueIdLength != ValueLength) + { + return STATUS_SUCCESS; + } + + /* Compare unique ID */ + if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) == ValueLength) + { + *EntryPresent = TRUE; + } + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +BOOLEAN +HasNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId) +{ + BOOLEAN EntryPresent = FALSE; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].QueryRoutine = CheckForNoDriveLetterEntry; + QueryTable[0].EntryContext = &EntryPresent; + + RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + DatabasePath, + QueryTable, + UniqueId, + NULL); + + return EntryPresent; +} + +/* + * @implemented + */ +VOID +UpdateReplicatedUniqueIds(IN PDEVICE_INFORMATION DeviceInformation, IN PDATABASE_ENTRY DatabaseEntry) +{ + PLIST_ENTRY NextEntry; + PUNIQUE_ID_REPLICATE ReplicatedUniqueId, NewEntry; + + /* Browse all the device replicated unique IDs */ + for (NextEntry = DeviceInformation->ReplicatedUniqueIdsListHead.Flink; + NextEntry != &(DeviceInformation->ReplicatedUniqueIdsListHead); + NextEntry = NextEntry->Flink) + { + ReplicatedUniqueId = CONTAINING_RECORD(NextEntry, + UNIQUE_ID_REPLICATE, + ReplicatedUniqueIdsListEntry); + + if (ReplicatedUniqueId->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength) + { + continue; + } + + /* If we find the UniqueId to update, break */ + if (RtlCompareMemory(ReplicatedUniqueId->UniqueId->UniqueId, + (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), + ReplicatedUniqueId->UniqueId->UniqueIdLength) == ReplicatedUniqueId->UniqueId->UniqueIdLength) + { + break; + } + } + + /* We found the unique ID, no need to continue */ + if (NextEntry != &(DeviceInformation->ReplicatedUniqueIdsListHead)) + { + return; + } + + /* Allocate a new entry for unique ID */ + NewEntry = AllocatePool(sizeof(UNIQUE_ID_REPLICATE)); + if (!NewEntry) + { + return; + } + + /* Allocate the unique ID */ + NewEntry->UniqueId = AllocatePool(DatabaseEntry->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); + if (!NewEntry->UniqueId) + { + FreePool(NewEntry); + return; + } + + /* Copy */ + NewEntry->UniqueId->UniqueIdLength = DatabaseEntry->UniqueIdLength; + RtlCopyMemory(NewEntry->UniqueId->UniqueId, + (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), + DatabaseEntry->UniqueIdLength); + /* And insert into replicated unique IDs list */ + InsertTailList(&DeviceInformation->ReplicatedUniqueIdsListHead, &NewEntry->ReplicatedUniqueIdsListEntry); + + return; +} diff --git a/reactos/drivers/hid/CMakeLists.txt b/reactos/drivers/hid/CMakeLists.txt new file mode 100644 index 00000000000..6051356e048 --- /dev/null +++ b/reactos/drivers/hid/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(hidclass) +add_subdirectory(hidparse) +add_subdirectory(hidusb) +add_subdirectory(kbdhid) +add_subdirectory(mouhid) diff --git a/reactos/drivers/hid/directory.rbuild b/reactos/drivers/hid/directory.rbuild new file mode 100644 index 00000000000..4fa5c649c5f --- /dev/null +++ b/reactos/drivers/hid/directory.rbuild @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/reactos/drivers/hid/hidclass/CMakeLists.txt b/reactos/drivers/hid/hidclass/CMakeLists.txt new file mode 100644 index 00000000000..f5f69220a3a --- /dev/null +++ b/reactos/drivers/hid/hidclass/CMakeLists.txt @@ -0,0 +1,21 @@ + +spec2def(hidclass.sys hidclass.spec) + +list(APPEND SOURCE + fdo.c + hidclass.c + hidclass.rc + pdo.c + ${CMAKE_CURRENT_BINARY_DIR}/hidclass.def) + +add_library(hidclass SHARED ${SOURCE}) + +set_entrypoint(hidclass 0) +set_subsystem(hidclass native) +set_target_properties(hidclass PROPERTIES SUFFIX ".sys") + + +add_importlibs(hidclass ntoskrnl hidparse hal) +add_importlib_target(hidclass.spec) + +add_cd_file(TARGET hidclass DESTINATION reactos/system32/drivers NO_CAB FOR all) \ No newline at end of file diff --git a/reactos/drivers/hid/hidclass/fdo.c b/reactos/drivers/hid/hidclass/fdo.c new file mode 100644 index 00000000000..27df9041161 --- /dev/null +++ b/reactos/drivers/hid/hidclass/fdo.c @@ -0,0 +1,598 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Human Interface Device Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/hid/hidclass/fdo.c + * PURPOSE: HID Class Driver + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ +#include "precomp.h" + +NTSTATUS +NTAPI +HidClassFDO_QueryCapabilitiesCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + // + // set event + // + KeSetEvent((PRKEVENT)Context, 0, FALSE); + + // + // completion is done in the HidClassFDO_QueryCapabilities routine + // + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +HidClassFDO_QueryCapabilities( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PDEVICE_CAPABILITIES Capabilities) +{ + PIRP Irp; + KEVENT Event; + NTSTATUS Status; + PIO_STACK_LOCATION IoStack; + PHIDCLASS_FDO_EXTENSION FDODeviceExtension; + + // + // get device extension + // + FDODeviceExtension = (PHIDCLASS_FDO_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // init event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // now allocte the irp + // + Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + if (!Irp) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get next stack location + // + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // init stack location + // + IoStack->MajorFunction = IRP_MJ_PNP; + IoStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES; + IoStack->Parameters.DeviceCapabilities.Capabilities = Capabilities; + + // + // set completion routine + // + IoSetCompletionRoutine(Irp, HidClassFDO_QueryCapabilitiesCompletionRoutine, (PVOID)&Event, TRUE, TRUE, TRUE); + + // + // init capabilities + // + RtlZeroMemory(Capabilities, sizeof(DEVICE_CAPABILITIES)); + Capabilities->Size = sizeof(DEVICE_CAPABILITIES); + Capabilities->Version = 1; // FIXME hardcoded constant + Capabilities->Address = MAXULONG; + Capabilities->UINumber = MAXULONG; + + // + // pnp irps have default completion code + // + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + + // + // call lower device + // + Status = IoCallDriver(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + // + // wait for completion + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + + // + // get status + // + Status = Irp->IoStatus.Status; + + // + // complete request + // + IoFreeIrp(Irp); + + // + // done + // + return Status; +} + +NTSTATUS +NTAPI +HidClassFDO_DispatchRequestSynchronousCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + // + // signal event + // + KeSetEvent((PRKEVENT)Context, 0, FALSE); + + // + // done + // + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +HidClassFDO_DispatchRequestSynchronous( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + KEVENT Event; + PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension; + NTSTATUS Status; + PIO_STACK_LOCATION IoStack; + + // + // init event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // get device extension + // + CommonDeviceExtension = (PHIDCLASS_COMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // set completion routine + // + IoSetCompletionRoutine(Irp, HidClassFDO_DispatchRequestSynchronousCompletion, &Event, TRUE, TRUE, TRUE); + + ASSERT(Irp->CurrentLocation > 0); + // + // create stack location + // + IoSetNextIrpStackLocation(Irp); + + // + // get next stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // store device object + // + IoStack->DeviceObject = DeviceObject; + + // + // call driver + // + DPRINT("IoStack MajorFunction %x MinorFunction %x\n", IoStack->MajorFunction, IoStack->MinorFunction); + Status = CommonDeviceExtension->DriverExtension->MajorFunction[IoStack->MajorFunction](DeviceObject, Irp); + + // + // wait for the request to finish + // + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + // + // update status + // + Status = Irp->IoStatus.Status; + } + + // + // done + // + return Status; +} + +NTSTATUS +HidClassFDO_GetDescriptors( + IN PDEVICE_OBJECT DeviceObject) +{ + PHIDCLASS_FDO_EXTENSION FDODeviceExtension; + PIRP Irp; + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + + // + // get device extension + // + FDODeviceExtension = (PHIDCLASS_FDO_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // lets allocate irp + // + Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + if (!Irp) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get stack location + // + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // init stack location + // + IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_DEVICE_DESCRIPTOR; + IoStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof(HID_DESCRIPTOR); + IoStack->Parameters.DeviceIoControl.InputBufferLength = 0; + IoStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL; + Irp->UserBuffer = &FDODeviceExtension->HidDescriptor; + + // + // send request + // + Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp); + if (!NT_SUCCESS(Status)) + { + // + // failed to get device descriptor + // + DPRINT1("[HIDCLASS] IOCTL_HID_GET_DEVICE_DESCRIPTOR failed with %x\n", Status); + IoFreeIrp(Irp); + return Status; + } + + // + // lets get device attributes + // + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_DEVICE_ATTRIBUTES; + IoStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof(HID_DEVICE_ATTRIBUTES); + Irp->UserBuffer = &FDODeviceExtension->Common.Attributes; + + // + // send request + // + Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp); + if (!NT_SUCCESS(Status)) + { + // + // failed to get device descriptor + // + DPRINT1("[HIDCLASS] IOCTL_HID_GET_DEVICE_ATTRIBUTES failed with %x\n", Status); + IoFreeIrp(Irp); + return Status; + } + + // + // sanity checks + // + ASSERT(FDODeviceExtension->HidDescriptor.bLength == sizeof(HID_DESCRIPTOR)); + ASSERT(FDODeviceExtension->HidDescriptor.bNumDescriptors > 0); + ASSERT(FDODeviceExtension->HidDescriptor.DescriptorList[0].wReportLength > 0); + ASSERT(FDODeviceExtension->HidDescriptor.DescriptorList[0].bReportType == HID_REPORT_DESCRIPTOR_TYPE); + + + // + // now allocate space for the report descriptor + // + FDODeviceExtension->ReportDescriptor = (PUCHAR)ExAllocatePool(NonPagedPool, FDODeviceExtension->HidDescriptor.DescriptorList[0].wReportLength); + if (!FDODeviceExtension->ReportDescriptor) + { + // + // not enough memory + // + IoFreeIrp(Irp); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // init stack location + // + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_GET_REPORT_DESCRIPTOR; + IoStack->Parameters.DeviceIoControl.OutputBufferLength = FDODeviceExtension->HidDescriptor.DescriptorList[0].wReportLength; + Irp->UserBuffer = FDODeviceExtension->ReportDescriptor; + + // + // send request + // + Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp); + if (!NT_SUCCESS(Status)) + { + // + // failed to get device descriptor + // + DPRINT1("[HIDCLASS] IOCTL_HID_GET_REPORT_DESCRIPTOR failed with %x\n", Status); + IoFreeIrp(Irp); + return Status; + } + + // + // completed successfully + // + return STATUS_SUCCESS; +} + + +NTSTATUS +HidClassFDO_StartDevice( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + NTSTATUS Status; + PHIDCLASS_FDO_EXTENSION FDODeviceExtension; + + // + // get device extension + // + FDODeviceExtension = (PHIDCLASS_FDO_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // query capabilities + // + Status = HidClassFDO_QueryCapabilities(DeviceObject, &FDODeviceExtension->Capabilities); + ASSERT(Status == STATUS_SUCCESS); + + // + // lets start the lower device too + // + IoSkipCurrentIrpStackLocation(Irp); + Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp); + ASSERT(Status == STATUS_SUCCESS); + + // + // lets get the descriptors + // + Status = HidClassFDO_GetDescriptors(DeviceObject); + ASSERT(Status == STATUS_SUCCESS); + + // + // now get the the collection description + // + Status = HidP_GetCollectionDescription(FDODeviceExtension->ReportDescriptor, FDODeviceExtension->HidDescriptor.DescriptorList[0].wReportLength, NonPagedPool, &FDODeviceExtension->Common.DeviceDescription); + ASSERT(Status == STATUS_SUCCESS); + + // + // complete request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; +} + +NTSTATUS +HidClassFDO_RemoveDevice( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PHIDCLASS_FDO_EXTENSION FDODeviceExtension = DeviceObject->DeviceExtension; + NTSTATUS Status; + + /* Pass the IRP down */ + IoSkipCurrentIrpStackLocation(Irp); + Status = IoCallDriver(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject, Irp); + + /* Now teardown our portion of the device stack */ + IoDetachDevice(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject); + IoDeleteDevice(DeviceObject); + + return Status; +} + +NTSTATUS +HidClassFDO_CopyDeviceRelations( + IN PDEVICE_OBJECT DeviceObject, + OUT PDEVICE_RELATIONS *OutRelations) +{ + PDEVICE_RELATIONS DeviceRelations; + PHIDCLASS_FDO_EXTENSION FDODeviceExtension; + ULONG Index; + + // + // get device extension + // + FDODeviceExtension = (PHIDCLASS_FDO_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // allocate result + // + DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(NonPagedPool, sizeof(DEVICE_RELATIONS) + (FDODeviceExtension->DeviceRelations->Count-1) * sizeof(PDEVICE_OBJECT)); + if (!DeviceRelations) + { + // + // no memory + // + *OutRelations = NULL; + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy device objects + // + for(Index = 0; Index < FDODeviceExtension->DeviceRelations->Count; Index++) + { + // + // reference pdo + // + ObReferenceObject(FDODeviceExtension->DeviceRelations->Objects[Index]); + + // + // store object + // + DeviceRelations->Objects[Index] = FDODeviceExtension->DeviceRelations->Objects[Index]; + } + + // + // set object count + // + DeviceRelations->Count = FDODeviceExtension->DeviceRelations->Count; + + // + // store result + // + *OutRelations = DeviceRelations; + return STATUS_SUCCESS; +} + +NTSTATUS +HidClassFDO_DeviceRelations( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PHIDCLASS_FDO_EXTENSION FDODeviceExtension; + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + PDEVICE_RELATIONS DeviceRelations; + + // + // get device extension + // + FDODeviceExtension = (PHIDCLASS_FDO_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // check relations type + // + if (IoStack->Parameters.QueryDeviceRelations.Type != BusRelations) + { + // + // only bus relations are handled + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject, Irp); + } + + if (FDODeviceExtension->DeviceRelations == NULL) + { + // + // time to create the pdos + // + Status = HidClassPDO_CreatePDO(DeviceObject, &FDODeviceExtension->DeviceRelations); + if (!NT_SUCCESS(Status)) + { + // + // failed + // + DPRINT1("[HIDCLASS] HidClassPDO_CreatePDO failed with %x\n", Status); + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + // + // sanity check + // + ASSERT(FDODeviceExtension->DeviceRelations->Count > 0); + } + + // + // now copy device relations + // + Status = HidClassFDO_CopyDeviceRelations(DeviceObject, &DeviceRelations); + // + // store result + // + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + + // + // complete request + // + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; +} + +NTSTATUS +HidClassFDO_PnP( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PHIDCLASS_FDO_EXTENSION FDODeviceExtension; + + // + // get device extension + // + FDODeviceExtension = (PHIDCLASS_FDO_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + switch(IoStack->MinorFunction) + { + case IRP_MN_START_DEVICE: + { + return HidClassFDO_StartDevice(DeviceObject, Irp); + } + case IRP_MN_REMOVE_DEVICE: + { + return HidClassFDO_RemoveDevice(DeviceObject, Irp); + } + case IRP_MN_QUERY_REMOVE_DEVICE: + case IRP_MN_QUERY_STOP_DEVICE: + { + // + // set status to succes + // + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // forward to lower device + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject, Irp); + } + case IRP_MN_CANCEL_REMOVE_DEVICE: + case IRP_MN_CANCEL_STOP_DEVICE: + { + // + // set status to succes + // + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // forward to lower device + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject, Irp); + } + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + return HidClassFDO_DeviceRelations(DeviceObject, Irp); + } + default: + { + // + // dispatch to lower device + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject, Irp); + } + } +} diff --git a/reactos/drivers/hid/hidclass/hidclass.c b/reactos/drivers/hid/hidclass/hidclass.c new file mode 100644 index 00000000000..deb67f496e0 --- /dev/null +++ b/reactos/drivers/hid/hidclass/hidclass.c @@ -0,0 +1,1032 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Human Interface Device Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/hid/hidclass/hidclass.c + * PURPOSE: HID Class Driver + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "precomp.h" + +static LPWSTR ClientIdentificationAddress = L"HIDCLASS"; +static ULONG HidClassDeviceNumber = 0; + +ULONG +NTAPI +DllInitialize(ULONG Unknown) +{ + return 0; +} + +ULONG +NTAPI +DllUnload() +{ + return 0; +} + +NTSTATUS +NTAPI +HidClassAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) +{ + WCHAR CharDeviceName[64]; + NTSTATUS Status; + UNICODE_STRING DeviceName; + PDEVICE_OBJECT NewDeviceObject; + PHIDCLASS_FDO_EXTENSION FDODeviceExtension; + ULONG DeviceExtensionSize; + PHIDCLASS_DRIVER_EXTENSION DriverExtension; + + + /* increment device number */ + InterlockedIncrement((PLONG)&HidClassDeviceNumber); + + /* construct device name */ + swprintf(CharDeviceName, L"\\Device\\_HID%08x", HidClassDeviceNumber); + + /* initialize device name */ + RtlInitUnicodeString(&DeviceName, CharDeviceName); + + /* get driver object extension */ + DriverExtension = (PHIDCLASS_DRIVER_EXTENSION) IoGetDriverObjectExtension(DriverObject, ClientIdentificationAddress); + if (!DriverExtension) + { + /* device removed */ + ASSERT(FALSE); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + /* calculate device extension size */ + DeviceExtensionSize = sizeof(HIDCLASS_FDO_EXTENSION) + DriverExtension->DeviceExtensionSize; + + /* now create the device */ + Status = IoCreateDevice(DriverObject, DeviceExtensionSize, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &NewDeviceObject); + if (!NT_SUCCESS(Status)) + { + /* failed to create device object */ + ASSERT(FALSE); + return Status; + } + + /* get device extension */ + FDODeviceExtension = (PHIDCLASS_FDO_EXTENSION)NewDeviceObject->DeviceExtension; + + /* zero device extension */ + RtlZeroMemory(FDODeviceExtension, sizeof(HIDCLASS_FDO_EXTENSION)); + + /* initialize device extension */ + FDODeviceExtension->Common.IsFDO = TRUE; + FDODeviceExtension->Common.DriverExtension = DriverExtension; + FDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject = PhysicalDeviceObject; + FDODeviceExtension->Common.HidDeviceExtension.MiniDeviceExtension = (PVOID)((ULONG_PTR)FDODeviceExtension + sizeof(HIDCLASS_FDO_EXTENSION)); + FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject = IoAttachDeviceToDeviceStack(NewDeviceObject, PhysicalDeviceObject); + if (FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject == NULL) + { + /* no PDO */ + IoDeleteDevice(NewDeviceObject); + DPRINT1("[HIDCLASS] failed to attach to device stack\n"); + return STATUS_DEVICE_REMOVED; + } + + /* sanity check */ + ASSERT(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject); + + /* increment stack size */ + NewDeviceObject->StackSize++; + + /* init device object */ + NewDeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; + NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + /* now call driver provided add device routine */ + ASSERT(DriverExtension->AddDevice != 0); + Status = DriverExtension->AddDevice(DriverObject, NewDeviceObject); + if (!NT_SUCCESS(Status)) + { + /* failed */ + DPRINT1("HIDCLASS: AddDevice failed with %x\n", Status); + IoDetachDevice(FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject); + IoDeleteDevice(NewDeviceObject); + return Status; + } + + /* succeeded */ + return Status; +} + +VOID +NTAPI +HidClassDriverUnload( + IN PDRIVER_OBJECT DriverObject) +{ + UNIMPLEMENTED +} + +NTSTATUS +NTAPI +HidClass_Create( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension; + PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension; + PHIDCLASS_FILEOP_CONTEXT Context; + + // + // get device extension + // + CommonDeviceExtension = (PHIDCLASS_COMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + if (CommonDeviceExtension->IsFDO) + { +#ifndef __REACTOS__ + + // + // only supported for PDO + // + Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_UNSUCCESSFUL; +#else + // + // ReactOS PnP manager [...] + // + DPRINT1("[HIDCLASS] PnP HACK\n"); + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +#endif + } + + // + // must be a PDO + // + ASSERT(CommonDeviceExtension->IsFDO == FALSE); + + // + // get device extension + // + PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)CommonDeviceExtension; + + // + // get stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + DPRINT("ShareAccess %x\n", IoStack->Parameters.Create.ShareAccess); + DPRINT("Options %x\n", IoStack->Parameters.Create.Options); + DPRINT("DesiredAccess %x\n", IoStack->Parameters.Create.SecurityContext->DesiredAccess); + + // + // allocate context + // + Context = (PHIDCLASS_FILEOP_CONTEXT)ExAllocatePool(NonPagedPool, sizeof(HIDCLASS_FILEOP_CONTEXT)); + if (!Context) + { + // + // no memory + // + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // init context + // + RtlZeroMemory(Context, sizeof(HIDCLASS_FILEOP_CONTEXT)); + Context->DeviceExtension = PDODeviceExtension; + KeInitializeSpinLock(&Context->Lock); + InitializeListHead(&Context->ReadPendingIrpListHead); + InitializeListHead(&Context->IrpCompletedListHead); + + // + // store context + // + ASSERT(IoStack->FileObject); + IoStack->FileObject->FsContext = (PVOID)Context; + + // + // done + // + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +HidClass_Close( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension; + PHIDCLASS_IRP_CONTEXT IrpContext; + + // + // get device extension + // + CommonDeviceExtension = (PHIDCLASS_COMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // is it a FDO request + // + if (CommonDeviceExtension->IsFDO) + { + // + // how did the request get there + // + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER_1; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER_1; + } + + // + // get stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // sanity checks + // + ASSERT(IoStack->FileObject); + ASSERT(IoStack->FileObject->FsContext); + + // + // get irp context + // + IrpContext = (PHIDCLASS_IRP_CONTEXT)IoStack->FileObject->FsContext; + + // + // cancel pending irps + // + UNIMPLEMENTED + + // + // remove context + // + IoStack->FileObject->FsContext = NULL; + + // + // free context + // + ExFreePool(IrpContext); + + // + // complete request + // + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + +PVOID +HidClass_GetSystemAddress( + IN PMDL ReportMDL) +{ + // + // sanity check + // + ASSERT(ReportMDL); + + if (ReportMDL->MdlFlags & (MDL_SOURCE_IS_NONPAGED_POOL | MDL_MAPPED_TO_SYSTEM_VA)) + { + // + // buffer is non paged pool + // + return ReportMDL->MappedSystemVa; + } + else + { + // + // map mdl + // + return MmMapLockedPages(ReportMDL, KernelMode); + } +} + +NTSTATUS +NTAPI +HidClass_ReadCompleteIrp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Ctx) +{ + PHIDCLASS_IRP_CONTEXT IrpContext; + KIRQL OldLevel; + PUCHAR Address; + ULONG Offset; + PHIDP_COLLECTION_DESC CollectionDescription; + PHIDP_REPORT_IDS ReportDescription; + + // + // get irp context + // + IrpContext = (PHIDCLASS_IRP_CONTEXT)Ctx; + + DPRINT("HidClass_ReadCompleteIrp Irql %lu\n", KeGetCurrentIrql()); + DPRINT("HidClass_ReadCompleteIrp Status %lx\n", Irp->IoStatus.Status); + DPRINT("HidClass_ReadCompleteIrp Length %lu\n", Irp->IoStatus.Information); + DPRINT("HidClass_ReadCompleteIrp Irp %p\n", Irp); + DPRINT("HidClass_ReadCompleteIrp InputReportBuffer %p\n", IrpContext->InputReportBuffer); + DPRINT("HidClass_ReadCompleteIrp InputReportBufferLength %li\n", IrpContext->InputReportBufferLength); + DPRINT("HidClass_ReadCompleteIrp OriginalIrp %p\n", IrpContext->OriginalIrp); + + // + // copy result + // + if (Irp->IoStatus.Information) + { + // + // get address + // + Address = (PUCHAR)HidClass_GetSystemAddress(IrpContext->OriginalIrp->MdlAddress); + if (Address) + { + // + // reports may have a report id prepended + // + Offset = 0; + + // + // get collection description + // + CollectionDescription = HidClassPDO_GetCollectionDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription, IrpContext->FileOp->DeviceExtension->CollectionNumber); + ASSERT(CollectionDescription); + + // + // get report description + // + ReportDescription = HidClassPDO_GetReportDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription, IrpContext->FileOp->DeviceExtension->CollectionNumber); + ASSERT(ReportDescription); + + if (CollectionDescription && ReportDescription) + { + // + // calculate offset + // + ASSERT(CollectionDescription->InputLength >= ReportDescription->InputLength); + Offset = CollectionDescription->InputLength - ReportDescription->InputLength; + } + + // + // copy result + // + RtlCopyMemory(&Address[Offset], IrpContext->InputReportBuffer, IrpContext->InputReportBufferLength); + } + } + + // + // copy result status + // + IrpContext->OriginalIrp->IoStatus.Status = Irp->IoStatus.Status; + Irp->IoStatus.Information = Irp->IoStatus.Information; + + // + // free input report buffer + // + ExFreePool(IrpContext->InputReportBuffer); + + // + // remove us from pending list + // + KeAcquireSpinLock(&IrpContext->FileOp->Lock, &OldLevel); + + // + // remove from pending list + // + RemoveEntryList(&Irp->Tail.Overlay.ListEntry); + + // + // insert into completed list + // + InsertTailList(&IrpContext->FileOp->IrpCompletedListHead, &Irp->Tail.Overlay.ListEntry); + + // + // release lock + // + KeReleaseSpinLock(&IrpContext->FileOp->Lock, OldLevel); + + // + // complete original request + // + IoCompleteRequest(IrpContext->OriginalIrp, IO_NO_INCREMENT); + + // + // free irp context + // + ExFreePool(IrpContext); + + // + // done + // + return STATUS_MORE_PROCESSING_REQUIRED; +} + +PIRP +HidClass_GetIrp( + IN PHIDCLASS_FILEOP_CONTEXT Context) +{ + KIRQL OldLevel; + PIRP Irp = NULL; + PLIST_ENTRY ListEntry; + + // + // acquire lock + // + KeAcquireSpinLock(&Context->Lock, &OldLevel); + + // + // is list empty? + // + if (!IsListEmpty(&Context->IrpCompletedListHead)) + { + // + // grab first entry + // + ListEntry = RemoveHeadList(&Context->IrpCompletedListHead); + + // + // get irp + // + Irp = (PIRP)CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry); + } + + // + // release lock + // + KeReleaseSpinLock(&Context->Lock, OldLevel); + + // + // done + // + return Irp; +} + +NTSTATUS +HidClass_BuildIrp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP RequestIrp, + IN PHIDCLASS_FILEOP_CONTEXT Context, + IN ULONG DeviceIoControlCode, + IN ULONG BufferLength, + OUT PIRP *OutIrp, + OUT PHIDCLASS_IRP_CONTEXT *OutIrpContext) +{ + PIRP Irp; + PIO_STACK_LOCATION IoStack; + PHIDCLASS_IRP_CONTEXT IrpContext; + PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension; + PHIDP_COLLECTION_DESC CollectionDescription; + PHIDP_REPORT_IDS ReportDescription; + + // + // get an irp from fresh list + // + Irp = HidClass_GetIrp(Context); + if (!Irp) + { + // + // build new irp + // + Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + if (!Irp) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + // + // re-use irp + // + IoReuseIrp(Irp, STATUS_SUCCESS); + } + + // + // allocate completion context + // + IrpContext = (PHIDCLASS_IRP_CONTEXT)ExAllocatePool(NonPagedPool, sizeof(HIDCLASS_IRP_CONTEXT)); + if (!IrpContext) + { + // + // no memory + // + IoFreeIrp(Irp); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get device extension + // + PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + // + // init irp context + // + RtlZeroMemory(IrpContext, sizeof(HIDCLASS_IRP_CONTEXT)); + IrpContext->OriginalIrp = RequestIrp; + IrpContext->FileOp = Context; + + // + // get collection description + // + CollectionDescription = HidClassPDO_GetCollectionDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription, IrpContext->FileOp->DeviceExtension->CollectionNumber); + ASSERT(CollectionDescription); + + // + // get report description + // + ReportDescription = HidClassPDO_GetReportDescription(&IrpContext->FileOp->DeviceExtension->Common.DeviceDescription, IrpContext->FileOp->DeviceExtension->CollectionNumber); + ASSERT(ReportDescription); + + // + // sanity check + // + ASSERT(CollectionDescription->InputLength >= ReportDescription->InputLength); + + // + // store report length + // + IrpContext->InputReportBufferLength = ReportDescription->InputLength; + + // + // allocate buffer + // + IrpContext->InputReportBuffer = ExAllocatePool(NonPagedPool, IrpContext->InputReportBufferLength); + if (!IrpContext->InputReportBuffer) + { + // + // no memory + // + IoFreeIrp(Irp); + ExFreePool(IrpContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get stack location + // + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // init stack location + // + IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IoStack->Parameters.DeviceIoControl.IoControlCode = DeviceIoControlCode; + IoStack->Parameters.DeviceIoControl.OutputBufferLength = IrpContext->InputReportBufferLength; + IoStack->Parameters.DeviceIoControl.InputBufferLength = 0; + IoStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL; + Irp->UserBuffer = IrpContext->InputReportBuffer; + IoStack->DeviceObject = DeviceObject; + + // + // store result + // + *OutIrp = Irp; + *OutIrpContext = IrpContext; + + // + // done + // + return STATUS_SUCCESS; +} + + +NTSTATUS +NTAPI +HidClass_Read( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PHIDCLASS_FILEOP_CONTEXT Context; + KIRQL OldLevel; + NTSTATUS Status; + PIRP NewIrp; + PHIDCLASS_IRP_CONTEXT NewIrpContext; + PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + CommonDeviceExtension = (PHIDCLASS_COMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(CommonDeviceExtension->IsFDO == FALSE); + + // + // sanity check + // + ASSERT(IoStack->FileObject); + ASSERT(IoStack->FileObject->FsContext); + + // + // get context + // + Context = (PHIDCLASS_FILEOP_CONTEXT)IoStack->FileObject->FsContext; + ASSERT(Context); + + // + // FIXME support polled devices + // + ASSERT(Context->DeviceExtension->Common.DriverExtension->DevicesArePolled == FALSE); + + // + // build irp request + // + Status = HidClass_BuildIrp(DeviceObject, Irp, Context, IOCTL_HID_READ_REPORT, IoStack->Parameters.Read.Length, &NewIrp, &NewIrpContext); + if (!NT_SUCCESS(Status)) + { + // + // failed + // + DPRINT1("HidClass_BuildIrp failed with %x\n", Status); + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + + // + // acquire lock + // + KeAcquireSpinLock(&Context->Lock, &OldLevel); + + // + // insert irp into pending list + // + InsertTailList(&Context->ReadPendingIrpListHead, &NewIrp->Tail.Overlay.ListEntry); + + // + // set completion routine + // + IoSetCompletionRoutine(NewIrp, HidClass_ReadCompleteIrp, NewIrpContext, TRUE, TRUE, TRUE); + + // + // make next location current + // + IoSetNextIrpStackLocation(NewIrp); + + // + // release spin lock + // + KeReleaseSpinLock(&Context->Lock, OldLevel); + + // + // mark irp pending + // + IoMarkIrpPending(Irp); + + // + // lets dispatch the request + // + ASSERT(Context->DeviceExtension); + Status = Context->DeviceExtension->Common.DriverExtension->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL](Context->DeviceExtension->FDODeviceObject, NewIrp); + + // + // complete + // + return STATUS_PENDING; +} + +NTSTATUS +NTAPI +HidClass_Write( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + UNIMPLEMENTED + ASSERT(FALSE); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +HidClass_DeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension; + PHID_COLLECTION_INFORMATION CollectionInformation; + PHIDP_COLLECTION_DESC CollectionDescription; + PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension; + + // + // get device extension + // + CommonDeviceExtension = (PHIDCLASS_COMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // only PDO are supported + // + if (CommonDeviceExtension->IsFDO) + { + // + // invalid request + // + DPRINT1("[HIDCLASS] DeviceControl Irp for FDO arrived\n"); + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER_1; + } + + ASSERT(CommonDeviceExtension->IsFDO == FALSE); + + // + // get pdo device extension + // + PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // get stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + switch(IoStack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_HID_GET_COLLECTION_INFORMATION: + { + // + // check if output buffer is big enough + // + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_COLLECTION_INFORMATION)) + { + // + // invalid buffer size + // + Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_BUFFER_SIZE; + } + + // + // get output buffer + // + CollectionInformation = (PHID_COLLECTION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + ASSERT(CollectionInformation); + + // + // get collection description + // + CollectionDescription = HidClassPDO_GetCollectionDescription(&CommonDeviceExtension->DeviceDescription, PDODeviceExtension->CollectionNumber); + ASSERT(CollectionDescription); + + // + // init result buffer + // + CollectionInformation->DescriptorSize = CollectionDescription->PreparsedDataLength; + CollectionInformation->Polled = CommonDeviceExtension->DriverExtension->DevicesArePolled; + CollectionInformation->VendorID = CommonDeviceExtension->Attributes.VendorID; + CollectionInformation->ProductID = CommonDeviceExtension->Attributes.ProductID; + CollectionInformation->VersionNumber = CommonDeviceExtension->Attributes.VersionNumber; + + // + // complete request + // + Irp->IoStatus.Information = sizeof(HID_COLLECTION_INFORMATION); + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + case IOCTL_HID_GET_COLLECTION_DESCRIPTOR: + { + // + // get collection description + // + CollectionDescription = HidClassPDO_GetCollectionDescription(&CommonDeviceExtension->DeviceDescription, PDODeviceExtension->CollectionNumber); + ASSERT(CollectionDescription); + + // + // check if output buffer is big enough + // + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < CollectionDescription->PreparsedDataLength) + { + // + // invalid buffer size + // + Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_BUFFER_SIZE; + } + + // + // copy result + // + ASSERT(Irp->UserBuffer); + RtlCopyMemory(Irp->UserBuffer, CollectionDescription->PreparsedData, CollectionDescription->PreparsedDataLength); + + // + // complete request + // + Irp->IoStatus.Information = CollectionDescription->PreparsedDataLength; + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + default: + { + DPRINT1("[HIDCLASS] DeviceControl IoControlCode 0x%x not implemented\n", IoStack->Parameters.DeviceIoControl.IoControlCode); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + } +} + +NTSTATUS +NTAPI +HidClass_InternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + UNIMPLEMENTED + ASSERT(FALSE); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS +NTAPI +HidClass_Power( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +HidClass_PnP( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension; + + // + // get common device extension + // + CommonDeviceExtension = (PHIDCLASS_COMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // check type of device object + // + if (CommonDeviceExtension->IsFDO) + { + // + // handle request + // + return HidClassFDO_PnP(DeviceObject, Irp); + } + else + { + // + // handle request + // + return HidClassPDO_PnP(DeviceObject, Irp); + } +} + +NTSTATUS +NTAPI +HidClass_DispatchDefault( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtension; + + // + // get common device extension + // + CommonDeviceExtension = (PHIDCLASS_COMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // FIXME: support PDO + // + ASSERT(CommonDeviceExtension->IsFDO == TRUE); + + // + // skip current irp stack location + // + IoSkipCurrentIrpStackLocation(Irp); + + // + // dispatch to lower device object + // + return IoCallDriver(CommonDeviceExtension->HidDeviceExtension.NextDeviceObject, Irp); +} + + +NTSTATUS +NTAPI +HidClassDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + DPRINT("[HIDCLASS] Dispatch Major %x Minor %x\n", IoStack->MajorFunction, IoStack->MinorFunction); + + // + // dispatch request based on major function + // + switch(IoStack->MajorFunction) + { + case IRP_MJ_CREATE: + return HidClass_Create(DeviceObject, Irp); + case IRP_MJ_CLOSE: + return HidClass_Close(DeviceObject, Irp); + case IRP_MJ_READ: + return HidClass_Read(DeviceObject, Irp); + case IRP_MJ_WRITE: + return HidClass_Write(DeviceObject, Irp); + case IRP_MJ_DEVICE_CONTROL: + return HidClass_DeviceControl(DeviceObject, Irp); + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + return HidClass_InternalDeviceControl(DeviceObject, Irp); + case IRP_MJ_POWER: + return HidClass_Power(DeviceObject, Irp); + case IRP_MJ_PNP: + return HidClass_PnP(DeviceObject, Irp); + default: + return HidClass_DispatchDefault(DeviceObject, Irp); + } +} + +NTSTATUS +NTAPI +HidRegisterMinidriver( + IN PHID_MINIDRIVER_REGISTRATION MinidriverRegistration) +{ + NTSTATUS Status; + PHIDCLASS_DRIVER_EXTENSION DriverExtension; + + /* check if the version matches */ + if (MinidriverRegistration->Revision > HID_REVISION) + { + /* revision mismatch */ + ASSERT(FALSE); + return STATUS_REVISION_MISMATCH; + } + + /* now allocate the driver object extension */ + Status = IoAllocateDriverObjectExtension(MinidriverRegistration->DriverObject, (PVOID)ClientIdentificationAddress, sizeof(HIDCLASS_DRIVER_EXTENSION), (PVOID*)&DriverExtension); + if (!NT_SUCCESS(Status)) + { + /* failed to allocate driver extension */ + ASSERT(FALSE); + return Status; + } + + /* zero driver extension */ + RtlZeroMemory(DriverExtension, sizeof(HIDCLASS_DRIVER_EXTENSION)); + + /* init driver extension */ + DriverExtension->DriverObject = MinidriverRegistration->DriverObject; + DriverExtension->DeviceExtensionSize = MinidriverRegistration->DeviceExtensionSize; + DriverExtension->DevicesArePolled = MinidriverRegistration->DevicesArePolled; + DriverExtension->AddDevice = MinidriverRegistration->DriverObject->DriverExtension->AddDevice; + DriverExtension->DriverUnload = MinidriverRegistration->DriverObject->DriverUnload; + + /* copy driver dispatch routines */ + RtlCopyMemory(DriverExtension->MajorFunction, MinidriverRegistration->DriverObject->MajorFunction, sizeof(PDRIVER_DISPATCH) * (IRP_MJ_MAXIMUM_FUNCTION+1)); + + /* initialize lock */ + KeInitializeSpinLock(&DriverExtension->Lock); + + /* now replace dispatch routines */ + DriverExtension->DriverObject->DriverExtension->AddDevice = HidClassAddDevice; + DriverExtension->DriverObject->DriverUnload = HidClassDriverUnload; + DriverExtension->DriverObject->MajorFunction[IRP_MJ_CREATE] = HidClassDispatch; + DriverExtension->DriverObject->MajorFunction[IRP_MJ_CLOSE] = HidClassDispatch; + DriverExtension->DriverObject->MajorFunction[IRP_MJ_READ] = HidClassDispatch; + DriverExtension->DriverObject->MajorFunction[IRP_MJ_WRITE] = HidClassDispatch; + DriverExtension->DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HidClassDispatch; + DriverExtension->DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = HidClassDispatch; + DriverExtension->DriverObject->MajorFunction[IRP_MJ_POWER] = HidClassDispatch; + DriverExtension->DriverObject->MajorFunction[IRP_MJ_PNP] = HidClassDispatch; + + /* done */ + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/hid/hidclass/hidclass.rbuild b/reactos/drivers/hid/hidclass/hidclass.rbuild new file mode 100644 index 00000000000..c5419c63b93 --- /dev/null +++ b/reactos/drivers/hid/hidclass/hidclass.rbuild @@ -0,0 +1,13 @@ + + + + + + ntoskrnl + hidparse + hal + fdo.c + hidclass.c + hidclass.rc + pdo.c + diff --git a/reactos/drivers/hid/hidclass/hidclass.rc b/reactos/drivers/hid/hidclass/hidclass.rc new file mode 100644 index 00000000000..a700199fb14 --- /dev/null +++ b/reactos/drivers/hid/hidclass/hidclass.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "USB HID Bus Driver\0" +#define REACTOS_STR_INTERNAL_NAME "hidclass\0" +#define REACTOS_STR_ORIGINAL_FILENAME "hidclass.sys\0" +#include diff --git a/reactos/drivers/hid/hidclass/hidclass.spec b/reactos/drivers/hid/hidclass/hidclass.spec new file mode 100644 index 00000000000..3539f1f6aea --- /dev/null +++ b/reactos/drivers/hid/hidclass/hidclass.spec @@ -0,0 +1,4 @@ +@ stdcall -private DllInitialize(long) +@ stdcall -private DllUnload() +@ stdcall HidRegisterMinidriver(ptr) + diff --git a/reactos/drivers/hid/hidclass/pdo.c b/reactos/drivers/hid/hidclass/pdo.c new file mode 100644 index 00000000000..cfaadc6e9d7 --- /dev/null +++ b/reactos/drivers/hid/hidclass/pdo.c @@ -0,0 +1,759 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Human Interface Device Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/hid/hidclass/fdo.c + * PURPOSE: HID Class Driver + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ +#include "precomp.h" + +PHIDP_COLLECTION_DESC +HidClassPDO_GetCollectionDescription( + PHIDP_DEVICE_DESC DeviceDescription, + ULONG CollectionNumber) +{ + ULONG Index; + + for(Index = 0; Index < DeviceDescription->CollectionDescLength; Index++) + { + if (DeviceDescription->CollectionDesc[Index].CollectionNumber == CollectionNumber) + { + // + // found collection + // + return &DeviceDescription->CollectionDesc[Index]; + } + } + + // + // failed to find collection + // + DPRINT1("[HIDCLASS] GetCollectionDescription CollectionNumber %x not found\n", CollectionNumber); + ASSERT(FALSE); + return NULL; +} + +PHIDP_REPORT_IDS +HidClassPDO_GetReportDescription( + PHIDP_DEVICE_DESC DeviceDescription, + ULONG CollectionNumber) +{ + ULONG Index; + + for(Index = 0; Index < DeviceDescription->ReportIDsLength; Index++) + { + if (DeviceDescription->ReportIDs[Index].CollectionNumber == CollectionNumber) + { + // + // found collection + // + return &DeviceDescription->ReportIDs[Index]; + } + } + + // + // failed to find collection + // + DPRINT1("[HIDCLASS] GetReportDescription CollectionNumber %x not found\n", CollectionNumber); + ASSERT(FALSE); + return NULL; +} + +NTSTATUS +HidClassPDO_HandleQueryDeviceId( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + NTSTATUS Status; + LPWSTR Buffer; + LPWSTR NewBuffer, Ptr; + ULONG Length; + + // + // copy current stack location + // + IoCopyCurrentIrpStackLocationToNext(Irp); + + // + // call mini-driver + // + Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp); + if (!NT_SUCCESS(Status)) + { + // + // failed + // + return Status; + } + + // + // get buffer + // + Buffer = (LPWSTR)Irp->IoStatus.Information; + Length = wcslen(Buffer); + + // + // allocate new buffer + // + NewBuffer = (LPWSTR)ExAllocatePool(NonPagedPool, (Length + 1) * sizeof(WCHAR)); + if (!NewBuffer) + { + // + // failed to allocate buffer + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // replace bus + // + wcscpy(NewBuffer, L"HID\\"); + + // + // get offset to first '\\' + // + Ptr = wcschr(Buffer, L'\\'); + if (Ptr) + { + // + // append result + // + wcscat(NewBuffer, Ptr + 1); + } + + // + // free old buffer + // + ExFreePool(Buffer); + + // + // store result + // + DPRINT("NewBuffer %S\n", NewBuffer); + Irp->IoStatus.Information = (ULONG_PTR)NewBuffer; + return STATUS_SUCCESS; +} + +NTSTATUS +HidClassPDO_HandleQueryHardwareId( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + NTSTATUS Status; + PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension; + WCHAR Buffer[200]; + ULONG Offset = 0; + LPWSTR Ptr; + PHIDP_COLLECTION_DESC CollectionDescription; + + // + // get device extension + // + PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + // + // copy current stack location + // + IoCopyCurrentIrpStackLocationToNext(Irp); + + // + // call mini-driver + // + Status = HidClassFDO_DispatchRequestSynchronous(DeviceObject, Irp); + if (!NT_SUCCESS(Status)) + { + // + // failed + // + return Status; + } + + if (PDODeviceExtension->Common.DeviceDescription.CollectionDescLength > 1) + { + // + // multi-tlc device + // + Offset = swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x&Rev_%04x&Col%02x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->Common.Attributes.VersionNumber, PDODeviceExtension->CollectionNumber) + 1; + Offset += swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x&Col%02x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->CollectionNumber) + 1; + } + else + { + // + // single tlc device + // + Offset = swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x&Rev_%04x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID, PDODeviceExtension->Common.Attributes.VersionNumber) + 1; + Offset += swprintf(&Buffer[Offset], L"HID\\Vid_%04x&Pid_%04x", PDODeviceExtension->Common.Attributes.VendorID, PDODeviceExtension->Common.Attributes.ProductID) + 1; + } + + // + // get collection description + // + CollectionDescription = HidClassPDO_GetCollectionDescription(&PDODeviceExtension->Common.DeviceDescription, PDODeviceExtension->CollectionNumber); + ASSERT(CollectionDescription); + + if (CollectionDescription->UsagePage == HID_USAGE_PAGE_GENERIC) + { + switch(CollectionDescription->Usage) + { + case HID_USAGE_GENERIC_POINTER: + case HID_USAGE_GENERIC_MOUSE: + // + // Pointer / Mouse + // + Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_MOUSE") + 1; + break; + case HID_USAGE_GENERIC_GAMEPAD: + case HID_USAGE_GENERIC_JOYSTICK: + // + // Joystick / Gamepad + // + Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_GAME") + 1; + break; + case HID_USAGE_GENERIC_KEYBOARD: + case HID_USAGE_GENERIC_KEYPAD: + // + // Keyboard / Keypad + // + Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_KEYBOARD") + 1; + break; + case HID_USAGE_GENERIC_SYSTEM_CTL: + // + // System Control + // + Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_CONTROL") + 1; + break; + } + } + else if (CollectionDescription->UsagePage == HID_USAGE_PAGE_CONSUMER && CollectionDescription->Usage == HID_USAGE_CONSUMERCTRL) + { + // + // Consumer Audio Control + // + Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_SYSTEM_CONSUMER") + 1; + } + + // + // add HID_DEVICE_UP:0001_U:0002' + // + Offset += swprintf(&Buffer[Offset], L"HID_DEVICE_UP:%04x_U:%04x", CollectionDescription->UsagePage, CollectionDescription->Usage) + 1; + + // + // add HID + // + Offset +=swprintf(&Buffer[Offset], L"HID_DEVICE") + 1; + + // + // free old buffer + // + ExFreePool((PVOID)Irp->IoStatus.Information); + + // + // allocate buffer + // + Ptr = (LPWSTR)ExAllocatePool(NonPagedPool, (Offset +1)* sizeof(WCHAR)); + if (!Ptr) + { + // + // no memory + // + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy buffer + // + RtlCopyMemory(Ptr, Buffer, Offset * sizeof(WCHAR)); + Ptr[Offset] = UNICODE_NULL; + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)Ptr; + return STATUS_SUCCESS; +} + +NTSTATUS +HidClassPDO_HandleQueryInstanceId( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + LPWSTR Buffer; + PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension; + + // + // get device extension + // + PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + + // + // allocate buffer + // + Buffer = ExAllocatePool(NonPagedPool, 5 * sizeof(WCHAR)); + if (!Buffer) + { + // + // failed + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // write device id + // + swprintf(Buffer, L"%04x", PDODeviceExtension->CollectionNumber); + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +HidClassPDO_HandleQueryCompatibleId( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + LPWSTR Buffer; + + Buffer = (LPWSTR)ExAllocatePool(NonPagedPool, 2 * sizeof(WCHAR)); + if (!Buffer) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // zero buffer + // + Buffer[0] = 0; + Buffer[1] = 0; + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + return STATUS_SUCCESS; +} + +NTSTATUS +HidClassPDO_PnP( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension; + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + PPNP_BUS_INFORMATION BusInformation; + PDEVICE_RELATIONS DeviceRelation; + ULONG Index, bFound; + + // + // get device extension + // + PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // handle request + // + switch(IoStack->MinorFunction) + { + case IRP_MN_QUERY_ID: + { + if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID) + { + // + // handle query device id + // + Status = HidClassPDO_HandleQueryDeviceId(DeviceObject, Irp); + break; + } + else if (IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs) + { + // + // handle instance id + // + Status = HidClassPDO_HandleQueryHardwareId(DeviceObject, Irp); + break; + } + else if (IoStack->Parameters.QueryId.IdType == BusQueryInstanceID) + { + // + // handle instance id + // + Status = HidClassPDO_HandleQueryInstanceId(DeviceObject, Irp); + break; + } + else if (IoStack->Parameters.QueryId.IdType == BusQueryCompatibleIDs) + { + // + // handle instance id + // + Status = HidClassPDO_HandleQueryCompatibleId(DeviceObject, Irp); + break; + } + + DPRINT1("[HIDCLASS]: IRP_MN_QUERY_ID IdType %x unimplemented\n", IoStack->Parameters.QueryId.IdType); + Status = STATUS_NOT_SUPPORTED; + Irp->IoStatus.Information = 0; + break; + } + case IRP_MN_QUERY_CAPABILITIES: + { + if (IoStack->Parameters.DeviceCapabilities.Capabilities == NULL) + { + // + // invalid request + // + Status = STATUS_DEVICE_CONFIGURATION_ERROR; + } + + // + // copy capabilities + // + RtlCopyMemory(IoStack->Parameters.DeviceCapabilities.Capabilities, &PDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES)); + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_BUS_INFORMATION: + { + // + // + // + BusInformation = (PPNP_BUS_INFORMATION)ExAllocatePool(NonPagedPool, sizeof(PNP_BUS_INFORMATION)); + + // + // fill in result + // + RtlCopyMemory(&BusInformation->BusTypeGuid, &GUID_BUS_TYPE_HID, sizeof(GUID)); + BusInformation->LegacyBusType = PNPBus; + BusInformation->BusNumber = 0; //FIXME + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)BusInformation; + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_PNP_DEVICE_STATE: + { + // + // FIXME set flags when driver fails / disabled + // + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + // + // only target relations are supported + // + if (IoStack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation) + { + // + // not supported + // + Status = Irp->IoStatus.Status; + break; + } + + // + // allocate device relations + // + DeviceRelation = (PDEVICE_RELATIONS)ExAllocatePool(NonPagedPool, sizeof(DEVICE_RELATIONS)); + if (!DeviceRelation) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // init device relation + // + DeviceRelation->Count = 1; + DeviceRelation->Objects[0] = DeviceObject; + ObReferenceObject(DeviceRelation->Objects[0]); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelation; + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_START_DEVICE: + { + // + // FIXME: support polled devices + // + ASSERT(PDODeviceExtension->Common.DriverExtension->DevicesArePolled == FALSE); + + // + // now register the device interface + // + Status = IoRegisterDeviceInterface(PDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject, &GUID_DEVINTERFACE_HID, NULL, &PDODeviceExtension->DeviceInterface); + DPRINT("[HIDCLASS] IoRegisterDeviceInterfaceState Status %x\n", Status); + if (NT_SUCCESS(Status)) + { + // + // enable device interface + // + Status = IoSetDeviceInterfaceState(&PDODeviceExtension->DeviceInterface, TRUE); + DPRINT("[HIDCLASS] IoSetDeviceInterFaceState %x\n", Status); + } + + // + // done + // + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_REMOVE_DEVICE: + { + /* Disable the device interface */ + if (PDODeviceExtension->DeviceInterface.Length != 0) + IoSetDeviceInterfaceState(&PDODeviceExtension->DeviceInterface, FALSE); + + // + // remove us from the fdo's pdo list + // + bFound = FALSE; + for(Index = 0; Index < PDODeviceExtension->FDODeviceExtension->DeviceRelations->Count; Index++) + { + if (PDODeviceExtension->FDODeviceExtension->DeviceRelations->Objects[Index] == DeviceObject) + { + // + // remove us + // + bFound = TRUE; + PDODeviceExtension->FDODeviceExtension->DeviceRelations->Objects[Index] = NULL; + break; + } + } + + /* Complete the IRP */ + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + if (bFound) + { + /* Delete our device object*/ + IoDeleteDevice(DeviceObject); + } + + return STATUS_SUCCESS; + } + case IRP_MN_QUERY_INTERFACE: + { + DPRINT1("[HIDCLASS] PDO IRP_MN_QUERY_INTERFACE not implemented\n"); + ASSERT(FALSE); + + // + // do nothing + // + Status = Irp->IoStatus.Status; + break; + } + case IRP_MN_QUERY_REMOVE_DEVICE: + case IRP_MN_CANCEL_STOP_DEVICE: + case IRP_MN_QUERY_STOP_DEVICE: + case IRP_MN_CANCEL_REMOVE_DEVICE: + { + // + // no/op + // + Status = STATUS_SUCCESS; + break; + } + default: + { + // + // do nothing + // + Status = Irp->IoStatus.Status; + break; + } + } + + // + // complete request + // + if (Status != STATUS_PENDING) + { + // + // store result + // + Irp->IoStatus.Status = Status; + + // + // complete request + // + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + // + // done processing + // + return Status; +} + +NTSTATUS +HidClassPDO_CreatePDO( + IN PDEVICE_OBJECT DeviceObject, + OUT PDEVICE_RELATIONS *OutDeviceRelations) +{ + PHIDCLASS_FDO_EXTENSION FDODeviceExtension; + NTSTATUS Status; + PDEVICE_OBJECT PDODeviceObject; + PHIDCLASS_PDO_DEVICE_EXTENSION PDODeviceExtension; + ULONG Index; + PDEVICE_RELATIONS DeviceRelations; + ULONG Length; + + // + // get device extension + // + FDODeviceExtension = (PHIDCLASS_FDO_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // first allocate device relations + // + Length = sizeof(DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT) * FDODeviceExtension->Common.DeviceDescription.CollectionDescLength; + DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(NonPagedPool, Length); + if (!DeviceRelations) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // zero device relations + // + RtlZeroMemory(DeviceRelations, Length); + + // + // lets create a PDO for top level collection + // + Index = 0; + do + { + // + // lets create the device object + // + Status = IoCreateDevice(FDODeviceExtension->Common.DriverExtension->DriverObject, sizeof(HIDCLASS_PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_UNKNOWN, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &PDODeviceObject); + if (!NT_SUCCESS(Status)) + { + // + // failed to create device + // + DPRINT1("[HIDCLASS] Failed to create PDO %x\n", Status); + break; + } + + // + // patch stack size + // + PDODeviceObject->StackSize = DeviceObject->StackSize + 1; + + // + // get device extension + // + PDODeviceExtension = (PHIDCLASS_PDO_DEVICE_EXTENSION)PDODeviceObject->DeviceExtension; + + // + // init device extension + // + PDODeviceExtension->Common.HidDeviceExtension.MiniDeviceExtension = FDODeviceExtension->Common.HidDeviceExtension.MiniDeviceExtension; + PDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject = FDODeviceExtension->Common.HidDeviceExtension.NextDeviceObject; + PDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject = FDODeviceExtension->Common.HidDeviceExtension.PhysicalDeviceObject; + PDODeviceExtension->Common.IsFDO = FALSE; + PDODeviceExtension->FDODeviceExtension = FDODeviceExtension; + PDODeviceExtension->FDODeviceObject = DeviceObject; + PDODeviceExtension->Common.DriverExtension = FDODeviceExtension->Common.DriverExtension; + PDODeviceExtension->CollectionNumber = FDODeviceExtension->Common.DeviceDescription.CollectionDesc[Index].CollectionNumber; + + // + // copy device data + // + RtlCopyMemory(&PDODeviceExtension->Common.Attributes, &FDODeviceExtension->Common.Attributes, sizeof(HID_DEVICE_ATTRIBUTES)); + RtlCopyMemory(&PDODeviceExtension->Common.DeviceDescription, &FDODeviceExtension->Common.DeviceDescription, sizeof(HIDP_DEVICE_DESC)); + RtlCopyMemory(&PDODeviceExtension->Capabilities, &FDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES)); + + // + // set device flags + // + PDODeviceObject->Flags |= DO_MAP_IO_BUFFER; + + // + // device is initialized + // + PDODeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + // + // store device object in device relations + // + DeviceRelations->Objects[Index] = PDODeviceObject; + DeviceRelations->Count++; + + // + // move to next + // + Index++; + + }while(Index < FDODeviceExtension->Common.DeviceDescription.CollectionDescLength); + + + // + // check if creating succeeded + // + if (!NT_SUCCESS(Status)) + { + // + // failed + // + for(Index = 0; Index < DeviceRelations->Count; Index++) + { + // + // delete device + // + IoDeleteDevice(DeviceRelations->Objects[Index]); + } + + // + // free device relations + // + ExFreePool(DeviceRelations); + return Status; + } + + // + // store device relations + // + *OutDeviceRelations = DeviceRelations; + + // + // done + // + return STATUS_SUCCESS; +} \ No newline at end of file diff --git a/reactos/drivers/hid/hidclass/precomp.h b/reactos/drivers/hid/hidclass/precomp.h new file mode 100644 index 00000000000..23820e6938a --- /dev/null +++ b/reactos/drivers/hid/hidclass/precomp.h @@ -0,0 +1,202 @@ +#pragma once + +#define _HIDPI_NO_FUNCTION_MACROS_ +#define NDEBUG +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + PDRIVER_OBJECT DriverObject; + ULONG DeviceExtensionSize; + BOOLEAN DevicesArePolled; + PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; + PDRIVER_ADD_DEVICE AddDevice; + PDRIVER_UNLOAD DriverUnload; + KSPIN_LOCK Lock; + +}HIDCLASS_DRIVER_EXTENSION, *PHIDCLASS_DRIVER_EXTENSION; + +typedef struct +{ + // + // hid device extension + // + HID_DEVICE_EXTENSION HidDeviceExtension; + + // + // if it is a pdo + // + BOOLEAN IsFDO; + + // + // driver extension + // + PHIDCLASS_DRIVER_EXTENSION DriverExtension; + + // + // device description + // + HIDP_DEVICE_DESC DeviceDescription; + + // + // hid attributes + // + HID_DEVICE_ATTRIBUTES Attributes; + +}HIDCLASS_COMMON_DEVICE_EXTENSION, *PHIDCLASS_COMMON_DEVICE_EXTENSION; + +typedef struct +{ + // + // parts shared by fdo and pdo + // + HIDCLASS_COMMON_DEVICE_EXTENSION Common; + + // + // device capabilities + // + DEVICE_CAPABILITIES Capabilities; + + // + // hid descriptor + // + HID_DESCRIPTOR HidDescriptor; + + // + // report descriptor + // + PUCHAR ReportDescriptor; + + // + // device relations + // + PDEVICE_RELATIONS DeviceRelations; + +}HIDCLASS_FDO_EXTENSION, *PHIDCLASS_FDO_EXTENSION; + +typedef struct +{ + // + // parts shared by fdo and pdo + // + HIDCLASS_COMMON_DEVICE_EXTENSION Common; + + // + // device capabilities + // + DEVICE_CAPABILITIES Capabilities; + + // + // collection index + // + ULONG CollectionNumber; + + // + // device interface + // + UNICODE_STRING DeviceInterface; + + // + // FDO device object + // + PDEVICE_OBJECT FDODeviceObject; + + // + // fdo device extension + // + PHIDCLASS_FDO_EXTENSION FDODeviceExtension; + +}HIDCLASS_PDO_DEVICE_EXTENSION, *PHIDCLASS_PDO_DEVICE_EXTENSION; + +typedef struct __HIDCLASS_FILEOP_CONTEXT__ +{ + // + // device extension + // + PHIDCLASS_PDO_DEVICE_EXTENSION DeviceExtension; + + // + // spin lock + // + KSPIN_LOCK Lock; + + // + // read irp pending list + // + LIST_ENTRY ReadPendingIrpListHead; + + // + // completed irp list + // + LIST_ENTRY IrpCompletedListHead; + +}HIDCLASS_FILEOP_CONTEXT, *PHIDCLASS_FILEOP_CONTEXT; + +typedef struct +{ + // + // original request + // + PIRP OriginalIrp; + + // + // file op + // + PHIDCLASS_FILEOP_CONTEXT FileOp; + + // + // buffer for reading report + // + PVOID InputReportBuffer; + + // + // buffer length + // + ULONG InputReportBufferLength; + + // + // work item + // + PIO_WORKITEM CompletionWorkItem; + +}HIDCLASS_IRP_CONTEXT, *PHIDCLASS_IRP_CONTEXT; + +/* fdo.c */ +NTSTATUS +HidClassFDO_PnP( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +HidClassFDO_DispatchRequestSynchronous( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +/* pdo.c */ +NTSTATUS +HidClassPDO_CreatePDO( + IN PDEVICE_OBJECT DeviceObject, + OUT PDEVICE_RELATIONS *OutDeviceRelations); + +NTSTATUS +HidClassPDO_PnP( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +PHIDP_COLLECTION_DESC +HidClassPDO_GetCollectionDescription( + PHIDP_DEVICE_DESC DeviceDescription, + ULONG CollectionNumber); + +PHIDP_REPORT_IDS +HidClassPDO_GetReportDescription( + PHIDP_DEVICE_DESC DeviceDescription, + ULONG CollectionNumber); + +/* eof */ diff --git a/reactos/drivers/hid/hidparse/CMakeLists.txt b/reactos/drivers/hid/hidparse/CMakeLists.txt new file mode 100644 index 00000000000..52f388da794 --- /dev/null +++ b/reactos/drivers/hid/hidparse/CMakeLists.txt @@ -0,0 +1,18 @@ + +spec2def(hidparse.sys hidparse.spec) +add_definitions(-DDEBUG_MODE) + +include_directories( + ${REACTOS_SOURCE_DIR}/ntoskrnl/include + ${REACTOS_SOURCE_DIR}/lib/drivers/hidparser) + + +add_library(hidparse SHARED hidparse.c hidparse.rc ${CMAKE_CURRENT_BINARY_DIR}/hidparse.def) + +set_module_type(hidparse kernelmodedriver) +add_importlibs(hidparse ntoskrnl) +add_importlib_target(hidparse.spec) + +target_link_libraries(hidparse hidparser) + +add_cd_file(TARGET hidparse DESTINATION reactos/system32/drivers NO_CAB FOR all) \ No newline at end of file diff --git a/reactos/drivers/hid/hidparse/hidparse.c b/reactos/drivers/hid/hidparse/hidparse.c new file mode 100644 index 00000000000..d44310630d8 --- /dev/null +++ b/reactos/drivers/hid/hidparse/hidparse.c @@ -0,0 +1,645 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/hidparse/hidparse.c + * PURPOSE: HID Parser + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "hidparse.h" + +PVOID +NTAPI +AllocFunction( + IN ULONG ItemSize) +{ + PVOID Item = ExAllocatePool(NonPagedPool, ItemSize); + if (Item) + { + // + // zero item + // + RtlZeroMemory(Item, ItemSize); + } + + // + // done + // + return Item; +} + +VOID +NTAPI +FreeFunction( + IN PVOID Item) +{ + // + // free item + // + ExFreePool(Item); +} + +VOID +NTAPI +ZeroFunction( + IN PVOID Item, + IN ULONG ItemSize) +{ + // + // zero item + // + RtlZeroMemory(Item, ItemSize); +} + +VOID +NTAPI +CopyFunction( + IN PVOID Target, + IN PVOID Source, + IN ULONG Length) +{ + // + // copy item + // + RtlCopyMemory(Target, Source, Length); +} + +VOID +NTAPI +DebugFunction( + IN LPCSTR FormatStr, ...) +{ + + va_list args; + unsigned int i; + char printbuffer[1024]; + + va_start(args, FormatStr); + i = vsprintf(printbuffer, FormatStr, args); + va_end(args); + + DbgPrint(printbuffer); +} + +VOID +NTAPI +HidP_FreeCollectionDescription ( + IN PHIDP_DEVICE_DESC DeviceDescription) +{ + HID_PARSER Parser; + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, &Parser); + + // + // free collection + // + HidParser_FreeCollectionDescription(&Parser, DeviceDescription); +} + + +HIDAPI +NTSTATUS +NTAPI +HidP_GetCaps( + IN PHIDP_PREPARSED_DATA PreparsedData, + OUT PHIDP_CAPS Capabilities) +{ + HID_PARSER Parser; + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, &Parser); + + // + // get caps + // + return HidParser_GetCaps(&Parser, PreparsedData, Capabilities); +} + +NTSTATUS +NTAPI +HidP_GetCollectionDescription( + IN PHIDP_REPORT_DESCRIPTOR ReportDesc, + IN ULONG DescLength, + IN POOL_TYPE PoolType, + OUT PHIDP_DEVICE_DESC DeviceDescription) +{ + HID_PARSER Parser; + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, &Parser); + + // + // get description; + // + return HidParser_GetCollectionDescription(&Parser, ReportDesc, DescLength, PoolType, DeviceDescription); +} + +HIDAPI +ULONG +NTAPI +HidP_MaxUsageListLength( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage OPTIONAL, + IN PHIDP_PREPARSED_DATA PreparsedData) +{ + HID_PARSER Parser; + + // + // sanity check + // + ASSERT(ReportType == HidP_Input || ReportType == HidP_Output || ReportType == HidP_Feature); + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, &Parser); + + + // + // get usage length + // + return HidParser_MaxUsageListLength(&Parser, PreparsedData, ReportType, UsagePage); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetSpecificValueCaps( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PULONG ValueCapsLength, + IN PHIDP_PREPARSED_DATA PreparsedData) +{ + HID_PARSER Parser; + + // + // sanity check + // + ASSERT(ReportType == HidP_Input || ReportType == HidP_Output || ReportType == HidP_Feature); + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, &Parser); + + // + // get value caps + // + return HidParser_GetSpecificValueCaps(&Parser, PreparsedData, ReportType, UsagePage, LinkCollection, Usage, ValueCaps, ValueCapsLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetUsages( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + OUT USAGE *UsageList, + IN OUT ULONG *UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + HID_PARSER Parser; + + // + // sanity check + // + ASSERT(ReportType == HidP_Input || ReportType == HidP_Output || ReportType == HidP_Feature); + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, &Parser); + + // + // get usages + // + return HidParser_GetUsages(&Parser, PreparsedData, ReportType, UsagePage, LinkCollection, UsageList, UsageLength, Report, ReportLength); +} + + +#undef HidP_GetButtonCaps + +HIDAPI +NTSTATUS +NTAPI +HidP_UsageListDifference( + IN PUSAGE PreviousUsageList, + IN PUSAGE CurrentUsageList, + OUT PUSAGE BreakUsageList, + OUT PUSAGE MakeUsageList, + IN ULONG UsageListLength) +{ + return HidParser_UsageListDifference(PreviousUsageList, CurrentUsageList, BreakUsageList, MakeUsageList, UsageListLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetUsagesEx( + IN HIDP_REPORT_TYPE ReportType, + IN USHORT LinkCollection, + OUT PUSAGE_AND_PAGE ButtonList, + IN OUT ULONG *UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + return HidP_GetUsages(ReportType, HID_USAGE_PAGE_UNDEFINED, LinkCollection, (PUSAGE)ButtonList, UsageLength, PreparsedData, Report, ReportLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_UsageAndPageListDifference( + IN PUSAGE_AND_PAGE PreviousUsageList, + IN PUSAGE_AND_PAGE CurrentUsageList, + OUT PUSAGE_AND_PAGE BreakUsageList, + OUT PUSAGE_AND_PAGE MakeUsageList, + IN ULONG UsageListLength) +{ + return HidParser_UsageAndPageListDifference(PreviousUsageList, CurrentUsageList, BreakUsageList, MakeUsageList, UsageListLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetScaledUsageValue( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + OUT PLONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + HID_PARSER Parser; + + // + // sanity check + // + ASSERT(ReportType == HidP_Input || ReportType == HidP_Output || ReportType == HidP_Feature); + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, &Parser); + + // + // get scaled usage value + // + return HidParser_GetScaledUsageValue(&Parser, PreparsedData, ReportType, UsagePage, LinkCollection, Usage, UsageValue, Report, ReportLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_TranslateUsageAndPagesToI8042ScanCodes( + IN PUSAGE_AND_PAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext) +{ + HID_PARSER Parser; + + // + // init parser + // + HidParser_InitParser(AllocFunction, FreeFunction, ZeroFunction, CopyFunction, DebugFunction, &Parser); + + // + // translate usage pages + // + return HidParser_TranslateUsageAndPagesToI8042ScanCodes(&Parser, ChangedUsageList, UsageListLength, KeyAction, ModifierState, InsertCodesProcedure, InsertCodesContext); +} + + + +HIDAPI +NTSTATUS +NTAPI +HidP_GetButtonCaps( + HIDP_REPORT_TYPE ReportType, + PHIDP_BUTTON_CAPS ButtonCaps, + PUSHORT ButtonCapsLength, + PHIDP_PREPARSED_DATA PreparsedData) +{ + return HidP_GetSpecificButtonCaps(ReportType, HID_USAGE_PAGE_UNDEFINED, 0, 0, ButtonCaps, (PULONG)ButtonCapsLength, PreparsedData); +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetSpecificButtonCaps( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PHIDP_BUTTON_CAPS ButtonCaps, + IN OUT PULONG ButtonCapsLength, + IN PHIDP_PREPARSED_DATA PreparsedData) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetData( + IN HIDP_REPORT_TYPE ReportType, + OUT PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetExtendedAttributes( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN PHIDP_PREPARSED_DATA PreparsedData, + OUT PHIDP_EXTENDED_ATTRIBUTES Attributes, + IN OUT PULONG LengthAttributes) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetLinkCollectionNodes( + OUT PHIDP_LINK_COLLECTION_NODE LinkCollectionNodes, + IN OUT PULONG LinkCollectionNodesLength, + IN PHIDP_PREPARSED_DATA PreparsedData) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetUsageValue( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PULONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +HidP_SysPowerEvent ( + IN PCHAR HidPacket, + IN USHORT HidPacketLength, + IN PHIDP_PREPARSED_DATA Ppd, + OUT PULONG OutputBuffer) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +HidP_SysPowerCaps ( + IN PHIDP_PREPARSED_DATA Ppd, + OUT PULONG OutputBuffer) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_GetUsageValueArray( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + OUT PCHAR UsageValue, + IN USHORT UsageValueByteLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + + +HIDAPI +NTSTATUS +NTAPI +HidP_UnsetUsages( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_TranslateUsagesToI8042ScanCodes( + IN PUSAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_SetUsages( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_SetUsageValueArray( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + IN PCHAR UsageValue, + IN USHORT UsageValueByteLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_SetUsageValue( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + IN ULONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_SetScaledUsageValue( + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + IN LONG UsageValue, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_SetData( + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +ULONG +NTAPI +HidP_MaxDataListLength( + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_PREPARSED_DATA PreparsedData) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidP_InitializeReportForID( + IN HIDP_REPORT_TYPE ReportType, + IN UCHAR ReportID, + IN PHIDP_PREPARSED_DATA PreparsedData, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +#undef HidP_GetValueCaps + +HIDAPI +NTSTATUS +NTAPI +HidP_GetValueCaps( + HIDP_REPORT_TYPE ReportType, + PHIDP_VALUE_CAPS ValueCaps, + PULONG ValueCapsLength, + PHIDP_PREPARSED_DATA PreparsedData) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegPath) +{ + + DPRINT("********* HID PARSE *********\n"); + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/hid/hidparse/hidparse.h b/reactos/drivers/hid/hidparse/hidparse.h new file mode 100644 index 00000000000..45202cb0014 --- /dev/null +++ b/reactos/drivers/hid/hidparse/hidparse.h @@ -0,0 +1,11 @@ +#pragma once + +#define _HIDPI_ +#define _HIDPI_NO_FUNCTION_MACROS_ +#include +#include +#include +#define NDEBUG +#include +#include "hidparser.h" +#include \ No newline at end of file diff --git a/reactos/drivers/hid/hidparse/hidparse.rbuild b/reactos/drivers/hid/hidparse/hidparse.rbuild new file mode 100644 index 00000000000..6cc8ed3677a --- /dev/null +++ b/reactos/drivers/hid/hidparse/hidparse.rbuild @@ -0,0 +1,13 @@ + + + + + + + include + lib/drivers/hidparser + ntoskrnl + hidparser + hidparse.c + hidparse.rc + diff --git a/reactos/drivers/hid/hidparse/hidparse.rc b/reactos/drivers/hid/hidparse/hidparse.rc new file mode 100644 index 00000000000..3da21c341f6 --- /dev/null +++ b/reactos/drivers/hid/hidparse/hidparse.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "USB HID Parser\0" +#define REACTOS_STR_INTERNAL_NAME "hidparse\0" +#define REACTOS_STR_ORIGINAL_FILENAME "hidparse.sys\0" +#include diff --git a/reactos/drivers/hid/hidparse/hidparse.spec b/reactos/drivers/hid/hidparse/hidparse.spec new file mode 100644 index 00000000000..73afdfa3d6c --- /dev/null +++ b/reactos/drivers/hid/hidparse/hidparse.spec @@ -0,0 +1,53 @@ +@ stdcall HidP_FreeCollectionDescription(ptr) +@ stdcall HidP_GetButtonCaps(long ptr ptr ptr) +@ stdcall HidP_GetCaps(ptr ptr) +@ stdcall HidP_GetCollectionDescription(ptr long long ptr) +@ stdcall HidP_GetData(long ptr ptr ptr ptr long) +@ stdcall HidP_GetExtendedAttributes(long long ptr ptr long) +@ stdcall HidP_GetLinkCollectionNodes(ptr ptr ptr) +@ stdcall HidP_GetScaledUsageValue(long long long long ptr ptr ptr long) +@ stdcall HidP_GetSpecificButtonCaps(long long long long ptr ptr ptr) +@ stdcall HidP_GetSpecificValueCaps(long long long long ptr ptr ptr) +@ stdcall HidP_GetUsageValue(long long long long ptr ptr ptr long) +@ stdcall HidP_GetUsageValueArray(long long long long ptr long ptr ptr long) +@ stdcall HidP_GetUsages(long long long ptr ptr ptr ptr long) +@ stdcall HidP_GetUsagesEx(long long ptr ptr ptr ptr long) +@ stdcall HidP_GetValueCaps(long ptr ptr ptr) +@ stdcall HidP_InitializeReportForID(long long ptr ptr long) +@ stdcall HidP_MaxDataListLength(long ptr) +@ stdcall HidP_MaxUsageListLength(long long ptr) +@ stdcall HidP_SetData(long ptr ptr ptr ptr long) +@ stdcall HidP_SetScaledUsageValue(long long long long long ptr ptr long) +@ stdcall HidP_SetUsageValue(long long long long long ptr ptr long) +@ stdcall HidP_SetUsageValueArray(long long long long ptr long long ptr long) +@ stdcall HidP_SetUsages(long long long ptr ptr ptr ptr long) +@ stdcall HidP_SysPowerCaps(ptr ptr) +@ stdcall HidP_SysPowerEvent(ptr long ptr ptr) +@ stdcall HidP_TranslateUsageAndPagesToI8042ScanCodes(ptr long long ptr ptr ptr) +@ stdcall HidP_TranslateUsagesToI8042ScanCodes(ptr long long ptr ptr ptr) +@ stdcall HidP_UnsetUsages(long long long ptr ptr ptr ptr long) +@ stdcall HidP_UsageAndPageListDifference(ptr ptr ptr ptr long) +@ stdcall HidP_UsageListDifference(ptr ptr ptr ptr long) + + + + + + + + + + + + + + + + + + + + + + + diff --git a/reactos/drivers/hid/hidusb/CMakeLists.txt b/reactos/drivers/hid/hidusb/CMakeLists.txt new file mode 100644 index 00000000000..88e3700d556 --- /dev/null +++ b/reactos/drivers/hid/hidusb/CMakeLists.txt @@ -0,0 +1,11 @@ + +list(APPEND SOURCE + hidusb.c + hidusb.rc) + +add_library(hidusb SHARED ${SOURCE}) + +set_module_type(hidusb kernelmodedriver) +add_importlibs(hidusb hidclass ntoskrnl usbd hal) + +add_cd_file(TARGET hidusb DESTINATION reactos/system32/drivers NO_CAB FOR all) \ No newline at end of file diff --git a/reactos/drivers/hid/hidusb/hidusb.c b/reactos/drivers/hid/hidusb/hidusb.c new file mode 100644 index 00000000000..a862045525b --- /dev/null +++ b/reactos/drivers/hid/hidusb/hidusb.c @@ -0,0 +1,1814 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Human Interface Device Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/hidusb/hidusb.c + * PURPOSE: HID USB Interface Driver + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "hidusb.h" + +PUSBD_PIPE_INFORMATION +HidUsb_GetInputInterruptInterfaceHandle( + PUSBD_INTERFACE_INFORMATION InterfaceInformation) +{ + ULONG Index; + + // + // sanity check + // + ASSERT(InterfaceInformation->NumberOfPipes); + + for(Index = 0; Index < InterfaceInformation->NumberOfPipes; Index++) + { + //DPRINT1("[HIDUSB] EndpointAddress %x PipeType %x PipeHandle %x\n", InterfaceInformation->Pipes[Index].EndpointAddress, InterfaceInformation->Pipes[Index].PipeType, InterfaceInformation->Pipes[Index].PipeHandle); + if (InterfaceInformation->Pipes[Index].PipeType == UsbdPipeTypeInterrupt && (InterfaceInformation->Pipes[Index].EndpointAddress & USB_ENDPOINT_DIRECTION_MASK)) + { + // + // found handle + // + return &InterfaceInformation->Pipes[Index]; + } + } + + // + // not found + // + return NULL; +} + +NTSTATUS +HidUsb_GetPortStatus( + IN PDEVICE_OBJECT DeviceObject, + IN PULONG PortStatus) +{ + PIRP Irp; + KEVENT Event; + IO_STATUS_BLOCK IoStatus; + PHID_DEVICE_EXTENSION DeviceExtension; + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // init result + // + *PortStatus = 0; + + // + // init event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // build irp + // + Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_GET_PORT_STATUS, DeviceExtension->NextDeviceObject, NULL, 0, NULL, 0, TRUE, &Event, &IoStatus); + if (!Irp) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get stack location + // + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // store result buffer + // + IoStack->Parameters.Others.Argument1 = (PVOID)PortStatus; + + // + // call driver + // + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + // + // wait for completion + // + KeWaitForSingleObject(&Event, Executive, KernelMode, 0, NULL); + return IoStatus.Status; + } + + // + // done + // + return Status; +} + +NTSTATUS +HidUsb_ResetInterruptPipe( + IN PDEVICE_OBJECT DeviceObject) +{ + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + PUSBD_PIPE_INFORMATION PipeInformation; + PURB Urb; + NTSTATUS Status; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // get interrupt pipe handle + // + ASSERT(HidDeviceExtension->InterfaceInfo); + PipeInformation = HidUsb_GetInputInterruptInterfaceHandle(HidDeviceExtension->InterfaceInfo); + ASSERT(PipeInformation); + ASSERT(PipeInformation->PipeHandle); + + // + // allocate urb + // + Urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST)); + if (!Urb) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // init urb + // + RtlZeroMemory(Urb, sizeof(struct _URB_PIPE_REQUEST)); + Urb->UrbHeader.Function = URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL; + Urb->UrbHeader.Length = sizeof(struct _URB_PIPE_REQUEST); + Urb->UrbPipeRequest.PipeHandle = PipeInformation->PipeHandle; + + // + // dispatch request + // + Status = Hid_DispatchUrb(DeviceObject, Urb); + + // + // free urb + // + ExFreePool(Urb); + + // + // done + // + return Status; +} + +NTSTATUS +HidUsb_AbortPipe( + IN PDEVICE_OBJECT DeviceObject) +{ + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + PURB Urb; + NTSTATUS Status; + PUSBD_PIPE_INFORMATION PipeInformation; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // allocate urb + // + Urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST)); + if (!Urb) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get pipe information + // + PipeInformation = HidUsb_GetInputInterruptInterfaceHandle(HidDeviceExtension->InterfaceInfo); + ASSERT(PipeInformation); + ASSERT(PipeInformation->PipeHandle); + + // + // init urb + // + RtlZeroMemory(Urb, sizeof(struct _URB_PIPE_REQUEST)); + Urb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE; + Urb->UrbHeader.Length = sizeof(struct _URB_PIPE_REQUEST); + Urb->UrbPipeRequest.PipeHandle = PipeInformation->PipeHandle; + + // + // dispatch request + // + Status = Hid_DispatchUrb(DeviceObject, Urb); + + // + // free urb + // + ExFreePool(Urb); + + // + // done + // + return Status; +} + +NTSTATUS +HidUsb_ResetPort( + IN PDEVICE_OBJECT DeviceObject) +{ + KEVENT Event; + PIRP Irp; + PHID_DEVICE_EXTENSION DeviceExtension; + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // init event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // build irp + // + Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_RESET_PORT, DeviceExtension->NextDeviceObject, NULL, 0, NULL, 0, TRUE, &Event, &IoStatusBlock); + if (!Irp) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // send the irp + // + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + // + // wait for request completion + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + // + // done + // + return IoStatusBlock.Status; +} + +NTSTATUS +NTAPI +HidCreate( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // sanity check for hidclass driver + // + ASSERT(IoStack->MajorFunction == IRP_MJ_CREATE || IoStack->MajorFunction == IRP_MJ_CLOSE); + + // + // complete request + // + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // informal debug print + // + DPRINT("HIDUSB Request: %x\n", IoStack->MajorFunction); + + // + // done + // + return STATUS_SUCCESS; +} + +VOID +NTAPI +HidUsb_ResetWorkerRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Ctx) +{ + NTSTATUS Status; + ULONG PortStatus; + PHID_USB_RESET_CONTEXT ResetContext; + PHID_DEVICE_EXTENSION DeviceExtension; + + DPRINT("[HIDUSB] ResetWorkerRoutine\n"); + + // + // get context + // + ResetContext = (PHID_USB_RESET_CONTEXT)Ctx; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)ResetContext->DeviceObject->DeviceExtension; + + // + // get port status + // + Status = HidUsb_GetPortStatus(ResetContext->DeviceObject, &PortStatus); + DPRINT("[HIDUSB] ResetWorkerRoutine GetPortStatus %x PortStatus %x\n", Status, PortStatus); + if (NT_SUCCESS(Status)) + { + if (!(PortStatus & USB_PORT_STATUS_ENABLE)) + { + // + // port is disabled + // + Status = HidUsb_ResetInterruptPipe(ResetContext->DeviceObject); + DPRINT1("[HIDUSB] ResetWorkerRoutine ResetPipe %x\n", Status); + } + else + { + // + // abort pipe + // + Status = HidUsb_AbortPipe(ResetContext->DeviceObject); + DPRINT1("[HIDUSB] ResetWorkerRoutine AbortPipe %x\n", Status); + if (NT_SUCCESS(Status)) + { + // + // reset port + // + Status = HidUsb_ResetPort(ResetContext->DeviceObject); + DPRINT1("[HIDUSB] ResetPort %x\n", Status); + if (Status == STATUS_DEVICE_DATA_ERROR) + { + // + // invalidate device state + // + IoInvalidateDeviceState(DeviceExtension->PhysicalDeviceObject); + } + + // + // reset interrupt pipe + // + if (NT_SUCCESS(Status)) + { + // + // reset pipe + // + Status = HidUsb_ResetInterruptPipe(ResetContext->DeviceObject); + DPRINT1("[HIDUSB] ResetWorkerRoutine ResetPipe %x\n", Status); + } + } + } + } + + // + // cleanup + // + ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); + IoFreeWorkItem(ResetContext->WorkItem); + IoCompleteRequest(ResetContext->Irp, IO_NO_INCREMENT); + ExFreePool(ResetContext); +} + + +NTSTATUS +NTAPI +HidUsb_ReadReportCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + PURB Urb; + PHID_USB_RESET_CONTEXT ResetContext; + + // + // get urb + // + Urb = (PURB)Context; + ASSERT(Urb); + + DPRINT("[HIDUSB] HidUsb_ReadReportCompletion %p Status %x Urb Status %x\n", Irp, Irp->IoStatus, Urb->UrbHeader.Status); + + if (Irp->PendingReturned) + { + // + // mark irp pending + // + IoMarkIrpPending(Irp); + } + + // + // did the reading report succeed / cancelled + // + if (NT_SUCCESS(Irp->IoStatus.Status) || Irp->IoStatus.Status == STATUS_CANCELLED || Irp->IoStatus.Status == STATUS_DEVICE_NOT_CONNECTED) + { + // + // store result length + // + Irp->IoStatus.Information = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength; + + // + // FIXME handle error + // + ASSERT(Urb->UrbHeader.Status == USBD_STATUS_SUCCESS); + + // + // free the urb + // + ExFreePool(Context); + + // + // finish completion + // + return STATUS_CONTINUE_COMPLETION; + } + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // allocate reset context + // + ResetContext = (PHID_USB_RESET_CONTEXT)ExAllocatePool(NonPagedPool, sizeof(HID_USB_RESET_CONTEXT)); + if (ResetContext) + { + // + // allocate work item + // + ResetContext->WorkItem = IoAllocateWorkItem(DeviceObject); + if (ResetContext->WorkItem) + { + // + // init reset context + // + ResetContext->Irp = Irp; + ResetContext->DeviceObject = DeviceObject; + + // + // queue the work item + // + IoQueueWorkItem(ResetContext->WorkItem, HidUsb_ResetWorkerRoutine, DelayedWorkQueue, ResetContext); + + // + // free urb + // + ExFreePool(Urb); + + // + // defer completion + // + return STATUS_MORE_PROCESSING_REQUIRED; + } + // + // free context + // + ExFreePool(ResetContext); + } + + // + // free urb + // + ExFreePool(Urb); + + // + // complete request + // + return STATUS_CONTINUE_COMPLETION; +} + + +NTSTATUS +NTAPI +HidUsb_ReadReport( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + PIO_STACK_LOCATION IoStack; + PURB Urb; + PUSBD_PIPE_INFORMATION PipeInformation; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // sanity checks + // + ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength); + ASSERT(Irp->UserBuffer); + ASSERT(HidDeviceExtension->InterfaceInfo); + + // + // get interrupt input pipe + // + PipeInformation = HidUsb_GetInputInterruptInterfaceHandle(HidDeviceExtension->InterfaceInfo); + ASSERT(PipeInformation); + + // + // lets allocate urb + // + Urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER)); + if (!Urb) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // init urb + // + RtlZeroMemory(Urb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER)); + + // + // sanity check + // + ASSERT(Irp->UserBuffer); + ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength); + ASSERT(PipeInformation->PipeHandle); + + // + // build the urb + // + UsbBuildInterruptOrBulkTransferRequest(Urb, + sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), + PipeInformation->PipeHandle, + Irp->UserBuffer, + NULL, + IoStack->Parameters.DeviceIoControl.OutputBufferLength, + USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK, + NULL); + + // + // store configuration handle + // + Urb->UrbHeader.UsbdDeviceHandle = HidDeviceExtension->ConfigurationHandle; + + // + // get next location to setup irp + // + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // init irp for lower driver + // + IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; + IoStack->Parameters.DeviceIoControl.InputBufferLength = 0; + IoStack->Parameters.DeviceIoControl.OutputBufferLength = 0; + IoStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL; + IoStack->Parameters.Others.Argument1 = (PVOID)Urb; + + + // + // set completion routine + // + IoSetCompletionRoutine(Irp, HidUsb_ReadReportCompletion, (PVOID)Urb, TRUE, TRUE, TRUE); + + // + // call driver + // + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); +} + + +NTSTATUS +NTAPI +HidUsb_GetReportDescriptor( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + PVOID Report = NULL; + ULONG BufferLength, Length; + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // sanity checks + // + ASSERT(HidDeviceExtension); + ASSERT(HidDeviceExtension->HidDescriptor); + ASSERT(HidDeviceExtension->HidDescriptor->bNumDescriptors >= 1); + ASSERT(HidDeviceExtension->HidDescriptor->DescriptorList[0].bReportType == HID_REPORT_DESCRIPTOR_TYPE); + ASSERT(HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength > 0); + + // + // FIXME: support old hid version + // + BufferLength = HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength; + Status = Hid_GetDescriptor(DeviceObject, URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), &Report, &BufferLength, HidDeviceExtension->HidDescriptor->DescriptorList[0].bReportType, 0, HidDeviceExtension->InterfaceInfo->InterfaceNumber); + if (!NT_SUCCESS(Status)) + { + // + // failed to get descriptor + // + DPRINT("[HIDUSB] failed to get report descriptor with %x\n", Status); + ASSERT(FALSE); + return Status; + } + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + DPRINT("[HIDUSB] GetReportDescriptor: Status %x ReportLength %lu OutputBufferLength %lu TransferredLength %lu\n", Status, HidDeviceExtension->HidDescriptor->DescriptorList[0].wReportLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength, BufferLength); + + // + // get length to copy + // + Length = min(IoStack->Parameters.DeviceIoControl.OutputBufferLength, BufferLength); + ASSERT(Length); + + // + // copy result + // + RtlCopyMemory(Irp->UserBuffer, Report, Length); + + // + // store result length + // + Irp->IoStatus.Information = Length; + + // + // done + // + return Status; + +} + +NTSTATUS +NTAPI +HidInternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + PHID_DEVICE_ATTRIBUTES Attributes; + ULONG Length; + NTSTATUS Status; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + switch(IoStack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_HID_GET_DEVICE_ATTRIBUTES: + { + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_DEVICE_ATTRIBUTES)) + { + // + // invalid request + // + Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE; + DPRINT1("[HIDUSB] IOCTL_HID_GET_DEVICE_ATTRIBUTES invalid buffer\n"); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_BUFFER_SIZE; + } + // + // store result + // + DPRINT("[HIDUSB] IOCTL_HID_GET_DEVICE_ATTRIBUTES\n"); + ASSERT(HidDeviceExtension->DeviceDescriptor); + Irp->IoStatus.Information = sizeof(HID_DESCRIPTOR); + Attributes = (PHID_DEVICE_ATTRIBUTES)Irp->UserBuffer; + Attributes->Size = sizeof(HID_DEVICE_ATTRIBUTES); + Attributes->VendorID = HidDeviceExtension->DeviceDescriptor->idVendor; + Attributes->ProductID = HidDeviceExtension->DeviceDescriptor->idProduct; + Attributes->VersionNumber = HidDeviceExtension->DeviceDescriptor->bcdDevice; + + // + // complete request + // + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + case IOCTL_HID_GET_DEVICE_DESCRIPTOR: + { + // + // sanity check + // + ASSERT(HidDeviceExtension->HidDescriptor); + DPRINT("[HIDUSB] IOCTL_HID_GET_DEVICE_DESCRIPTOR DescriptorLength %lu OutputBufferLength %lu\n", HidDeviceExtension->HidDescriptor->bLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength); + + // + // store length + // + Length = min(HidDeviceExtension->HidDescriptor->bLength, IoStack->Parameters.DeviceIoControl.OutputBufferLength); + + // + // copy descriptor + // + RtlCopyMemory(Irp->UserBuffer, HidDeviceExtension->HidDescriptor, Length); + + // + // store result length + // + Irp->IoStatus.Information = HidDeviceExtension->HidDescriptor->bLength; + Irp->IoStatus.Status = STATUS_SUCCESS; + + /* complete request */ + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + case IOCTL_HID_GET_REPORT_DESCRIPTOR: + { + Status = HidUsb_GetReportDescriptor(DeviceObject, Irp); + DPRINT("[HIDUSB] IOCTL_HID_GET_REPORT_DESCRIPTOR Status %x\n", Status); + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + case IOCTL_HID_READ_REPORT: + { + DPRINT("[HIDUSB] IOCTL_HID_READ_REPORT\n"); + Status = HidUsb_ReadReport(DeviceObject, Irp); + return Status; + } + case IOCTL_HID_WRITE_REPORT: + { + DPRINT1("[HIDUSB] IOCTL_HID_WRITE_REPORT not implemented \n"); + ASSERT(FALSE); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + case IOCTL_GET_PHYSICAL_DESCRIPTOR: + { + DPRINT1("[HIDUSB] IOCTL_GET_PHYSICAL_DESCRIPTOR not implemented \n"); + ASSERT(FALSE); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST: + { + DPRINT1("[HIDUSB] IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST not implemented \n"); + ASSERT(FALSE); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + case IOCTL_HID_GET_FEATURE: + { + DPRINT1("[HIDUSB] IOCTL_HID_GET_FEATURE not implemented \n"); + ASSERT(FALSE); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + case IOCTL_HID_SET_FEATURE: + { + DPRINT1("[HIDUSB] IOCTL_HID_SET_FEATURE not implemented \n"); + ASSERT(FALSE); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + case IOCTL_HID_SET_OUTPUT_REPORT: + { + DPRINT1("[HIDUSB] IOCTL_HID_SET_OUTPUT_REPORT not implemented \n"); + ASSERT(FALSE); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + case IOCTL_HID_GET_INPUT_REPORT: + { + DPRINT1("[HIDUSB] IOCTL_HID_GET_INPUT_REPORT not implemented \n"); + ASSERT(FALSE); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + case IOCTL_HID_GET_INDEXED_STRING: + { + DPRINT1("[HIDUSB] IOCTL_HID_GET_INDEXED_STRING not implemented \n"); + ASSERT(FALSE); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + case IOCTL_HID_GET_MS_GENRE_DESCRIPTOR: + { + DPRINT1("[HIDUSB] IOCTL_HID_GET_MS_GENRE_DESCRIPTOR not implemented \n"); + ASSERT(FALSE); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + default: + { + UNIMPLEMENTED + ASSERT(FALSE); + Status = Irp->IoStatus.Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + } +} + +NTSTATUS +NTAPI +HidPower( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +HidSystemControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PHID_DEVICE_EXTENSION DeviceExtension; + + // + // get hid device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // copy stack location + // + IoCopyCurrentIrpStackLocationToNext(Irp); + + // + // submit request + // + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); +} + +NTSTATUS +NTAPI +Hid_PnpCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + // + // signal event + // + KeSetEvent((PRKEVENT)Context, 0, FALSE); + + // + // done + // + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +Hid_DispatchUrb( + IN PDEVICE_OBJECT DeviceObject, + IN PURB Urb) +{ + PIRP Irp; + KEVENT Event; + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + IO_STATUS_BLOCK IoStatus; + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + + // + // init event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + + // + // build irp + // + Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_SUBMIT_URB, DeviceExtension->NextDeviceObject, NULL, 0, NULL, 0, TRUE, &Event, &IoStatus); + if (!Irp) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get next stack location + // + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // store urb + // + IoStack->Parameters.Others.Argument1 = (PVOID)Urb; + + // + // set completion routine + // + IoSetCompletionRoutine(Irp, Hid_PnpCompletion, (PVOID)&Event, TRUE, TRUE, TRUE); + + // + // call driver + // + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + + // + // wait for the request to finish + // + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + + // + // complete request + // + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + if (Status == STATUS_PENDING) + { + // + // get final status + // + Status = IoStatus.Status; + } + + DPRINT("[HIDUSB] DispatchUrb %x\n", Status); + + + // + // done + // + return Status; +} + +NTSTATUS +Hid_GetDescriptor( + IN PDEVICE_OBJECT DeviceObject, + IN USHORT UrbFunction, + IN USHORT UrbLength, + IN OUT PVOID *UrbBuffer, + IN OUT PULONG UrbBufferLength, + IN UCHAR DescriptorType, + IN UCHAR Index, + IN USHORT LanguageIndex) +{ + PURB Urb; + NTSTATUS Status; + UCHAR Allocated = FALSE; + + // + // allocate urb + // + Urb = (PURB)ExAllocatePool(NonPagedPool, UrbLength); + if (!Urb) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // is there an urb buffer + // + if (!*UrbBuffer) + { + // + // allocate buffer + // + *UrbBuffer = ExAllocatePool(NonPagedPool, *UrbBufferLength); + if (!*UrbBuffer) + { + // + // no memory + // + ExFreePool(Urb); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // zero buffer + // + RtlZeroMemory(*UrbBuffer, *UrbBufferLength); + Allocated = TRUE; + } + + // + // zero urb + // + RtlZeroMemory(Urb, UrbLength); + + // + // build descriptor request + // + UsbBuildGetDescriptorRequest(Urb, UrbLength, DescriptorType, Index, LanguageIndex, *UrbBuffer, NULL, *UrbBufferLength, NULL); + + // + // set urb function + // + Urb->UrbHeader.Function = UrbFunction; + + // + // dispatch urb + // + Status = Hid_DispatchUrb(DeviceObject, Urb); + + // + // did the request fail + // + if (!NT_SUCCESS(Status)) + { + if (Allocated) + { + // + // free allocated buffer + // + ExFreePool(*UrbBuffer); + *UrbBuffer = NULL; + } + + // + // free urb + // + ExFreePool(Urb); + *UrbBufferLength = 0; + return Status; + } + + // + // did urb request fail + // + if (!NT_SUCCESS(Urb->UrbHeader.Status)) + { + if (Allocated) + { + // + // free allocated buffer + // + ExFreePool(*UrbBuffer); + *UrbBuffer = NULL; + } + + // + // free urb + // + ExFreePool(Urb); + *UrbBufferLength = 0; + return STATUS_UNSUCCESSFUL; + } + + // + // store result length + // + *UrbBufferLength = Urb->UrbControlDescriptorRequest.TransferBufferLength; + + // + // free urb + // + ExFreePool(Urb); + + // + // completed successfully + // + return STATUS_SUCCESS; +} + +NTSTATUS +Hid_SelectConfiguration( + IN PDEVICE_OBJECT DeviceObject) +{ + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + NTSTATUS Status; + USBD_INTERFACE_LIST_ENTRY InterfaceList[2]; + PURB Urb; + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // now parse the descriptors + // + InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(HidDeviceExtension->ConfigurationDescriptor, + HidDeviceExtension->ConfigurationDescriptor, + -1, + -1, + USB_DEVICE_CLASS_HUMAN_INTERFACE, + -1, + -1); + + // + // sanity check + // + ASSERT(InterfaceDescriptor); + ASSERT(InterfaceDescriptor->bInterfaceClass == USB_DEVICE_CLASS_HUMAN_INTERFACE); + ASSERT(InterfaceDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE); + ASSERT(InterfaceDescriptor->bLength == sizeof(USB_INTERFACE_DESCRIPTOR)); + + // + // setup interface list + // + RtlZeroMemory(InterfaceList, sizeof(InterfaceList)); + InterfaceList[0].InterfaceDescriptor = InterfaceDescriptor; + + // + // build urb + // + Urb = USBD_CreateConfigurationRequestEx(HidDeviceExtension->ConfigurationDescriptor, InterfaceList); + if (!Urb) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // dispatch request + // + Status = Hid_DispatchUrb(DeviceObject, Urb); + if (NT_SUCCESS(Status)) + { + // + // store configuration handle + // + HidDeviceExtension->ConfigurationHandle = Urb->UrbSelectConfiguration.ConfigurationHandle; + + // + // copy interface info + // + HidDeviceExtension->InterfaceInfo = (PUSBD_INTERFACE_INFORMATION)ExAllocatePool(NonPagedPool, Urb->UrbSelectConfiguration.Interface.Length); + if (HidDeviceExtension->InterfaceInfo) + { + // + // copy interface info + // + RtlCopyMemory(HidDeviceExtension->InterfaceInfo, &Urb->UrbSelectConfiguration.Interface, Urb->UrbSelectConfiguration.Interface.Length); + } + } + + // + // free urb request + // + ExFreePool(Urb); + + // + // done + // + return Status; +} + +NTSTATUS +Hid_SetIdle( + IN PDEVICE_OBJECT DeviceObject) +{ + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + PURB Urb; + NTSTATUS Status; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // allocate urb + // + Urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST)); + if (!Urb) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // zero urb + // + RtlZeroMemory(Urb, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST)); + + // + // format urb + // + UsbBuildVendorRequest(Urb, + URB_FUNCTION_CLASS_INTERFACE, + sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST), + 0, + 0, + USB_SET_IDLE_REQUEST, // HID_SET_IDLE + 0, + 0, + NULL, + NULL, + 0, + NULL); + + // + // dispatch urb + // + Status = Hid_DispatchUrb(DeviceObject, Urb); + + // + // free urb + // + ExFreePool(Urb); + + // + // print status + // + DPRINT("Status %x\n", Status); + return Status; +} + + +NTSTATUS +Hid_GetProtocol( + IN PDEVICE_OBJECT DeviceObject) +{ + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + PURB Urb; + NTSTATUS Status; + UCHAR Protocol[1]; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // allocate urb + // + Urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST)); + if (!Urb) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // zero urb + // + RtlZeroMemory(Urb, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST)); + + // + // format urb + // + UsbBuildVendorRequest(Urb, + URB_FUNCTION_CLASS_INTERFACE, + sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST), + USBD_TRANSFER_DIRECTION_IN, + 0, + USB_GET_PROTOCOL_REQUEST, + 0, + 0, + Protocol, + NULL, + 1, + NULL); + Protocol[0] = 0xFF; + // + // dispatch urb + // + Status = Hid_DispatchUrb(DeviceObject, Urb); + + // + // free urb + // + ExFreePool(Urb); + + // + // print status + // + DPRINT("Status %x Protocol %x\n", Status, Protocol[0] & 0xFF); + + // + // assert when boot protocol is still active + // + ASSERT(Protocol[0] == 0x1); + return Status; +} + +NTSTATUS +Hid_PnpStart( + IN PDEVICE_OBJECT DeviceObject) +{ + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + NTSTATUS Status; + ULONG DescriptorLength; + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + PHID_DESCRIPTOR HidDescriptor; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // get device descriptor + // + DescriptorLength = sizeof(USB_DEVICE_DESCRIPTOR); + Status = Hid_GetDescriptor(DeviceObject, URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), (PVOID*)&HidDeviceExtension->DeviceDescriptor, &DescriptorLength, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0); + if (!NT_SUCCESS(Status)) + { + // + // failed to obtain device descriptor + // + DPRINT1("[HIDUSB] failed to get device descriptor %x\n", Status); + return Status; + } + + // + // now get the configuration descriptor + // + DescriptorLength = sizeof(USB_CONFIGURATION_DESCRIPTOR); + Status = Hid_GetDescriptor(DeviceObject, URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), (PVOID*)&HidDeviceExtension->ConfigurationDescriptor, &DescriptorLength, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0); + if (!NT_SUCCESS(Status)) + { + // + // failed to obtain device descriptor + // + DPRINT1("[HIDUSB] failed to get device descriptor %x\n", Status); + return Status; + } + + // + // sanity check + // + ASSERT(DescriptorLength); + ASSERT(HidDeviceExtension->ConfigurationDescriptor); + ASSERT(HidDeviceExtension->ConfigurationDescriptor->bLength); + + // + // store full length + // + DescriptorLength = HidDeviceExtension->ConfigurationDescriptor->wTotalLength; + + // + // delete partial configuration descriptor + // + ExFreePool(HidDeviceExtension->ConfigurationDescriptor); + HidDeviceExtension->ConfigurationDescriptor = NULL; + + // + // get full configuration descriptor + // + Status = Hid_GetDescriptor(DeviceObject, URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), (PVOID*)&HidDeviceExtension->ConfigurationDescriptor, &DescriptorLength, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0); + if (!NT_SUCCESS(Status)) + { + // + // failed to obtain device descriptor + // + DPRINT1("[HIDUSB] failed to get device descriptor %x\n", Status); + return Status; + } + + // + // now parse the descriptors + // + InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(HidDeviceExtension->ConfigurationDescriptor, + HidDeviceExtension->ConfigurationDescriptor, + -1, + -1, + USB_DEVICE_CLASS_HUMAN_INTERFACE, + -1, + -1); + if (!InterfaceDescriptor) + { + // + // no interface class + // + DPRINT1("[HIDUSB] HID Class found\n"); + return STATUS_UNSUCCESSFUL; + } + + // + // sanity check + // + ASSERT(InterfaceDescriptor->bInterfaceClass == USB_DEVICE_CLASS_HUMAN_INTERFACE); + ASSERT(InterfaceDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE); + ASSERT(InterfaceDescriptor->bLength == sizeof(USB_INTERFACE_DESCRIPTOR)); + + // + // now set the device idle + // + Hid_SetIdle(DeviceObject); + + Hid_GetProtocol(DeviceObject); + + // + // move to next descriptor + // + HidDescriptor = (PHID_DESCRIPTOR)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength); + ASSERT(HidDescriptor->bLength >= 2); + + // + // check if this is the hid descriptor + // + if (HidDescriptor->bLength == sizeof(HID_DESCRIPTOR) && HidDescriptor->bDescriptorType == HID_HID_DESCRIPTOR_TYPE) + { + // + // found + // + HidDeviceExtension->HidDescriptor = HidDescriptor; + + // + // select configuration + // + Status = Hid_SelectConfiguration(DeviceObject); + ASSERT(Status == STATUS_SUCCESS); + + // + // done + // + DPRINT("[HIDUSB] SelectConfiguration %x\n", Status); + return Status; + } + + // + // FIXME parse hid descriptor + // + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_SUCCESS; +} + + +NTSTATUS +NTAPI +HidPnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + NTSTATUS Status; + PIO_STACK_LOCATION IoStack; + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + KEVENT Event; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + DPRINT("[HIDUSB] Pnp %x\n", IoStack->MinorFunction); + + // + // handle requests based on request type + // + switch(IoStack->MinorFunction) + { + case IRP_MN_REMOVE_DEVICE: + { + // + // pass request onto lower driver + // + IoSkipCurrentIrpStackLocation(Irp); + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + + // + // free resources + // + if (HidDeviceExtension->HidDescriptor) + { + ExFreePool(HidDeviceExtension->HidDescriptor); + HidDeviceExtension->HidDescriptor = NULL; + } + + // + // delete and detach device + // + IoDetachDevice(DeviceExtension->NextDeviceObject); + IoDeleteDevice(DeviceObject); + + return Status; + } + case IRP_MN_QUERY_PNP_DEVICE_STATE: + { + // + // device can not be disabled + // + Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE; + + // + // pass request to next request + // + IoSkipCurrentIrpStackLocation(Irp); + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + + // + // done + // + return Status; + } + case IRP_MN_QUERY_STOP_DEVICE: + case IRP_MN_QUERY_REMOVE_DEVICE: + { + // + // we're fine with it + // + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // pass request to next driver + // + IoSkipCurrentIrpStackLocation(Irp); + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + + // + // done + // + return Status; + } + case IRP_MN_STOP_DEVICE: + { + // + // FIXME: unconfigure the device + // + + // + // prepare irp + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + IoCopyCurrentIrpStackLocationToNext(Irp); + IoSetCompletionRoutine(Irp, Hid_PnpCompletion, (PVOID)&Event, TRUE, TRUE, TRUE); + + // + // send irp and wait for completion + // + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = Irp->IoStatus.Status; + } + + // + // free resources + // + if (HidDeviceExtension->HidDescriptor) + { + ExFreePool(HidDeviceExtension->HidDescriptor); + HidDeviceExtension->HidDescriptor = NULL; + } + + // + // done + // + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + case IRP_MN_QUERY_CAPABILITIES: + { + // + // prepare irp + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + IoCopyCurrentIrpStackLocationToNext(Irp); + IoSetCompletionRoutine(Irp, Hid_PnpCompletion, (PVOID)&Event, TRUE, TRUE, TRUE); + + // + // send irp and wait for completion + // + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = Irp->IoStatus.Status; + } + + // + // don't need to safely remove + // + IoStack->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE; + + // + // done + // + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + case IRP_MN_START_DEVICE: + { + // + // prepare irp + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + IoCopyCurrentIrpStackLocationToNext(Irp); + IoSetCompletionRoutine(Irp, Hid_PnpCompletion, (PVOID)&Event, TRUE, TRUE, TRUE); + + // + // send irp and wait for completion + // + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = Irp->IoStatus.Status; + } + + // + // did the device successfully start + // + if (!NT_SUCCESS(Status)) + { + // + // failed + // + DPRINT1("HIDUSB: IRP_MN_START_DEVICE failed with %x\n", Status); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + + // + // start device + // + Status = Hid_PnpStart(DeviceObject); + + // + // complete request + // + Irp->IoStatus.Status = Status; + DPRINT("[HIDUSB] IRP_MN_START_DEVICE Status %x\n", Status); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + default: + { + // + // forward and forget request + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + } + } +} + +NTSTATUS +NTAPI +HidAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT DeviceObject) +{ + PHID_USB_DEVICE_EXTENSION HidDeviceExtension; + PHID_DEVICE_EXTENSION DeviceExtension; + + // + // get device extension + // + DeviceExtension = (PHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + HidDeviceExtension = (PHID_USB_DEVICE_EXTENSION)DeviceExtension->MiniDeviceExtension; + + // + // init event + // + KeInitializeEvent(&HidDeviceExtension->Event, NotificationEvent, FALSE); + + // + // done + // + return STATUS_SUCCESS; +} + +VOID +NTAPI +Hid_Unload( + IN PDRIVER_OBJECT DriverObject) +{ + UNIMPLEMENTED +} + + +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegPath) +{ + HID_MINIDRIVER_REGISTRATION Registration; + NTSTATUS Status; + + // + // initialize driver object + // + DriverObject->MajorFunction[IRP_MJ_CREATE] = HidCreate; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = HidCreate; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = HidInternalDeviceControl; + DriverObject->MajorFunction[IRP_MJ_POWER] = HidPower; + DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = HidSystemControl; + DriverObject->MajorFunction[IRP_MJ_PNP] = HidPnp; + DriverObject->DriverExtension->AddDevice = HidAddDevice; + DriverObject->DriverUnload = Hid_Unload; + + // + // prepare registration info + // + RtlZeroMemory(&Registration, sizeof(HID_MINIDRIVER_REGISTRATION)); + + // + // fill in registration info + // + Registration.Revision = HID_REVISION; + Registration.DriverObject = DriverObject; + Registration.RegistryPath = RegPath; + Registration.DeviceExtensionSize = sizeof(HID_USB_DEVICE_EXTENSION); + Registration.DevicesArePolled = FALSE; + + // + // register driver + // + Status = HidRegisterMinidriver(&Registration); + + // + // informal debug + // + DPRINT("********* HIDUSB *********\n"); + DPRINT("HIDUSB Registration Status %x\n", Status); + + return Status; +} diff --git a/reactos/drivers/hid/hidusb/hidusb.h b/reactos/drivers/hid/hidusb/hidusb.h new file mode 100644 index 00000000000..816afb8ed33 --- /dev/null +++ b/reactos/drivers/hid/hidusb/hidusb.h @@ -0,0 +1,88 @@ +#pragma once + +#define _HIDPI_ +#define _HIDPI_NO_FUNCTION_MACROS_ +#define NDEBUG +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef struct +{ + // + // event for completion + // + KEVENT Event; + + // + // device descriptor + // + PUSB_DEVICE_DESCRIPTOR DeviceDescriptor; + + // + // configuration descriptor + // + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; + + // + // interface information + // + PUSBD_INTERFACE_INFORMATION InterfaceInfo; + + // + // configuration handle + // + USBD_CONFIGURATION_HANDLE ConfigurationHandle; + + // + // hid descriptor + // + PHID_DESCRIPTOR HidDescriptor; +}HID_USB_DEVICE_EXTENSION, *PHID_USB_DEVICE_EXTENSION; + +typedef struct +{ + // + // request irp + // + PIRP Irp; + + // + // work item + // + PIO_WORKITEM WorkItem; + + // + // device object + // + PDEVICE_OBJECT DeviceObject; + +}HID_USB_RESET_CONTEXT, *PHID_USB_RESET_CONTEXT; + + +NTSTATUS +Hid_GetDescriptor( + IN PDEVICE_OBJECT DeviceObject, + IN USHORT UrbFunction, + IN USHORT UrbLength, + IN OUT PVOID *UrbBuffer, + IN OUT PULONG UrbBufferLength, + IN UCHAR DescriptorType, + IN UCHAR Index, + IN USHORT LanguageIndex); + +NTSTATUS +Hid_DispatchUrb( + IN PDEVICE_OBJECT DeviceObject, + IN PURB Urb); + +#define USB_SET_IDLE_REQUEST 0xA +#define USB_GET_PROTOCOL_REQUEST 0x3 + diff --git a/reactos/drivers/hid/hidusb/hidusb.rbuild b/reactos/drivers/hid/hidusb/hidusb.rbuild new file mode 100644 index 00000000000..fd9c46de598 --- /dev/null +++ b/reactos/drivers/hid/hidusb/hidusb.rbuild @@ -0,0 +1,11 @@ + + + + + ntoskrnl + hidclass + usbd + hal + hidusb.c + hidusb.rc + diff --git a/reactos/drivers/hid/hidusb/hidusb.rc b/reactos/drivers/hid/hidusb/hidusb.rc new file mode 100644 index 00000000000..4ad02a08141 --- /dev/null +++ b/reactos/drivers/hid/hidusb/hidusb.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "USB HID Interface Driver\0" +#define REACTOS_STR_INTERNAL_NAME "hidusb\0" +#define REACTOS_STR_ORIGINAL_FILENAME "hidusb.sys\0" +#include diff --git a/reactos/drivers/hid/kbdhid/CMakeLists.txt b/reactos/drivers/hid/kbdhid/CMakeLists.txt new file mode 100644 index 00000000000..f8667efae35 --- /dev/null +++ b/reactos/drivers/hid/kbdhid/CMakeLists.txt @@ -0,0 +1,11 @@ + +add_definitions(-DDEBUG_MODE) + +include_directories(${REACTOS_SOURCE_DIR}/ntoskrnl/include) + +add_library(kbdhid SHARED kbdhid.c kbdhid.rc) + +set_module_type(kbdhid kernelmodedriver) +add_importlibs(kbdhid ntoskrnl hal hidparse) + +add_cd_file(TARGET kbdhid DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/reactos/drivers/hid/kbdhid/kbdhid.c b/reactos/drivers/hid/kbdhid/kbdhid.c new file mode 100644 index 00000000000..ce9f62665da --- /dev/null +++ b/reactos/drivers/hid/kbdhid/kbdhid.c @@ -0,0 +1,923 @@ +/* + * PROJECT: ReactOS HID Stack + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/hid/kbdhid/kbdhid.c + * PURPOSE: Keyboard HID Driver + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "kbdhid.h" + +VOID +KbdHid_DispatchInputData( + IN PKBDHID_DEVICE_EXTENSION DeviceExtension, + IN PKEYBOARD_INPUT_DATA InputData) +{ + KIRQL OldIrql; + ULONG InputDataConsumed; + + if (!DeviceExtension->ClassService) + return; + + /* sanity check */ + ASSERT(DeviceExtension->ClassService); + ASSERT(DeviceExtension->ClassDeviceObject); + + /* raise irql */ + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + + /* dispatch input data */ + (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->ClassService)(DeviceExtension->ClassDeviceObject, InputData, InputData + 1, &InputDataConsumed); + + /* lower irql to previous level */ + KeLowerIrql(OldIrql); +} + +BOOLEAN +NTAPI +KbdHid_InsertScanCodes( + IN PVOID Context, + IN PCHAR NewScanCodes, + IN ULONG Length) +{ + KEYBOARD_INPUT_DATA InputData; + ULONG Index; + PKBDHID_DEVICE_EXTENSION DeviceExtension; + + /* get device extension */ + DeviceExtension = (PKBDHID_DEVICE_EXTENSION)Context; + + for(Index = 0; Index < Length; Index++) + { + DPRINT("[KBDHID] ScanCode Index %lu ScanCode %x\n", Index, NewScanCodes[Index] & 0xFF); + + /* init input data */ + RtlZeroMemory(&InputData, sizeof(KEYBOARD_INPUT_DATA)); + + /* use keyboard unit id */ + InputData.UnitId = DeviceExtension->KeyboardTypematic.UnitId; + + if (((UCHAR)(NewScanCodes[Index] & 0xFF))> 0x7F) + { + /* scan codes greater than 0x7F are a key break */ + InputData.Flags |= KEY_BREAK; + } + + /* store key code */ + InputData.MakeCode = NewScanCodes[Index]; + + /* dispatch scan codes */ + KbdHid_DispatchInputData((PKBDHID_DEVICE_EXTENSION)Context, &InputData); + } + + /* done */ + return TRUE; +} + + +NTSTATUS +NTAPI +KbdHid_ReadCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + PKBDHID_DEVICE_EXTENSION DeviceExtension; + NTSTATUS Status; + ULONG ButtonLength; + + /* get device extension */ + DeviceExtension = (PKBDHID_DEVICE_EXTENSION)Context; + + if (Irp->IoStatus.Status == STATUS_PRIVILEGE_NOT_HELD || + Irp->IoStatus.Status == STATUS_DEVICE_NOT_CONNECTED || + Irp->IoStatus.Status == STATUS_CANCELLED || + DeviceExtension->StopReadReport) + { + /* failed to read or should be stopped*/ + DPRINT1("[KBDHID] ReadCompletion terminating read Status %x\n", Irp->IoStatus.Status); + + /* report no longer active */ + DeviceExtension->ReadReportActive = FALSE; + + /* request stopping of the report cycle */ + DeviceExtension->StopReadReport = FALSE; + + /* signal completion event */ + KeSetEvent(&DeviceExtension->ReadCompletionEvent, 0, 0); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // print out raw report + // + ASSERT(DeviceExtension->ReportLength >= 9); + DPRINT("[KBDHID] ReadCompletion %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", DeviceExtension->Report[0], DeviceExtension->Report[1], DeviceExtension->Report[2], + DeviceExtension->Report[3], DeviceExtension->Report[4], DeviceExtension->Report[5], + DeviceExtension->Report[6], DeviceExtension->Report[7], DeviceExtension->Report[8]); + + + /* get current usages */ + ButtonLength = DeviceExtension->UsageListLength; + Status = HidP_GetUsagesEx(HidP_Input, HIDP_LINK_COLLECTION_UNSPECIFIED, DeviceExtension->CurrentUsageList, &ButtonLength, DeviceExtension->PreparsedData, DeviceExtension->Report, DeviceExtension->ReportLength); + ASSERT(Status == HIDP_STATUS_SUCCESS); + + /* FIXME check if needs mapping */ + + /* get usage difference */ + Status = HidP_UsageAndPageListDifference(DeviceExtension->PreviousUsageList, DeviceExtension->CurrentUsageList, DeviceExtension->BreakUsageList, DeviceExtension->MakeUsageList, DeviceExtension->UsageListLength); + ASSERT(Status == HIDP_STATUS_SUCCESS); + + /* replace previous usage list with current list */ + RtlMoveMemory(DeviceExtension->PreviousUsageList, DeviceExtension->CurrentUsageList, sizeof(USAGE_AND_PAGE) * DeviceExtension->UsageListLength); + + /* translate break usage list */ + HidP_TranslateUsageAndPagesToI8042ScanCodes(DeviceExtension->BreakUsageList, DeviceExtension->UsageListLength, HidP_Keyboard_Break, &DeviceExtension->ModifierState, KbdHid_InsertScanCodes, DeviceExtension); + ASSERT(Status == HIDP_STATUS_SUCCESS); + + /* translate new usage list */ + HidP_TranslateUsageAndPagesToI8042ScanCodes(DeviceExtension->MakeUsageList, DeviceExtension->UsageListLength, HidP_Keyboard_Make, &DeviceExtension->ModifierState, KbdHid_InsertScanCodes, DeviceExtension); + ASSERT(Status == HIDP_STATUS_SUCCESS); + + /* re-init read */ + KbdHid_InitiateRead(DeviceExtension); + + /* stop completion */ + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +KbdHid_InitiateRead( + IN PKBDHID_DEVICE_EXTENSION DeviceExtension) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + + /* re-use irp */ + IoReuseIrp(DeviceExtension->Irp, STATUS_SUCCESS); + + /* init irp */ + DeviceExtension->Irp->MdlAddress = DeviceExtension->ReportMDL; + + /* get next stack location */ + IoStack = IoGetNextIrpStackLocation(DeviceExtension->Irp); + + /* init stack location */ + IoStack->Parameters.Read.Length = DeviceExtension->ReportLength; + IoStack->Parameters.Read.Key = 0; + IoStack->Parameters.Read.ByteOffset.QuadPart = 0LL; + IoStack->MajorFunction = IRP_MJ_READ; + IoStack->FileObject = DeviceExtension->FileObject; + + /* set completion routine */ + IoSetCompletionRoutine(DeviceExtension->Irp, KbdHid_ReadCompletion, DeviceExtension, TRUE, TRUE, TRUE); + + /* read is active */ + DeviceExtension->ReadReportActive = TRUE; + + /* start the read */ + Status = IoCallDriver(DeviceExtension->NextDeviceObject, DeviceExtension->Irp); + + /* done */ + return Status; +} + +NTSTATUS +NTAPI +KbdHid_CreateCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + KeSetEvent((PKEVENT)Context, 0, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +NTAPI +KbdHid_Create( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + KEVENT Event; + PKBDHID_DEVICE_EXTENSION DeviceExtension; + + DPRINT("[KBDHID]: IRP_MJ_CREATE\n"); + + /* get device extension */ + DeviceExtension = (PKBDHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* get stack location */ + IoStack = IoGetCurrentIrpStackLocation(Irp); + + /* copy stack location to next */ + IoCopyCurrentIrpStackLocationToNext(Irp); + + /* init event */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + /* prepare irp */ + IoSetCompletionRoutine(Irp, KbdHid_CreateCompletion, &Event, TRUE, TRUE, TRUE); + + /* call lower driver */ + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + /* request pending */ + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + + /* check for success */ + if (!NT_SUCCESS(Status)) + { + /* failed */ + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + + /* is the driver already in use */ + if (DeviceExtension->FileObject == NULL) + { + /* did the caller specify correct attributes */ + ASSERT(IoStack->Parameters.Create.SecurityContext); + if (IoStack->Parameters.Create.SecurityContext->DesiredAccess) + { + /* store file object */ + DeviceExtension->FileObject = IoStack->FileObject; + + /* reset event */ + KeResetEvent(&DeviceExtension->ReadCompletionEvent); + + /* initiating read */ + Status = KbdHid_InitiateRead(DeviceExtension); + DPRINT("[KBDHID] KbdHid_InitiateRead: status %x\n", Status); + if (Status == STATUS_PENDING) + { + /* report irp is pending */ + Status = STATUS_SUCCESS; + } + } + } + + /* complete request */ + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; +} + + +NTSTATUS +NTAPI +KbdHid_Close( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PKBDHID_DEVICE_EXTENSION DeviceExtension; + + /* get device extension */ + DeviceExtension = (PKBDHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + DPRINT("[KBDHID] IRP_MJ_CLOSE ReadReportActive %x\n", DeviceExtension->ReadReportActive); + + if (DeviceExtension->ReadReportActive) + { + /* request stopping of the report cycle */ + DeviceExtension->StopReadReport = TRUE; + + /* wait until the reports have been read */ + KeWaitForSingleObject(&DeviceExtension->ReadCompletionEvent, Executive, KernelMode, FALSE, NULL); + + /* cancel irp */ + IoCancelIrp(DeviceExtension->Irp); + } + + DPRINT("[KBDHID] IRP_MJ_CLOSE ReadReportActive %x\n", DeviceExtension->ReadReportActive); + + /* remove file object */ + DeviceExtension->FileObject = NULL; + + /* skip location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* pass irp to down the stack */ + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); +} + +NTSTATUS +NTAPI +KbdHid_InternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PKBDHID_DEVICE_EXTENSION DeviceExtension; + PCONNECT_DATA Data; + PKEYBOARD_ATTRIBUTES Attributes; + + /* get current stack location */ + IoStack = IoGetCurrentIrpStackLocation(Irp); + + DPRINT("[KBDHID] InternalDeviceControl %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode); + + /* get device extension */ + DeviceExtension = (PKBDHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KEYBOARD_QUERY_ATTRIBUTES) + { + /* verify output buffer length */ + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUSE_ATTRIBUTES)) + { + /* invalid request */ + DPRINT1("[MOUHID] IOCTL_MOUSE_QUERY_ATTRIBUTES Buffer too small\n"); + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_BUFFER_TOO_SMALL; + } + + /* get output buffer */ + Attributes = (PKEYBOARD_ATTRIBUTES)Irp->AssociatedIrp.SystemBuffer; + + /* copy attributes */ + RtlCopyMemory(Attributes, &DeviceExtension->Attributes, sizeof(KEYBOARD_ATTRIBUTES)); + + /* complete request */ + Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES); + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_KEYBOARD_CONNECT) + { + /* verify input buffer length */ + if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONNECT_DATA)) + { + /* invalid request */ + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER; + } + + /* is it already connected */ + if (DeviceExtension->ClassService) + { + /* already connected */ + Irp->IoStatus.Status = STATUS_SHARING_VIOLATION; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SHARING_VIOLATION; + } + + /* get connect data */ + Data = (PCONNECT_DATA)IoStack->Parameters.DeviceIoControl.Type3InputBuffer; + + /* store connect details */ + DeviceExtension->ClassDeviceObject = Data->ClassDeviceObject; + DeviceExtension->ClassService = Data->ClassService; + + /* completed successfully */ + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_KEYBOARD_DISCONNECT) + { + /* not implemented */ + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_KEYBOARD_ENABLE) + { + /* not supported */ + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_SUPPORTED; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_KEYBOARD_DISABLE) + { + /* not supported */ + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_SUPPORTED; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KEYBOARD_QUERY_INDICATORS) + { + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS)) + { + /* invalid parameter */ + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER; + } + + /* copy indicators */ + RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &DeviceExtension->KeyboardIndicator, sizeof(KEYBOARD_INDICATOR_PARAMETERS)); + + /* complete request */ + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(KEYBOARD_INDICATOR_PARAMETERS); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KEYBOARD_QUERY_TYPEMATIC) + { + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) + { + /* invalid parameter */ + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER; + } + + /* copy indicators */ + RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &DeviceExtension->KeyboardTypematic, sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)); + + /* done */ + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(KEYBOARD_TYPEMATIC_PARAMETERS); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KEYBOARD_SET_INDICATORS) + { + if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS)) + { + /* invalid parameter */ + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER; + } + + /* copy indicators */ + RtlCopyMemory(&DeviceExtension->KeyboardIndicator, Irp->AssociatedIrp.SystemBuffer, sizeof(KEYBOARD_INDICATOR_PARAMETERS)); + + /* done */ + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KEYBOARD_SET_TYPEMATIC) + { + if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) + { + /* invalid parameter */ + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER; + } + + /* copy indicators */ + RtlCopyMemory(&DeviceExtension->KeyboardTypematic, Irp->AssociatedIrp.SystemBuffer, sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)); + + /* done */ + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION) + { + /* not implemented */ + DPRINT1("IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION not implemented\n"); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + + /* unknown control code */ + DPRINT1("[KBDHID] Unknown DeviceControl %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode); + /* unknown request not supported */ + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_SUPPORTED; +} + +NTSTATUS +NTAPI +KbdHid_DeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PKBDHID_DEVICE_EXTENSION DeviceExtension; + + /* get device extension */ + DeviceExtension = (PKBDHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* skip stack location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* pass and forget */ + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); +} + +NTSTATUS +NTAPI +KbdHid_Power( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +KbdHid_SubmitRequest( + PDEVICE_OBJECT DeviceObject, + ULONG IoControlCode, + ULONG InputBufferSize, + PVOID InputBuffer, + ULONG OutputBufferSize, + PVOID OutputBuffer) +{ + KEVENT Event; + PKBDHID_DEVICE_EXTENSION DeviceExtension; + PIRP Irp; + NTSTATUS Status; + IO_STATUS_BLOCK IoStatus; + + /* get device extension */ + DeviceExtension = (PKBDHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* init event */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + /* build request */ + Irp = IoBuildDeviceIoControlRequest(IoControlCode, DeviceExtension->NextDeviceObject, InputBuffer, InputBufferSize, OutputBuffer, OutputBufferSize, FALSE, &Event, &IoStatus); + if (!Irp) + { + /* no memory */ + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* send request */ + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + /* wait for request to complete */ + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatus.Status; + } + + /* done */ + return Status; +} + +NTSTATUS +NTAPI +KbdHid_StartDevice( + IN PDEVICE_OBJECT DeviceObject) +{ + NTSTATUS Status; + ULONG Buttons; + HID_COLLECTION_INFORMATION Information; + PHIDP_PREPARSED_DATA PreparsedData; + HIDP_CAPS Capabilities; + PKBDHID_DEVICE_EXTENSION DeviceExtension; + PUSAGE_AND_PAGE Buffer; + + /* get device extension */ + DeviceExtension = (PKBDHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* query collection information */ + Status = KbdHid_SubmitRequest(DeviceObject, IOCTL_HID_GET_COLLECTION_INFORMATION, 0, NULL, sizeof(HID_COLLECTION_INFORMATION), &Information); + if (!NT_SUCCESS(Status)) + { + /* failed to query collection information */ + DPRINT1("[KBDHID] failed to obtain collection information with %x\n", Status); + return Status; + } + + /* lets allocate space for preparsed data */ + PreparsedData = (PHIDP_PREPARSED_DATA)ExAllocatePool(NonPagedPool, Information.DescriptorSize); + if (!PreparsedData) + { + /* no memory */ + DPRINT1("[KBDHID] no memory size %u\n", Information.DescriptorSize); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* now obtain the preparsed data */ + Status = KbdHid_SubmitRequest(DeviceObject, IOCTL_HID_GET_COLLECTION_DESCRIPTOR, 0, NULL, Information.DescriptorSize, PreparsedData); + if (!NT_SUCCESS(Status)) + { + /* failed to get preparsed data */ + DPRINT1("[KBDHID] failed to obtain collection information with %x\n", Status); + ExFreePool(PreparsedData); + return Status; + } + + /* lets get the caps */ + Status = HidP_GetCaps(PreparsedData, &Capabilities); + if (Status != HIDP_STATUS_SUCCESS) + { + /* failed to get capabilities */ + DPRINT1("[KBDHID] failed to obtain caps with %x\n", Status); + ExFreePool(PreparsedData); + return Status; + } + + DPRINT("[KBDHID] Usage %x UsagePage %x InputReportLength %lu\n", Capabilities.Usage, Capabilities.UsagePage, Capabilities.InputReportByteLength); + + /* init input report*/ + DeviceExtension->ReportLength = Capabilities.InputReportByteLength; + ASSERT(DeviceExtension->ReportLength); + DeviceExtension->Report = (PCHAR)ExAllocatePool(NonPagedPool, DeviceExtension->ReportLength); + ASSERT(DeviceExtension->Report); + RtlZeroMemory(DeviceExtension->Report, DeviceExtension->ReportLength); + + /* build mdl */ + DeviceExtension->ReportMDL = IoAllocateMdl(DeviceExtension->Report, DeviceExtension->ReportLength, FALSE, FALSE, NULL); + ASSERT(DeviceExtension->ReportMDL); + + /* init mdl */ + MmBuildMdlForNonPagedPool(DeviceExtension->ReportMDL); + + /* get max number of buttons */ + Buttons = HidP_MaxUsageListLength(HidP_Input, HID_USAGE_PAGE_KEYBOARD, PreparsedData); + DPRINT("[KBDHID] Buttons %lu\n", Buttons); + ASSERT(Buttons > 0); + + /* now allocate an array for those buttons */ + Buffer = (PUSAGE_AND_PAGE)ExAllocatePool(NonPagedPool, sizeof(USAGE_AND_PAGE) * 4 * Buttons); + if (!Buffer) + { + /* no memory */ + ExFreePool(PreparsedData); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* init usage lists */ + RtlZeroMemory(Buffer, sizeof(USAGE_AND_PAGE) * 4 * Buttons); + DeviceExtension->CurrentUsageList = Buffer; + Buffer += Buttons; + DeviceExtension->PreviousUsageList = Buffer; + Buffer += Buttons; + DeviceExtension->MakeUsageList = Buffer; + Buffer += Buttons; + DeviceExtension->BreakUsageList = Buffer; + + // + // FIMXE: implement device hacks + // + // UsageMappings + // KeyboardTypeOverride + // KeyboardSubTypeOverride + // KeyboardNumberTotalKeysOverride + // KeyboardNumberFunctionKeysOverride + // KeyboardNumberIndicatorsOverride + + /* store number of buttons */ + DeviceExtension->UsageListLength = (USHORT)Buttons; + + /* store preparsed data */ + DeviceExtension->PreparsedData = PreparsedData; + + /* completed successfully */ + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +KbdHid_StartDeviceCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + KeSetEvent((PKEVENT)Context, 0, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +NTAPI +KbdHid_Flush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PKBDHID_DEVICE_EXTENSION DeviceExtension; + + /* get device extension */ + DeviceExtension = (PKBDHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* skip current stack location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* get next stack location */ + IoStack = IoGetNextIrpStackLocation(Irp); + + /* change request to hid flush queue request */ + IoStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_FLUSH_QUEUE; + + /* call device */ + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); +} + +NTSTATUS +NTAPI +KbdHid_Pnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + KEVENT Event; + NTSTATUS Status; + PKBDHID_DEVICE_EXTENSION DeviceExtension; + + /* get device extension */ + DeviceExtension = (PKBDHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* get current irp stack */ + IoStack = IoGetCurrentIrpStackLocation(Irp); + DPRINT("[KBDHID] IRP_MJ_PNP Request: %x\n", IoStack->MinorFunction); + + if (IoStack->MinorFunction == IRP_MN_STOP_DEVICE || + IoStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE || + IoStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE || + IoStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE || + IoStack->MinorFunction == IRP_MN_QUERY_REMOVE_DEVICE) + { + /* indicate success */ + Irp->IoStatus.Status = STATUS_SUCCESS; + + /* skip irp stack location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* dispatch to lower device */ + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + } + else if (IoStack->MinorFunction == IRP_MN_REMOVE_DEVICE) + { + /* FIXME synchronization */ + + /* cancel irp */ + IoCancelIrp(DeviceExtension->Irp); + + /* indicate success */ + Irp->IoStatus.Status = STATUS_SUCCESS; + + /* skip irp stack location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* dispatch to lower device */ + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + + IoFreeIrp(DeviceExtension->Irp); + IoDetachDevice(DeviceExtension->NextDeviceObject); + IoDeleteDevice(DeviceObject); + return Status; + } + else if (IoStack->MinorFunction == IRP_MN_START_DEVICE) + { + /* init event */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + /* copy stack location */ + IoCopyCurrentIrpStackLocationToNext (Irp); + + /* set completion routine */ + IoSetCompletionRoutine(Irp, KbdHid_StartDeviceCompletion, &Event, TRUE, TRUE, TRUE); + Irp->IoStatus.Status = 0; + + /* pass request */ + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = Irp->IoStatus.Status; + } + + if (!NT_SUCCESS(Status)) + { + /* failed */ + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + + /* lets start the device */ + Status = KbdHid_StartDevice(DeviceObject); + DPRINT("KbdHid_StartDevice %x\n", Status); + + /* complete request */ + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + /* done */ + return Status; + } + else + { + /* skip irp stack location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* dispatch to lower device */ + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + } +} + +NTSTATUS +NTAPI +KbdHid_AddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) +{ + NTSTATUS Status; + PDEVICE_OBJECT DeviceObject, NextDeviceObject; + PKBDHID_DEVICE_EXTENSION DeviceExtension; + POWER_STATE State; + + /* create device object */ + Status = IoCreateDevice(DriverObject, sizeof(KBDHID_DEVICE_EXTENSION), NULL, FILE_DEVICE_KEYBOARD, 0, FALSE, &DeviceObject); + if (!NT_SUCCESS(Status)) + { + /* failed to create device object */ + return Status; + } + + /* now attach it */ + NextDeviceObject = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject); + if (!NextDeviceObject) + { + /* failed to attach */ + IoDeleteDevice(DeviceObject); + return STATUS_DEVICE_NOT_CONNECTED; + } + + /* get device extension */ + DeviceExtension = (PKBDHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* zero extension */ + RtlZeroMemory(DeviceExtension, sizeof(KBDHID_DEVICE_EXTENSION)); + + /* init device extension */ + DeviceExtension->NextDeviceObject = NextDeviceObject; + KeInitializeEvent(&DeviceExtension->ReadCompletionEvent, NotificationEvent, FALSE); + + /* init keyboard attributes */ + DeviceExtension->Attributes.KeyboardIdentifier.Type = KEYBOARD_TYPE_UNKNOWN; + DeviceExtension->Attributes.KeyboardIdentifier.Subtype = MICROSOFT_KBD_101_TYPE; + DeviceExtension->Attributes.NumberOfFunctionKeys = MICROSOFT_KBD_FUNC; + DeviceExtension->Attributes.NumberOfIndicators = 3; // caps, num lock, scroll lock + DeviceExtension->Attributes.NumberOfKeysTotal = 101; + DeviceExtension->Attributes.InputDataQueueLength = 1; + DeviceExtension->Attributes.KeyRepeatMinimum.Rate = KEYBOARD_TYPEMATIC_RATE_MINIMUM; + DeviceExtension->Attributes.KeyRepeatMinimum.Delay = KEYBOARD_TYPEMATIC_DELAY_MINIMUM; + DeviceExtension->Attributes.KeyRepeatMaximum.Rate = KEYBOARD_TYPEMATIC_RATE_DEFAULT; + DeviceExtension->Attributes.KeyRepeatMaximum.Delay = KEYBOARD_TYPEMATIC_DELAY_MAXIMUM; + + /* allocate irp */ + DeviceExtension->Irp = IoAllocateIrp(NextDeviceObject->StackSize, FALSE); + + /* FIXME handle allocation error */ + ASSERT(DeviceExtension->Irp); + + /* set power state to D0 */ + State.DeviceState = PowerDeviceD0; + PoSetPowerState(DeviceObject, DevicePowerState, State); + + /* init device object */ + DeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; + DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + /* completed successfully */ + return STATUS_SUCCESS; +} + +VOID +NTAPI +KbdHid_Unload( + IN PDRIVER_OBJECT DriverObject) +{ + UNIMPLEMENTED +} + + +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegPath) +{ + /* initialize driver object */ + DriverObject->DriverUnload = KbdHid_Unload; + DriverObject->DriverExtension->AddDevice = KbdHid_AddDevice; + DriverObject->MajorFunction[IRP_MJ_CREATE] = KbdHid_Create; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = KbdHid_Close; + DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = KbdHid_Flush; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KbdHid_DeviceControl; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = KbdHid_InternalDeviceControl; + DriverObject->MajorFunction[IRP_MJ_POWER] = KbdHid_Power; + DriverObject->MajorFunction[IRP_MJ_PNP] = KbdHid_Pnp; + DriverObject->DriverUnload = KbdHid_Unload; + DriverObject->DriverExtension->AddDevice = KbdHid_AddDevice; + + /* done */ + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/hid/kbdhid/kbdhid.h b/reactos/drivers/hid/kbdhid/kbdhid.h new file mode 100644 index 00000000000..bc606bb2f82 --- /dev/null +++ b/reactos/drivers/hid/kbdhid/kbdhid.h @@ -0,0 +1,146 @@ +#pragma once + +#define _HIDPI_NO_FUNCTION_MACROS_ +#include +#include +#include +#include +#define NDEBUG +#include +#include +//#include +#include +#include + + +typedef struct +{ + // + // lower device object + // + PDEVICE_OBJECT NextDeviceObject; + + // + // irp which is used for reading input reports + // + PIRP Irp; + + // + // event + // + KEVENT ReadCompletionEvent; + + // + // device object for class callback + // + PDEVICE_OBJECT ClassDeviceObject; + + // + // class callback + // + PVOID ClassService; + + // + // usage list length + // + USHORT UsageListLength; + + // + // current usage list length + // + PUSAGE_AND_PAGE CurrentUsageList; + + // + // previous usage list + // + PUSAGE_AND_PAGE PreviousUsageList; + + // + // removed usage item list + // + PUSAGE_AND_PAGE BreakUsageList; + + // + // new item usage list + // + PUSAGE_AND_PAGE MakeUsageList; + + // + // preparsed data + // + PHIDP_PREPARSED_DATA PreparsedData; + + // + // mdl for reading input report + // + PMDL ReportMDL; + + // + // input report buffer + // + PCHAR Report; + + // + // input report length + // + ULONG ReportLength; + + // + // file object the device is reading reports from + // + PFILE_OBJECT FileObject; + + // + // report read is active + // + UCHAR ReadReportActive; + + // + // stop reading flag + // + UCHAR StopReadReport; + + // + // keyboard attributes + // + KEYBOARD_ATTRIBUTES Attributes; + + // + // keyboard modifier state + // + HIDP_KEYBOARD_MODIFIER_STATE ModifierState; + + // + // keyboard indicator state + // + KEYBOARD_INDICATOR_PARAMETERS KeyboardIndicator; + + // + // keyboard type matic + // + KEYBOARD_TYPEMATIC_PARAMETERS KeyboardTypematic; + + + +}KBDHID_DEVICE_EXTENSION, *PKBDHID_DEVICE_EXTENSION; + +/* defaults from kbfiltr.h */ +#define KEYBOARD_TYPEMATIC_RATE_MINIMUM 2 +#define KEYBOARD_TYPEMATIC_RATE_MAXIMUM 30 +#define KEYBOARD_TYPEMATIC_RATE_DEFAULT 30 +#define KEYBOARD_TYPEMATIC_DELAY_MINIMUM 250 +#define KEYBOARD_TYPEMATIC_DELAY_MAXIMUM 1000 +#define KEYBOARD_TYPEMATIC_DELAY_DEFAULT 250 + +/* FIXME: write kbd.h */ +#define MICROSOFT_KBD_FUNC 12 +#define KEYBOARD_TYPE_UNKNOWN (0x51) +#define MICROSOFT_KBD_101_TYPE 0 + + + + +NTSTATUS +KbdHid_InitiateRead( + IN PKBDHID_DEVICE_EXTENSION DeviceExtension); + diff --git a/reactos/drivers/hid/kbdhid/kbdhid.rbuild b/reactos/drivers/hid/kbdhid/kbdhid.rbuild new file mode 100644 index 00000000000..904f6d2324c --- /dev/null +++ b/reactos/drivers/hid/kbdhid/kbdhid.rbuild @@ -0,0 +1,12 @@ + + + + + + include + ntoskrnl + hal + hidparse + kbdhid.c + kbdhid.rc + diff --git a/reactos/drivers/hid/kbdhid/kbdhid.rc b/reactos/drivers/hid/kbdhid/kbdhid.rc new file mode 100644 index 00000000000..317d71e2932 --- /dev/null +++ b/reactos/drivers/hid/kbdhid/kbdhid.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "Keyboard HID Class Driver\0" +#define REACTOS_STR_INTERNAL_NAME "kbdhid\0" +#define REACTOS_STR_ORIGINAL_FILENAME "kbdhid.sys\0" +#include diff --git a/reactos/drivers/hid/mouhid/CMakeLists.txt b/reactos/drivers/hid/mouhid/CMakeLists.txt new file mode 100644 index 00000000000..77d9c10d7f5 --- /dev/null +++ b/reactos/drivers/hid/mouhid/CMakeLists.txt @@ -0,0 +1,11 @@ + +add_definitions(-DDEBUG_MODE) + +include_directories(${REACTOS_SOURCE_DIR}/ntoskrnl/include) + +add_library(mouhid SHARED mouhid.c mouhid.rc) + +set_module_type(mouhid kernelmodedriver) +add_importlibs(mouhid ntoskrnl hal hidparse) + +add_cd_file(TARGET mouhid DESTINATION reactos/system32/drivers FOR all) \ No newline at end of file diff --git a/reactos/drivers/hid/mouhid/mouhid.c b/reactos/drivers/hid/mouhid/mouhid.c new file mode 100644 index 00000000000..b713b70f3dc --- /dev/null +++ b/reactos/drivers/hid/mouhid/mouhid.c @@ -0,0 +1,979 @@ +/* + * PROJECT: ReactOS HID Stack + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/hid/mouhid/mouhid.c + * PURPOSE: Mouse HID Driver + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "mouhid.h" + +static USHORT MouHid_ButtonUpFlags[] = +{ + 0xFF, /* unused */ + MOUSE_LEFT_BUTTON_DOWN, + MOUSE_RIGHT_BUTTON_DOWN, + MOUSE_MIDDLE_BUTTON_DOWN, + MOUSE_BUTTON_4_DOWN, + MOUSE_BUTTON_5_DOWN +}; + +static USHORT MouHid_ButtonDownFlags[] = +{ + 0xFF, /* unused */ + MOUSE_LEFT_BUTTON_UP, + MOUSE_RIGHT_BUTTON_UP, + MOUSE_MIDDLE_BUTTON_UP, + MOUSE_BUTTON_4_UP, + MOUSE_BUTTON_5_UP +}; + +VOID +MouHid_GetButtonMove( + IN PMOUHID_DEVICE_EXTENSION DeviceExtension, + OUT PLONG LastX, + OUT PLONG LastY) +{ + NTSTATUS Status; + + /* init result */ + *LastX = 0; + *LastY = 0; + + /* get scaled usage value x */ + Status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_X, (PLONG)LastX, DeviceExtension->PreparsedData, DeviceExtension->Report, DeviceExtension->ReportLength); + /* FIXME handle error */ + ASSERT(Status == HIDP_STATUS_SUCCESS); + + /* get scaled usage value y */ + Status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_Y, (PLONG)LastY, DeviceExtension->PreparsedData, DeviceExtension->Report, DeviceExtension->ReportLength); + /* FIXME handle error */ + ASSERT(Status == HIDP_STATUS_SUCCESS); + +} + + +VOID +MouHid_GetButtonFlags( + IN PMOUHID_DEVICE_EXTENSION DeviceExtension, + OUT PUSHORT ButtonFlags) +{ + NTSTATUS Status; + USAGE Usage; + ULONG Index; + PUSAGE TempList; + ULONG CurrentUsageListLength; + + /* init flags */ + *ButtonFlags = 0; + + /* get usages */ + CurrentUsageListLength = DeviceExtension->UsageListLength; + Status = HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_BUTTON, HIDP_LINK_COLLECTION_UNSPECIFIED, DeviceExtension->CurrentUsageList, &CurrentUsageListLength, DeviceExtension->PreparsedData, DeviceExtension->Report, DeviceExtension->ReportLength); + if (Status != HIDP_STATUS_SUCCESS) + { + DPRINT1("MouHid_GetButtonFlags failed to get usages with %x\n", Status); + return; + } + + /* extract usage list difference */ + Status = HidP_UsageListDifference(DeviceExtension->PreviousUsageList, DeviceExtension->CurrentUsageList, DeviceExtension->BreakUsageList, DeviceExtension->MakeUsageList, DeviceExtension->UsageListLength); + if (Status != HIDP_STATUS_SUCCESS) + { + DPRINT1("MouHid_GetButtonFlags failed to get usages with %x\n", Status); + return; + } + + if (DeviceExtension->UsageListLength) + { + Index = 0; + do + { + /* get usage */ + Usage = DeviceExtension->BreakUsageList[Index]; + if (!Usage) + break; + + if (Usage <= 5) + { + /* max 5 buttons supported */ + *ButtonFlags |= MouHid_ButtonDownFlags[Usage]; + } + + /* move to next index*/ + Index++; + }while(Index < DeviceExtension->UsageListLength); + } + + if (DeviceExtension->UsageListLength) + { + Index = 0; + do + { + /* get usage */ + Usage = DeviceExtension->MakeUsageList[Index]; + if (!Usage) + break; + + if (Usage <= 5) + { + /* max 5 buttons supported */ + *ButtonFlags |= MouHid_ButtonUpFlags[Usage]; + } + + /* move to next index*/ + Index++; + }while(Index < DeviceExtension->UsageListLength); + } + + /* now switch the previous list with current list */ + TempList = DeviceExtension->CurrentUsageList; + DeviceExtension->CurrentUsageList = DeviceExtension->PreviousUsageList; + DeviceExtension->PreviousUsageList = TempList; +} + +VOID +MouHid_DispatchInputData( + IN PMOUHID_DEVICE_EXTENSION DeviceExtension, + IN PMOUSE_INPUT_DATA InputData) +{ + KIRQL OldIrql; + ULONG InputDataConsumed; + + if (!DeviceExtension->ClassService) + return; + + /* sanity check */ + ASSERT(DeviceExtension->ClassService); + ASSERT(DeviceExtension->ClassDeviceObject); + + /* raise irql */ + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + + /* dispatch input data */ + (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->ClassService)(DeviceExtension->ClassDeviceObject, InputData, InputData + 1, &InputDataConsumed); + + /* lower irql to previous level */ + KeLowerIrql(OldIrql); +} + +NTSTATUS +NTAPI +MouHid_ReadCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + PMOUHID_DEVICE_EXTENSION DeviceExtension; + USHORT ButtonFlags; + LONG UsageValue; + NTSTATUS Status; + LONG LastX, LastY; + MOUSE_INPUT_DATA MouseInputData; + + /* get device extension */ + DeviceExtension = (PMOUHID_DEVICE_EXTENSION)Context; + + if (Irp->IoStatus.Status == STATUS_PRIVILEGE_NOT_HELD || + Irp->IoStatus.Status == STATUS_DEVICE_NOT_CONNECTED || + Irp->IoStatus.Status == STATUS_CANCELLED || + DeviceExtension->StopReadReport) + { + /* failed to read or should be stopped*/ + DPRINT1("[MOUHID] ReadCompletion terminating read Status %x\n", Irp->IoStatus.Status); + + /* report no longer active */ + DeviceExtension->ReadReportActive = FALSE; + + /* request stopping of the report cycle */ + DeviceExtension->StopReadReport = FALSE; + + /* signal completion event */ + KeSetEvent(&DeviceExtension->ReadCompletionEvent, 0, 0); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + /* get mouse change flags */ + MouHid_GetButtonFlags(DeviceExtension, &ButtonFlags); + + /* get mouse change */ + MouHid_GetButtonMove(DeviceExtension, &LastX, &LastY); + + /* init input data */ + RtlZeroMemory(&MouseInputData, sizeof(MOUSE_INPUT_DATA)); + + /* init input data */ + MouseInputData.ButtonFlags = ButtonFlags; + MouseInputData.LastX = LastX; + MouseInputData.LastY = LastY; + + /* detect mouse wheel change */ + if (DeviceExtension->MouseIdentifier == WHEELMOUSE_HID_HARDWARE) + { + /* get usage */ + UsageValue = 0; + Status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_WHEEL, &UsageValue, DeviceExtension->PreparsedData, DeviceExtension->Report, DeviceExtension->ReportLength); + if (Status == HIDP_STATUS_SUCCESS && UsageValue != 0) + { + /* store wheel status */ + MouseInputData.ButtonFlags |= MOUSE_WHEEL; + MouseInputData.ButtonData = (USHORT)(UsageValue * WHEEL_DELTA); + } + else + { + DPRINT("[MOUHID] failed to get wheel status with %x\n", Status); + } + } + + DPRINT("[MOUHID] LastX %ld LastY %ld Flags %x ButtonData %x\n", MouseInputData.LastX, MouseInputData.LastY, MouseInputData.ButtonFlags, MouseInputData.ButtonData); + + /* dispatch mouse action */ + MouHid_DispatchInputData(DeviceExtension, &MouseInputData); + + /* re-init read */ + MouHid_InitiateRead(DeviceExtension); + + /* stop completion */ + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +MouHid_InitiateRead( + IN PMOUHID_DEVICE_EXTENSION DeviceExtension) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + + /* re-use irp */ + IoReuseIrp(DeviceExtension->Irp, STATUS_SUCCESS); + + /* init irp */ + DeviceExtension->Irp->MdlAddress = DeviceExtension->ReportMDL; + + /* get next stack location */ + IoStack = IoGetNextIrpStackLocation(DeviceExtension->Irp); + + /* init stack location */ + IoStack->Parameters.Read.Length = DeviceExtension->ReportLength; + IoStack->Parameters.Read.Key = 0; + IoStack->Parameters.Read.ByteOffset.QuadPart = 0LL; + IoStack->MajorFunction = IRP_MJ_READ; + IoStack->FileObject = DeviceExtension->FileObject; + + /* set completion routine */ + IoSetCompletionRoutine(DeviceExtension->Irp, MouHid_ReadCompletion, DeviceExtension, TRUE, TRUE, TRUE); + + /* read is active */ + DeviceExtension->ReadReportActive = TRUE; + + /* start the read */ + Status = IoCallDriver(DeviceExtension->NextDeviceObject, DeviceExtension->Irp); + + /* done */ + return Status; +} + +NTSTATUS +NTAPI +MouHid_CreateCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + KeSetEvent((PKEVENT)Context, 0, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +NTAPI +MouHid_Create( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + KEVENT Event; + PMOUHID_DEVICE_EXTENSION DeviceExtension; + + DPRINT("MOUHID: IRP_MJ_CREATE\n"); + + /* get device extension */ + DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* get stack location */ + IoStack = IoGetCurrentIrpStackLocation(Irp); + + /* copy stack location to next */ + IoCopyCurrentIrpStackLocationToNext(Irp); + + /* init event */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + /* prepare irp */ + IoSetCompletionRoutine(Irp, MouHid_CreateCompletion, &Event, TRUE, TRUE, TRUE); + + /* call lower driver */ + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + /* request pending */ + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + + /* check for success */ + if (!NT_SUCCESS(Status)) + { + /* failed */ + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + + /* is the driver already in use */ + if (DeviceExtension->FileObject == NULL) + { + /* did the caller specify correct attributes */ + ASSERT(IoStack->Parameters.Create.SecurityContext); + if (IoStack->Parameters.Create.SecurityContext->DesiredAccess) + { + /* store file object */ + DeviceExtension->FileObject = IoStack->FileObject; + + /* reset event */ + KeResetEvent(&DeviceExtension->ReadCompletionEvent); + + /* initiating read */ + Status = MouHid_InitiateRead(DeviceExtension); + DPRINT("[MOUHID] MouHid_InitiateRead: status %x\n", Status); + if (Status == STATUS_PENDING) + { + /* report irp is pending */ + Status = STATUS_SUCCESS; + } + } + } + + /* complete request */ + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; +} + + +NTSTATUS +NTAPI +MouHid_Close( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PMOUHID_DEVICE_EXTENSION DeviceExtension; + + /* get device extension */ + DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + DPRINT("[MOUHID] IRP_MJ_CLOSE ReadReportActive %x\n", DeviceExtension->ReadReportActive); + + if (DeviceExtension->ReadReportActive) + { + /* request stopping of the report cycle */ + DeviceExtension->StopReadReport = TRUE; + + /* wait until the reports have been read */ + KeWaitForSingleObject(&DeviceExtension->ReadCompletionEvent, Executive, KernelMode, FALSE, NULL); + + /* cancel irp */ + IoCancelIrp(DeviceExtension->Irp); + } + + DPRINT("[MOUHID] IRP_MJ_CLOSE ReadReportActive %x\n", DeviceExtension->ReadReportActive); + + /* remove file object */ + DeviceExtension->FileObject = NULL; + + /* skip location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* pass irp to down the stack */ + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); +} + +NTSTATUS +NTAPI +MouHid_InternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PMOUSE_ATTRIBUTES Attributes; + PMOUHID_DEVICE_EXTENSION DeviceExtension; + PCONNECT_DATA Data; + + /* get current stack location */ + IoStack = IoGetCurrentIrpStackLocation(Irp); + + DPRINT("[MOUHID] InternalDeviceControl %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode); + + /* get device extension */ + DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* handle requests */ + if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_MOUSE_QUERY_ATTRIBUTES) + { + /* verify output buffer length */ + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUSE_ATTRIBUTES)) + { + /* invalid request */ + DPRINT1("[MOUHID] IOCTL_MOUSE_QUERY_ATTRIBUTES Buffer too small\n"); + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_BUFFER_TOO_SMALL; + } + + /* get output buffer */ + Attributes = (PMOUSE_ATTRIBUTES)Irp->AssociatedIrp.SystemBuffer; + + /* type of mouse */ + Attributes->MouseIdentifier = DeviceExtension->MouseIdentifier; + + /* number of buttons */ + Attributes->NumberOfButtons = DeviceExtension->UsageListLength; + + /* sample rate not used for usb */ + Attributes->SampleRate = 0; + + /* queue length */ + Attributes->InputDataQueueLength = 2; + + DPRINT("[MOUHID] MouseIdentifier %x\n", Attributes->MouseIdentifier); + DPRINT("[MOUHID] NumberOfButtons %x\n", Attributes->NumberOfButtons); + DPRINT("[MOUHID] SampleRate %x\n", Attributes->SampleRate); + DPRINT("[MOUHID] InputDataQueueLength %x\n", Attributes->InputDataQueueLength); + + /* complete request */ + Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES); + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_MOUSE_CONNECT) + { + /* verify input buffer length */ + if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONNECT_DATA)) + { + /* invalid request */ + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER; + } + + /* is it already connected */ + if (DeviceExtension->ClassService) + { + /* already connected */ + Irp->IoStatus.Status = STATUS_SHARING_VIOLATION; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SHARING_VIOLATION; + } + + /* get connect data */ + Data = (PCONNECT_DATA)IoStack->Parameters.DeviceIoControl.Type3InputBuffer; + + /* store connect details */ + DeviceExtension->ClassDeviceObject = Data->ClassDeviceObject; + DeviceExtension->ClassService = Data->ClassService; + + /* completed successfully */ + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_MOUSE_DISCONNECT) + { + /* not supported */ + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_MOUSE_ENABLE) + { + /* not supported */ + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_SUPPORTED; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_MOUSE_DISABLE) + { + /* not supported */ + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_DEVICE_REQUEST; + } + + DPRINT1("[MOUHID] Unknown DeviceControl %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode); + /* unknown request not supported */ + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_SUPPORTED; +} + +NTSTATUS +NTAPI +MouHid_DeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PMOUHID_DEVICE_EXTENSION DeviceExtension; + + /* get device extension */ + DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* skip stack location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* pass and forget */ + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); +} + +NTSTATUS +NTAPI +MouHid_Power( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +MouHid_SubmitRequest( + PDEVICE_OBJECT DeviceObject, + ULONG IoControlCode, + ULONG InputBufferSize, + PVOID InputBuffer, + ULONG OutputBufferSize, + PVOID OutputBuffer) +{ + KEVENT Event; + PMOUHID_DEVICE_EXTENSION DeviceExtension; + PIRP Irp; + NTSTATUS Status; + IO_STATUS_BLOCK IoStatus; + + /* get device extension */ + DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* init event */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + /* build request */ + Irp = IoBuildDeviceIoControlRequest(IoControlCode, DeviceExtension->NextDeviceObject, InputBuffer, InputBufferSize, OutputBuffer, OutputBufferSize, FALSE, &Event, &IoStatus); + if (!Irp) + { + /* no memory */ + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* send request */ + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + /* wait for request to complete */ + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatus.Status; + } + + /* done */ + return Status; +} + +NTSTATUS +NTAPI +MouHid_StartDevice( + IN PDEVICE_OBJECT DeviceObject) +{ + NTSTATUS Status; + ULONG Buttons; + HID_COLLECTION_INFORMATION Information; + PVOID PreparsedData; + HIDP_CAPS Capabilities; + ULONG ValueCapsLength; + HIDP_VALUE_CAPS ValueCaps; + PMOUHID_DEVICE_EXTENSION DeviceExtension; + PUSHORT Buffer; + + /* get device extension */ + DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* query collection information */ + Status = MouHid_SubmitRequest(DeviceObject, IOCTL_HID_GET_COLLECTION_INFORMATION, 0, NULL, sizeof(HID_COLLECTION_INFORMATION), &Information); + if (!NT_SUCCESS(Status)) + { + /* failed to query collection information */ + DPRINT1("[MOUHID] failed to obtain collection information with %x\n", Status); + return Status; + } + + /* lets allocate space for preparsed data */ + PreparsedData = ExAllocatePool(NonPagedPool, Information.DescriptorSize); + if (!PreparsedData) + { + /* no memory */ + DPRINT1("[MOUHID] no memory size %u\n", Information.DescriptorSize); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* now obtain the preparsed data */ + Status = MouHid_SubmitRequest(DeviceObject, IOCTL_HID_GET_COLLECTION_DESCRIPTOR, 0, NULL, Information.DescriptorSize, PreparsedData); + if (!NT_SUCCESS(Status)) + { + /* failed to get preparsed data */ + DPRINT1("[MOUHID] failed to obtain collection information with %x\n", Status); + ExFreePool(PreparsedData); + return Status; + } + + /* lets get the caps */ + Status = HidP_GetCaps(PreparsedData, &Capabilities); + if (Status != HIDP_STATUS_SUCCESS) + { + /* failed to get capabilities */ + DPRINT1("[MOUHID] failed to obtain caps with %x\n", Status); + ExFreePool(PreparsedData); + return Status; + } + + DPRINT("[MOUHID] Usage %x UsagePage %x InputReportLength %lu\n", Capabilities.Usage, Capabilities.UsagePage, Capabilities.InputReportByteLength); + + /* verify capabilities */ + if ((Capabilities.Usage != HID_USAGE_GENERIC_POINTER && Capabilities.Usage != HID_USAGE_GENERIC_MOUSE) || Capabilities.UsagePage != HID_USAGE_PAGE_GENERIC) + { + /* not supported */ + ExFreePool(PreparsedData); + return STATUS_UNSUCCESSFUL; + } + + /* init input report*/ + DeviceExtension->ReportLength = Capabilities.InputReportByteLength; + ASSERT(DeviceExtension->ReportLength); + DeviceExtension->Report = (PCHAR)ExAllocatePool(NonPagedPool, DeviceExtension->ReportLength); + ASSERT(DeviceExtension->Report); + RtlZeroMemory(DeviceExtension->Report, DeviceExtension->ReportLength); + + /* build mdl */ + DeviceExtension->ReportMDL = IoAllocateMdl(DeviceExtension->Report, DeviceExtension->ReportLength, FALSE, FALSE, NULL); + ASSERT(DeviceExtension->ReportMDL); + + /* init mdl */ + MmBuildMdlForNonPagedPool(DeviceExtension->ReportMDL); + + /* get max number of buttons */ + Buttons = HidP_MaxUsageListLength(HidP_Input, HID_USAGE_PAGE_BUTTON, PreparsedData); + DPRINT("[MOUHID] Buttons %lu\n", Buttons); + ASSERT(Buttons > 0); + + /* now allocate an array for those buttons */ + Buffer = ExAllocatePool(NonPagedPool, sizeof(USAGE) * 4 * Buttons); + if (!Buffer) + { + /* no memory */ + ExFreePool(PreparsedData); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* init usage lists */ + RtlZeroMemory(Buffer, sizeof(USAGE) * 4 * Buttons); + DeviceExtension->CurrentUsageList = Buffer; + Buffer += Buttons; + DeviceExtension->PreviousUsageList = Buffer; + Buffer += Buttons; + DeviceExtension->MakeUsageList = Buffer; + Buffer += Buttons; + DeviceExtension->BreakUsageList = Buffer; + + /* store number of buttons */ + DeviceExtension->UsageListLength = (USHORT)Buttons; + + /* store preparsed data */ + DeviceExtension->PreparsedData = PreparsedData; + + ValueCapsLength = 1; + HidP_GetSpecificValueCaps(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_X, &ValueCaps, &ValueCapsLength, PreparsedData); + + ValueCapsLength = 1; + HidP_GetSpecificValueCaps(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_Y, &ValueCaps, &ValueCapsLength, PreparsedData); + + /* now check for wheel mouse support */ + ValueCapsLength = 1; + Status = HidP_GetSpecificValueCaps(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_WHEEL, &ValueCaps, &ValueCapsLength, PreparsedData); + if (Status == HIDP_STATUS_SUCCESS ) + { + /* mouse has wheel support */ + DeviceExtension->MouseIdentifier = WHEELMOUSE_HID_HARDWARE; + DeviceExtension->WheelUsagePage = ValueCaps.UsagePage; + DPRINT("[MOUHID] mouse wheel support detected\n", Status); + } + else + { + /* check if the mouse has z-axis */ + ValueCapsLength = 1; + Status = HidP_GetSpecificValueCaps(HidP_Input, HID_USAGE_PAGE_GENERIC, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_GENERIC_Z, &ValueCaps, &ValueCapsLength, PreparsedData); + if (Status == HIDP_STATUS_SUCCESS && ValueCapsLength == 1) + { + /* wheel support */ + DeviceExtension->MouseIdentifier = WHEELMOUSE_HID_HARDWARE; + DeviceExtension->WheelUsagePage = ValueCaps.UsagePage; + DPRINT("[MOUHID] mouse wheel support detected with z-axis\n", Status); + } + } + + /* completed successfully */ + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +MouHid_StartDeviceCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + KeSetEvent((PKEVENT)Context, 0, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +NTAPI +MouHid_Flush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PMOUHID_DEVICE_EXTENSION DeviceExtension; + + /* get device extension */ + DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* skip current stack location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* get next stack location */ + IoStack = IoGetNextIrpStackLocation(Irp); + + /* change request to hid flush queue request */ + IoStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_FLUSH_QUEUE; + + /* call device */ + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); +} + +NTSTATUS +NTAPI +MouHid_Pnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + KEVENT Event; + NTSTATUS Status; + PMOUHID_DEVICE_EXTENSION DeviceExtension; + + /* get device extension */ + DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* get current irp stack */ + IoStack = IoGetCurrentIrpStackLocation(Irp); + DPRINT("[MOUHID] IRP_MJ_PNP Request: %x\n", IoStack->MinorFunction); + + if (IoStack->MinorFunction == IRP_MN_STOP_DEVICE || + IoStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE || + IoStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE || + IoStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE || + IoStack->MinorFunction == IRP_MN_QUERY_REMOVE_DEVICE) + { + /* indicate success */ + Irp->IoStatus.Status = STATUS_SUCCESS; + + /* skip irp stack location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* dispatch to lower device */ + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + } + else if (IoStack->MinorFunction == IRP_MN_REMOVE_DEVICE) + { + /* FIXME synchronization */ + + /* request stop */ + DeviceExtension->StopReadReport = TRUE; + + /* cancel irp */ + IoCancelIrp(DeviceExtension->Irp); + + /* indicate success */ + Irp->IoStatus.Status = STATUS_SUCCESS; + + /* skip irp stack location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* dispatch to lower device */ + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + + /* wait for completion of stop event */ + KeWaitForSingleObject(&DeviceExtension->ReadCompletionEvent, Executive, KernelMode, FALSE, NULL); + + /* free irp */ + IoFreeIrp(DeviceExtension->Irp); + + /* detach device */ + IoDetachDevice(DeviceExtension->NextDeviceObject); + + /* delete device */ + IoDeleteDevice(DeviceObject); + + /* done */ + return Status; + } + else if (IoStack->MinorFunction == IRP_MN_START_DEVICE) + { + /* init event */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + /* copy stack location */ + IoCopyCurrentIrpStackLocationToNext (Irp); + + /* set completion routine */ + IoSetCompletionRoutine(Irp, MouHid_StartDeviceCompletion, &Event, TRUE, TRUE, TRUE); + Irp->IoStatus.Status = 0; + + /* pass request */ + Status = IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = Irp->IoStatus.Status; + } + + if (!NT_SUCCESS(Status)) + { + /* failed */ + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + + /* lets start the device */ + Status = MouHid_StartDevice(DeviceObject); + DPRINT("MouHid_StartDevice %x\n", Status); + + /* complete request */ + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + /* done */ + return Status; + } + else + { + /* skip irp stack location */ + IoSkipCurrentIrpStackLocation(Irp); + + /* dispatch to lower device */ + return IoCallDriver(DeviceExtension->NextDeviceObject, Irp); + } +} + +NTSTATUS +NTAPI +MouHid_AddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) +{ + NTSTATUS Status; + PDEVICE_OBJECT DeviceObject, NextDeviceObject; + PMOUHID_DEVICE_EXTENSION DeviceExtension; + POWER_STATE State; + + /* create device object */ + Status = IoCreateDevice(DriverObject, sizeof(MOUHID_DEVICE_EXTENSION), NULL, FILE_DEVICE_MOUSE, 0, FALSE, &DeviceObject); + if (!NT_SUCCESS(Status)) + { + /* failed to create device object */ + return Status; + } + + /* now attach it */ + NextDeviceObject = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject); + if (!NextDeviceObject) + { + /* failed to attach */ + IoDeleteDevice(DeviceObject); + return STATUS_DEVICE_NOT_CONNECTED; + } + + /* get device extension */ + DeviceExtension = (PMOUHID_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + /* zero extension */ + RtlZeroMemory(DeviceExtension, sizeof(MOUHID_DEVICE_EXTENSION)); + + /* init device extension */ + DeviceExtension->MouseIdentifier = MOUSE_HID_HARDWARE; + DeviceExtension->WheelUsagePage = 0; + DeviceExtension->NextDeviceObject = NextDeviceObject; + KeInitializeEvent(&DeviceExtension->ReadCompletionEvent, NotificationEvent, FALSE); + DeviceExtension->Irp = IoAllocateIrp(NextDeviceObject->StackSize, FALSE); + + /* FIXME handle allocation error */ + ASSERT(DeviceExtension->Irp); + + /* FIXME query parameter 'FlipFlopWheel', 'WheelScalingFactor' */ + + /* set power state to D0 */ + State.DeviceState = PowerDeviceD0; + PoSetPowerState(DeviceObject, DevicePowerState, State); + + /* init device object */ + DeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; + DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + /* completed successfully */ + return STATUS_SUCCESS; +} + +VOID +NTAPI +MouHid_Unload( + IN PDRIVER_OBJECT DriverObject) +{ + UNIMPLEMENTED +} + + +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegPath) +{ + /* FIXME check for parameters 'UseOnlyMice', 'TreatAbsoluteAsRelative', 'TreatAbsolutePointerAsAbsolute' */ + + /* initialize driver object */ + DriverObject->DriverUnload = MouHid_Unload; + DriverObject->DriverExtension->AddDevice = MouHid_AddDevice; + DriverObject->MajorFunction[IRP_MJ_CREATE] = MouHid_Create; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = MouHid_Close; + DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = MouHid_Flush; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MouHid_DeviceControl; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MouHid_InternalDeviceControl; + DriverObject->MajorFunction[IRP_MJ_POWER] = MouHid_Power; + DriverObject->MajorFunction[IRP_MJ_PNP] = MouHid_Pnp; + DriverObject->DriverUnload = MouHid_Unload; + DriverObject->DriverExtension->AddDevice = MouHid_AddDevice; + + /* done */ + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/hid/mouhid/mouhid.h b/reactos/drivers/hid/mouhid/mouhid.h new file mode 100644 index 00000000000..78048fdce5d --- /dev/null +++ b/reactos/drivers/hid/mouhid/mouhid.h @@ -0,0 +1,118 @@ +#pragma once + +#define _HIDPI_NO_FUNCTION_MACROS_ +#include +#include +#include +#include +#define NDEBUG +#include +#include +#include +#include + + +typedef struct +{ + // + // lower device object + // + PDEVICE_OBJECT NextDeviceObject; + + // + // irp which is used for reading input reports + // + PIRP Irp; + + // + // event + // + KEVENT ReadCompletionEvent; + + // + // device object for class callback + // + PDEVICE_OBJECT ClassDeviceObject; + + // + // class callback + // + PVOID ClassService; + + // + // mouse type + // + USHORT MouseIdentifier; + + // + // wheel usage page + // + USHORT WheelUsagePage; + + // + // usage list length + // + USHORT UsageListLength; + + // + // current usage list length + // + PUSAGE CurrentUsageList; + + // + // previous usage list + // + PUSAGE PreviousUsageList; + + // + // removed usage item list + // + PUSAGE BreakUsageList; + + // + // new item usage list + // + PUSAGE MakeUsageList; + + // + // preparsed data + // + PVOID PreparsedData; + + // + // mdl for reading input report + // + PMDL ReportMDL; + + // + // input report buffer + // + PCHAR Report; + + // + // input report length + // + ULONG ReportLength; + + // + // file object the device is reading reports from + // + PFILE_OBJECT FileObject; + + // + // report read is active + // + UCHAR ReadReportActive; + + // + // stop reading flag + // + UCHAR StopReadReport; + +}MOUHID_DEVICE_EXTENSION, *PMOUHID_DEVICE_EXTENSION; + +#define WHEEL_DELTA 120 + +NTSTATUS +MouHid_InitiateRead( + IN PMOUHID_DEVICE_EXTENSION DeviceExtension); diff --git a/reactos/drivers/hid/mouhid/mouhid.rbuild b/reactos/drivers/hid/mouhid/mouhid.rbuild new file mode 100644 index 00000000000..cfb1f95bfa0 --- /dev/null +++ b/reactos/drivers/hid/mouhid/mouhid.rbuild @@ -0,0 +1,11 @@ + + + + + include + ntoskrnl + hal + hidparse + mouhid.c + mouhid.rc + diff --git a/reactos/drivers/hid/mouhid/mouhid.rc b/reactos/drivers/hid/mouhid/mouhid.rc new file mode 100644 index 00000000000..ae82d18f99b --- /dev/null +++ b/reactos/drivers/hid/mouhid/mouhid.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "Mouse HID Class Driver\0" +#define REACTOS_STR_INTERNAL_NAME "mouhid\0" +#define REACTOS_STR_ORIGINAL_FILENAME "mouhid.sys\0" +#include diff --git a/reactos/drivers/input/kbdclass/kbdclass.c b/reactos/drivers/input/kbdclass/kbdclass.c index 3cf6c9511af..e1bbe5830a9 100644 --- a/reactos/drivers/input/kbdclass/kbdclass.c +++ b/reactos/drivers/input/kbdclass/kbdclass.c @@ -824,6 +824,72 @@ HandleReadIrp( return Status; } +static NTSTATUS NTAPI +ClassPnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PPORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK Iosb; + NTSTATUS Status; + + switch (IrpSp->MinorFunction) + { + case IRP_MN_START_DEVICE: + Status = ForwardIrpAndWait(DeviceObject, Irp); + if (NT_SUCCESS(Status)) + { + InitializeObjectAttributes(&ObjectAttributes, + &DeviceExtension->InterfaceName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwOpenFile(&DeviceExtension->FileHandle, + FILE_READ_DATA, + &ObjectAttributes, + &Iosb, + 0, + 0); + if (!NT_SUCCESS(Status)) + DeviceExtension->FileHandle = NULL; + } + else + DeviceExtension->FileHandle = NULL; + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + + case IRP_MN_REMOVE_DEVICE: + case IRP_MN_STOP_DEVICE: + if (DeviceExtension->FileHandle) + { + ZwClose(DeviceExtension->FileHandle); + DeviceExtension->FileHandle = NULL; + } + Status = STATUS_SUCCESS; + break; + + default: + Status = Irp->IoStatus.Status; + break; + } + + Irp->IoStatus.Status = Status; + if (NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED) + { + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(DeviceExtension->LowerDevice, Irp); + } + else + { + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } +} + static VOID NTAPI ClassStartIo( IN PDEVICE_OBJECT DeviceObject, @@ -1021,6 +1087,7 @@ DriverEntry( DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClassCleanup; DriverObject->MajorFunction[IRP_MJ_READ] = ClassRead; + DriverObject->MajorFunction[IRP_MJ_PNP] = ClassPnp; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl; DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget; DriverObject->DriverStartIo = ClassStartIo; diff --git a/reactos/drivers/input/kbdclass/kbdclass.h b/reactos/drivers/input/kbdclass/kbdclass.h index f68386e2b6d..8defeb68d14 100644 --- a/reactos/drivers/input/kbdclass/kbdclass.h +++ b/reactos/drivers/input/kbdclass/kbdclass.h @@ -48,6 +48,7 @@ typedef struct _PORT_DEVICE_EXTENSION PORT_DEVICE_STATE PnpState; PDEVICE_OBJECT LowerDevice; PDEVICE_OBJECT ClassDO; + HANDLE FileHandle; UNICODE_STRING InterfaceName; } PORT_DEVICE_EXTENSION, *PPORT_DEVICE_EXTENSION; diff --git a/reactos/drivers/input/mouclass/mouclass.c b/reactos/drivers/input/mouclass/mouclass.c index 99818bb6b5e..58f957f01aa 100644 --- a/reactos/drivers/input/mouclass/mouclass.c +++ b/reactos/drivers/input/mouclass/mouclass.c @@ -800,6 +800,72 @@ HandleReadIrp( return Status; } +static NTSTATUS NTAPI +ClassPnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PPORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK Iosb; + NTSTATUS Status; + + switch (IrpSp->MinorFunction) + { + case IRP_MN_START_DEVICE: + Status = ForwardIrpAndWait(DeviceObject, Irp); + if (NT_SUCCESS(Status)) + { + InitializeObjectAttributes(&ObjectAttributes, + &DeviceExtension->InterfaceName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwOpenFile(&DeviceExtension->FileHandle, + FILE_READ_DATA, + &ObjectAttributes, + &Iosb, + 0, + 0); + if (!NT_SUCCESS(Status)) + DeviceExtension->FileHandle = NULL; + } + else + DeviceExtension->FileHandle = NULL; + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + + case IRP_MN_REMOVE_DEVICE: + case IRP_MN_STOP_DEVICE: + if (DeviceExtension->FileHandle) + { + ZwClose(DeviceExtension->FileHandle); + DeviceExtension->FileHandle = NULL; + } + Status = STATUS_SUCCESS; + break; + + default: + Status = Irp->IoStatus.Status; + break; + } + + Irp->IoStatus.Status = Status; + if (NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED) + { + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(DeviceExtension->LowerDevice, Irp); + } + else + { + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } +} + static VOID NTAPI ClassStartIo( IN PDEVICE_OBJECT DeviceObject, @@ -993,6 +1059,7 @@ DriverEntry( DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClassCleanup; DriverObject->MajorFunction[IRP_MJ_READ] = ClassRead; + DriverObject->MajorFunction[IRP_MJ_PNP] = ClassPnp; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl; DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget; DriverObject->DriverStartIo = ClassStartIo; diff --git a/reactos/drivers/input/mouclass/mouclass.h b/reactos/drivers/input/mouclass/mouclass.h index cd70beefead..5bf7e15c7e5 100644 --- a/reactos/drivers/input/mouclass/mouclass.h +++ b/reactos/drivers/input/mouclass/mouclass.h @@ -48,6 +48,7 @@ typedef struct _PORT_DEVICE_EXTENSION PORT_DEVICE_STATE PnpState; PDEVICE_OBJECT LowerDevice; PDEVICE_OBJECT ClassDO; + HANDLE FileHandle; UNICODE_STRING InterfaceName; } PORT_DEVICE_EXTENSION, *PPORT_DEVICE_EXTENSION; diff --git a/reactos/drivers/storage/class/CMakeLists.txt b/reactos/drivers/storage/class/CMakeLists.txt index d3fd268b523..a7e0bf22bed 100644 --- a/reactos/drivers/storage/class/CMakeLists.txt +++ b/reactos/drivers/storage/class/CMakeLists.txt @@ -2,4 +2,5 @@ add_subdirectory(cdrom) add_subdirectory(class2) add_subdirectory(disk) +add_subdirectory(disk_new) add_subdirectory(ramdisk) diff --git a/reactos/drivers/storage/class/class2/class2.c b/reactos/drivers/storage/class/class2/class2.c index 60268748ab9..e6528c061f2 100644 --- a/reactos/drivers/storage/class/class2/class2.c +++ b/reactos/drivers/storage/class/class2/class2.c @@ -12,6 +12,10 @@ #include #include +/* Part of the drive letter hack */ +#include +#include + //#define NDEBUG #include @@ -130,6 +134,272 @@ DriverEntry( return STATUS_SUCCESS; } +/* The following hack to assign drive letters with a non-PnP storage stack */ + +typedef struct _CLASS_DEVICE_INFO { + ULONG Partitions; + ULONG DeviceNumber; + ULONG DriveNumber; + PDEVICE_OBJECT LowerDevice; +} CLASS_DEVICE_INFO, *PCLASS_DEVICE_INFO; + +typedef struct _CLASS_DRIVER_EXTENSION { + ULONG PortNumber; + CLASS_INIT_DATA InitializationData; +} CLASS_DRIVER_EXTENSION, *PCLASS_DRIVER_EXTENSION; + +VOID +NTAPI +ScsiClassRemoveDriveLetter(PCLASS_DEVICE_INFO DeviceInfo) +{ + WCHAR Buffer1[100]; + UNICODE_STRING DriveLetterU; + ULONG Index; + + DriveLetterU.Buffer = Buffer1; + DriveLetterU.MaximumLength = sizeof(Buffer1); + + /* Delete the symbolic link to PhysicalDriveX */ + DriveLetterU.Length = swprintf(DriveLetterU.Buffer, L"\\??\\PhysicalDrive%d", DeviceInfo->DriveNumber) * sizeof(WCHAR); + IoDeleteSymbolicLink(&DriveLetterU); + + DbgPrint("HACK: Deleted symbolic link %wZ\n", &DriveLetterU); + + for (Index = 0; Index < sizeof(ULONG) * 8; Index++) + { + if (DeviceInfo->Partitions & (1 << Index)) + { + DriveLetterU.Length = swprintf(DriveLetterU.Buffer, L"\\??\\%C:", ('C' + Index)) * sizeof(WCHAR); + IoDeleteSymbolicLink(&DriveLetterU); + DbgPrint("HACK: Deleted symbolic link %wZ\n", &DriveLetterU); + } + } +} + +NTSTATUS +NTAPI +ScsiClassAssignDriveLetter(PCLASS_DEVICE_INFO DeviceInfo) +{ + WCHAR Buffer1[100]; + WCHAR Buffer2[100]; + UNICODE_STRING DriveLetterU, PartitionU; + NTSTATUS Status; + ULONG Index, PartitionNumber, DeviceNumber, DriveNumber; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK Iosb; + HANDLE PartitionHandle; + + /* We assume this device does not current have a drive letter */ + + Index = 0; + DeviceNumber = 0; + DriveNumber = 0; + PartitionNumber = 1; + DriveLetterU.Buffer = Buffer1; + DriveLetterU.MaximumLength = sizeof(Buffer1); + PartitionU.Buffer = Buffer2; + PartitionU.MaximumLength = sizeof(Buffer2); + + /* Determine the correct disk number */ + do + { + /* Check that the disk exists */ + PartitionU.Length = swprintf(PartitionU.Buffer, L"\\Device\\HardDisk%d\\Partition0", DeviceNumber) * sizeof(WCHAR); + InitializeObjectAttributes(&ObjectAttributes, + &PartitionU, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + Status = ZwOpenFile(&PartitionHandle, + FILE_READ_ATTRIBUTES, + &ObjectAttributes, + &Iosb, + 0, + 0); + if (!NT_SUCCESS(Status)) + { + /* Return the last one that worked */ + DeviceNumber--; + } + else + { + ZwClose(PartitionHandle); + DeviceNumber++; + } + } while (Status == STATUS_SUCCESS); + + /* Determine the correct drive number */ + do + { + /* Check that the drive exists */ + PartitionU.Length = swprintf(PartitionU.Buffer, L"\\??\\PhysicalDrive%d", DriveNumber) * sizeof(WCHAR); + InitializeObjectAttributes(&ObjectAttributes, + &PartitionU, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + Status = ZwOpenFile(&PartitionHandle, + FILE_READ_ATTRIBUTES, + &ObjectAttributes, + &Iosb, + 0, + 0); + if (NT_SUCCESS(Status)) + { + ZwClose(PartitionHandle); + DriveNumber++; + } + } while (Status == STATUS_SUCCESS); + + /* Create the symbolic link to PhysicalDriveX */ + PartitionU.Length = swprintf(PartitionU.Buffer, L"\\Device\\Harddisk%d\\Partition0", DeviceNumber) * sizeof(WCHAR); + DriveLetterU.Length = swprintf(DriveLetterU.Buffer, L"\\??\\PhysicalDrive%d", DriveNumber) * sizeof(WCHAR); + + Status = IoCreateSymbolicLink(&DriveLetterU, &PartitionU); + if (!NT_SUCCESS(Status)) + { + /* Failed to create symbolic link */ + return Status; + } + + DbgPrint("HACK: Created symbolic link %wZ -> %wZ\n", &PartitionU, &DriveLetterU); + + while (TRUE) + { + /* Check that the disk exists */ + PartitionU.Length = swprintf(PartitionU.Buffer, L"\\Device\\Harddisk%d\\Partition%d", DeviceNumber, PartitionNumber) * sizeof(WCHAR); + InitializeObjectAttributes(&ObjectAttributes, + &PartitionU, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + Status = ZwOpenFile(&PartitionHandle, + FILE_READ_ATTRIBUTES, + &ObjectAttributes, + &Iosb, + 0, + 0); + if (!NT_SUCCESS(Status)) + break; + else + { + ZwClose(PartitionHandle); + + /* Assign it a drive letter */ + do + { + DriveLetterU.Length = swprintf(DriveLetterU.Buffer, L"\\??\\%C:", ('C' + Index)) * sizeof(WCHAR); + + Status = IoCreateSymbolicLink(&DriveLetterU, &PartitionU); + + Index++; + } while (Status != STATUS_SUCCESS); + + DeviceInfo->Partitions |= (1 << (Index - 1)); + + DbgPrint("HACK: Created symbolic link %wZ -> %wZ\n", &PartitionU, &DriveLetterU); + PartitionNumber++; + } + } + + DeviceInfo->DeviceNumber = DeviceNumber; + DeviceInfo->DriveNumber = DriveNumber; + + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +ScsiClassPlugPlay( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + + if (IrpSp->MinorFunction == IRP_MN_START_DEVICE) + { + IoSkipCurrentIrpStackLocation(Irp); + return STATUS_SUCCESS; + } + else if (IrpSp->MinorFunction == IRP_MN_REMOVE_DEVICE) + { + PCLASS_DEVICE_INFO DeviceInfo = DeviceObject->DeviceExtension; + + ScsiClassRemoveDriveLetter(DeviceInfo); + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + IoDetachDevice(DeviceInfo->LowerDevice); + IoDeleteDevice(DeviceObject); + return STATUS_SUCCESS; + } + else + { + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_SUPPORTED; + } +} + +NTSTATUS +NTAPI +ScsiClassAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) +{ + PCLASS_DRIVER_EXTENSION DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject); + PCLASS_DEVICE_INFO DeviceInfo; + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status; + + if (DriverExtension->InitializationData.ClassFindDevices(DriverObject, NULL, &DriverExtension->InitializationData, + PhysicalDeviceObject, DriverExtension->PortNumber)) + { + /* Create a device object */ + Status = IoCreateDevice(DriverObject, + sizeof(CLASS_DEVICE_INFO), + NULL, + FILE_DEVICE_DISK, + 0, + FALSE, + &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + DeviceInfo = DeviceObject->DeviceExtension; + RtlZeroMemory(DeviceInfo, sizeof(CLASS_DEVICE_INFO)); + + /* Attach it to the PDO */ + DeviceInfo->LowerDevice = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject); + + /* Check that the kernel has already assigned drive letters */ + if (KeLoaderBlock == NULL) + { + /* Assign a drive letter */ + ScsiClassAssignDriveLetter(DeviceInfo); + } + else + { + /* The kernel will handle it */ + } + + /* Move to the next port number */ + DriverExtension->PortNumber++; + } + else + { + /* Failed to find device */ + DbgPrint("FAILED TO FIND DEVICE!\n"); + } + + return STATUS_SUCCESS; +} +/* ---- End hack ---- */ + + ULONG NTAPI @@ -162,7 +432,6 @@ Return Value: PDRIVER_OBJECT DriverObject = Argument1; - ULONG portNumber = 0; PDEVICE_OBJECT portDeviceObject; NTSTATUS status; STRING deviceNameString; @@ -170,6 +439,7 @@ Return Value: PFILE_OBJECT fileObject; CCHAR deviceNameBuffer[256]; BOOLEAN deviceFound = FALSE; + PCLASS_DRIVER_EXTENSION DriverExtension; DebugPrint((3,"\n\nSCSI Class Driver\n")); @@ -200,6 +470,16 @@ Return Value: return (ULONG) STATUS_REVISION_MISMATCH; } + status = IoAllocateDriverObjectExtension(DriverObject, + DriverObject, + sizeof(CLASS_DRIVER_EXTENSION), + (PVOID *)&DriverExtension); + if (!NT_SUCCESS(status)) + return status; + + RtlCopyMemory(&DriverExtension->InitializationData, InitializationData, sizeof(CLASS_INIT_DATA)); + DriverExtension->PortNumber = 0; + // // Update driver object with entry points. // @@ -208,10 +488,12 @@ Return Value: DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiClassCreateClose; DriverObject->MajorFunction[IRP_MJ_READ] = ScsiClassReadWrite; DriverObject->MajorFunction[IRP_MJ_WRITE] = ScsiClassReadWrite; + DriverObject->MajorFunction[IRP_MJ_PNP] = ScsiClassPlugPlay; DriverObject->MajorFunction[IRP_MJ_SCSI] = ScsiClassInternalIoControl; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiClassDeviceControlDispatch; DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ScsiClassShutdownFlush; DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ScsiClassShutdownFlush; + DriverObject->DriverExtension->AddDevice = ScsiClassAddDevice; if (InitializationData->ClassStartIo) { DriverObject->DriverStartIo = InitializationData->ClassStartIo; @@ -223,7 +505,7 @@ Return Value: do { - sprintf(deviceNameBuffer, "\\Device\\ScsiPort%lu", portNumber); + sprintf(deviceNameBuffer, "\\Device\\ScsiPort%lu", DriverExtension->PortNumber); DebugPrint((2, "ScsiClassInitialize: Open Port %s\n", deviceNameBuffer)); @@ -249,7 +531,7 @@ Return Value: // if (InitializationData->ClassFindDevices(DriverObject, Argument2, InitializationData, - portDeviceObject, portNumber)) { + portDeviceObject, DriverExtension->PortNumber)) { deviceFound = TRUE; } @@ -259,11 +541,12 @@ Return Value: // Check next SCSI adapter. // - portNumber++; + DriverExtension->PortNumber++; } while(NT_SUCCESS(status)); - return deviceFound ? STATUS_SUCCESS : STATUS_NO_SUCH_DEVICE; + /* We don't want to fail init just because we don't have devices right now */ + return STATUS_SUCCESS; /*deviceFound ? STATUS_SUCCESS : STATUS_NO_SUCH_DEVICE;*/ } diff --git a/reactos/drivers/storage/class/disk_new/CMakeLists.txt b/reactos/drivers/storage/class/disk_new/CMakeLists.txt new file mode 100644 index 00000000000..f97376b5545 --- /dev/null +++ b/reactos/drivers/storage/class/disk_new/CMakeLists.txt @@ -0,0 +1,12 @@ +include_directories(..) + +add_library(disk_new SHARED data.c disk.c diskwmi.c enum.c geometry.c part.c pnp.c disk.rc) + +target_link_libraries(disk_new libcntpr wdmguid) +if(NOT MSVC AND ARCH MATCHES i386) + add_target_compile_flags(disk_new "-mrtd -fno-builtin -Wno-unused-variable -Wno-pointer-sign") +endif() + +set_module_type(disk_new kernelmodedriver) +add_importlibs(disk_new classpnp ntoskrnl hal) +add_cd_file(TARGET disk_new DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/reactos/drivers/storage/class/disk_new/data.c b/reactos/drivers/storage/class/disk_new/data.c new file mode 100644 index 00000000000..c2fab4fea00 --- /dev/null +++ b/reactos/drivers/storage/class/disk_new/data.c @@ -0,0 +1,82 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + disk.c + +Abstract: + + SCSI disk class driver + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "disk.h" + +#ifdef ALLOC_DATA_PRAGMA +#pragma data_seg("PAGE") +#endif + +/* +#define HackDisableTaggedQueuing (0x01) +#define HackDisableSynchronousTransfers (0x02) +#define HackDisableSpinDown (0x04) +#define HackDisableWriteCache (0x08) +#define HackCauseNotReportableHack (0x10) +#define HackRequiresStartUnitCommand (0x20) +*/ + +CLASSPNP_SCAN_FOR_SPECIAL_INFO DiskBadControllers[] = { + { "COMPAQ" , "PD-1" , NULL, 0x02 }, + { "CONNER" , "CP3500" , NULL, 0x02 }, + { "FUJITSU" , "M2652S-512" , NULL, 0x01 }, + { "HP ", "C1113F " , NULL, 0x20 }, + // iomegas require START_UNIT commands so be sure to match all of them. + { "iomega" , "jaz" , NULL, 0x30 }, + { "iomega" , NULL , NULL, 0x20 }, + { "IOMEGA" , "ZIP" , NULL, 0x27 }, + { "IOMEGA" , NULL , NULL, 0x20 }, + { "MAXTOR" , "MXT-540SL" , "I1.2", 0x01 }, + { "MICROP" , "1936-21MW1002002" , NULL, 0x03 }, + { "OLIVETTI", "CP3500" , NULL, 0x02 }, + { "SEAGATE" , "ST41601N" , "0102", 0x02 }, + { "SEAGATE" , "ST3655N" , NULL, 0x08 }, + { "SEAGATE" , "ST3390N" , NULL, 0x08 }, + { "SEAGATE" , "ST12550N" , NULL, 0x08 }, + { "SEAGATE" , "ST32430N" , NULL, 0x08 }, + { "SEAGATE" , "ST31230N" , NULL, 0x08 }, + { "SEAGATE" , "ST15230N" , NULL, 0x08 }, + { "SyQuest" , "SQ5110" , "CHC", 0x03 }, + { "TOSHIBA" , "MK538FB" , "60", 0x01 }, + { NULL , NULL , NULL, 0x0 } +}; + +// +// ======== ROS DIFF ======== +// Added MediaTypes in their own brace nesting level +// ======== ROS DIFF ======== +// +DISK_MEDIA_TYPES_LIST const DiskMediaTypes[] = { + { "COMPAQ" , "PD-1 LF-1094" , NULL, 1, 1, {PC_5_RW , 0 , 0 , 0 }}, + { "HP" , NULL , NULL, 2, 2, {MO_5_WO , MO_5_RW, 0 , 0 }}, + { "iomega" , "jaz" , NULL, 1, 1, {IOMEGA_JAZ , 0 , 0 , 0 }}, + { "IOMEGA" , "ZIP" , NULL, 1, 1, {IOMEGA_ZIP , 0 , 0 , 0 }}, + { "PINNACLE", "Apex 4.6GB" , NULL, 3, 2, {PINNACLE_APEX_5_RW, MO_5_RW, MO_5_WO, 0 }}, + { "SONY" , "SMO-F541" , NULL, 2, 2, {MO_5_WO , MO_5_RW, 0 , 0 }}, + { "SONY" , "SMO-F551" , NULL, 2, 2, {MO_5_WO , MO_5_RW, 0 , 0 }}, + { NULL , NULL , NULL, 0, 0, {0 , 0 , 0 , 0 }} +}; + +#ifdef ALLOC_DATA_PRAGMA +#pragma data_seg() +#endif + diff --git a/reactos/drivers/storage/class/disk_new/disk.c b/reactos/drivers/storage/class/disk_new/disk.c new file mode 100644 index 00000000000..7bd390d447f --- /dev/null +++ b/reactos/drivers/storage/class/disk_new/disk.c @@ -0,0 +1,6576 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + disk.c + +Abstract: + + SCSI disk class driver + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "disk.h" + +// +// Now instantiate the GUIDs +// + +#include "initguid.h" +#include "ntddstor.h" +#include "ioevent.h" + +NTSTATUS +DiskDetermineMediaTypes( + IN PDEVICE_OBJECT Fdo, + IN PIRP Irp, + IN UCHAR MediumType, + IN UCHAR DensityCode, + IN BOOLEAN MediaPresent, + IN BOOLEAN IsWritable + ); + +PPARTITION_INFORMATION_EX +DiskPdoFindPartitionEntry( + IN PPHYSICAL_DEVICE_EXTENSION Pdo, + IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo + ); + +PPARTITION_INFORMATION_EX +DiskFindAdjacentPartition( + IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo, + IN PPARTITION_INFORMATION_EX BasePartition + ); + +PPARTITION_INFORMATION_EX +DiskFindContainingPartition( + IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo, + IN PPARTITION_INFORMATION_EX BasePartition, + IN BOOLEAN SearchTopToBottom + ); + +NTSTATUS +DiskIoctlCreateDisk( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskIoctlGetDriveLayout( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskIoctlGetDriveLayoutEx( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskIoctlSetDriveLayout( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskIoctlSetDriveLayoutEx( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskIoctlGetPartitionInfo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskIoctlGetPartitionInfoEx( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskIoctlGetLengthInfo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskIoctlSetPartitionInfo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskIoctlSetPartitionInfoEx( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskIoctlSetPartitionInfoEx( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskIoctlGetDriveGeometryEx( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, DiskUnload) +#pragma alloc_text(PAGE, DiskCreateFdo) +#pragma alloc_text(PAGE, DiskDetermineMediaTypes) +#pragma alloc_text(PAGE, DiskModeSelect) +#pragma alloc_text(PAGE, DisableWriteCache) +#pragma alloc_text(PAGE, DiskIoctlVerify) +#pragma alloc_text(PAGE, DiskSetSpecialHacks) +#pragma alloc_text(PAGE, DiskScanRegistryForSpecial) +#pragma alloc_text(PAGE, DiskQueryPnpCapabilities) +#pragma alloc_text(PAGE, DiskGetCacheInformation) +#pragma alloc_text(PAGE, DiskSetCacheInformation) +#pragma alloc_text(PAGE, DiskSetInfoExceptionInformation) +#pragma alloc_text(PAGE, DiskGetInfoExceptionInformation) + +#pragma alloc_text(PAGE, DiskPdoFindPartitionEntry) +#pragma alloc_text(PAGE, DiskFindAdjacentPartition) +#pragma alloc_text(PAGE, DiskFindContainingPartition) + +#pragma alloc_text(PAGE, DiskIoctlCreateDisk) +#pragma alloc_text(PAGE, DiskIoctlGetDriveLayout) +#pragma alloc_text(PAGE, DiskIoctlGetDriveLayoutEx) +#pragma alloc_text(PAGE, DiskIoctlSetDriveLayout) +#pragma alloc_text(PAGE, DiskIoctlSetDriveLayoutEx) +#pragma alloc_text(PAGE, DiskIoctlGetPartitionInfo) +#pragma alloc_text(PAGE, DiskIoctlGetPartitionInfoEx) +#pragma alloc_text(PAGE, DiskIoctlGetLengthInfo) +#pragma alloc_text(PAGE, DiskIoctlSetPartitionInfo) +#pragma alloc_text(PAGE, DiskIoctlSetPartitionInfoEx) +#pragma alloc_text(PAGE, DiskIoctlGetDriveGeometryEx) +#endif + +extern ULONG DiskDisableGpt; + +const GUID GUID_NULL = { 0 }; +#define DiskCompareGuid(_First,_Second) \ + (memcmp ((_First),(_Second), sizeof (GUID))) + + +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine initializes the SCSI hard disk class driver. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + RegistryPath - Pointer to the name of the services node for this driver. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + CLASS_INIT_DATA InitializationData; + CLASS_QUERY_WMI_REGINFO_EX_LIST classQueryWmiRegInfoExList; + GUID guidQueryRegInfoEx = GUID_CLASSPNP_QUERY_REGINFOEX; + + NTSTATUS status; + +#if defined(_X86_) + // + // Read the information NtDetect squirreled away about the disks in this + // system. + // + + status = DiskSaveDetectInfo(DriverObject); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "Disk: couldn't save NtDetect information (%#08lx)\n", + status)); + } +#endif + + // + // Zero InitData + // + + RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA)); + + InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA); + + // + // Setup sizes and entry points for functional device objects + // + + InitializationData.FdoData.DeviceExtensionSize = FUNCTIONAL_EXTENSION_SIZE; + InitializationData.FdoData.DeviceType = FILE_DEVICE_DISK; + InitializationData.FdoData.DeviceCharacteristics = FILE_DEVICE_SECURE_OPEN; + + InitializationData.FdoData.ClassInitDevice = DiskInitFdo; + InitializationData.FdoData.ClassStartDevice = DiskStartFdo; + InitializationData.FdoData.ClassStopDevice = DiskStopDevice; + InitializationData.FdoData.ClassRemoveDevice = DiskRemoveDevice; + InitializationData.FdoData.ClassPowerDevice = ClassSpinDownPowerHandler; + + InitializationData.FdoData.ClassError = DiskFdoProcessError; + InitializationData.FdoData.ClassReadWriteVerification = DiskReadWriteVerification; + InitializationData.FdoData.ClassDeviceControl = DiskDeviceControl; + InitializationData.FdoData.ClassShutdownFlush = DiskShutdownFlush; + InitializationData.FdoData.ClassCreateClose = NULL; + + // + // Setup sizes and entry points for physical device objects + // + + InitializationData.PdoData.DeviceExtensionSize = PHYSICAL_EXTENSION_SIZE; + InitializationData.PdoData.DeviceType = FILE_DEVICE_DISK; + InitializationData.PdoData.DeviceCharacteristics = FILE_DEVICE_SECURE_OPEN; + + InitializationData.PdoData.ClassInitDevice = DiskInitPdo; + InitializationData.PdoData.ClassStartDevice = DiskStartPdo; + InitializationData.PdoData.ClassStopDevice = DiskStopDevice; + InitializationData.PdoData.ClassRemoveDevice = DiskRemoveDevice; + + // + // Use default power routine for PDOs + // + + InitializationData.PdoData.ClassPowerDevice = NULL; + + InitializationData.PdoData.ClassError = NULL; + InitializationData.PdoData.ClassReadWriteVerification = DiskReadWriteVerification; + InitializationData.PdoData.ClassDeviceControl = DiskDeviceControl; + InitializationData.PdoData.ClassShutdownFlush = DiskShutdownFlush; + InitializationData.PdoData.ClassCreateClose = NULL; + + InitializationData.PdoData.ClassDeviceControl = DiskDeviceControl; + + InitializationData.PdoData.ClassQueryPnpCapabilities = DiskQueryPnpCapabilities; + + InitializationData.ClassAddDevice = DiskAddDevice; + InitializationData.ClassEnumerateDevice = DiskEnumerateDevice; + + InitializationData.ClassQueryId = DiskQueryId; + + + InitializationData.FdoData.ClassWmiInfo.GuidCount = 7; + InitializationData.FdoData.ClassWmiInfo.GuidRegInfo = DiskWmiFdoGuidList; + InitializationData.FdoData.ClassWmiInfo.ClassQueryWmiRegInfo = DiskFdoQueryWmiRegInfo; + InitializationData.FdoData.ClassWmiInfo.ClassQueryWmiDataBlock = DiskFdoQueryWmiDataBlock; + InitializationData.FdoData.ClassWmiInfo.ClassSetWmiDataBlock = DiskFdoSetWmiDataBlock; + InitializationData.FdoData.ClassWmiInfo.ClassSetWmiDataItem = DiskFdoSetWmiDataItem; + InitializationData.FdoData.ClassWmiInfo.ClassExecuteWmiMethod = DiskFdoExecuteWmiMethod; + InitializationData.FdoData.ClassWmiInfo.ClassWmiFunctionControl = DiskWmiFunctionControl; + + +#if 0 + // + // Enable this to add WMI support for PDOs + InitializationData.PdoData.ClassWmiInfo.GuidCount = 1; + InitializationData.PdoData.ClassWmiInfo.GuidRegInfo = DiskWmiPdoGuidList; + InitializationData.PdoData.ClassWmiInfo.ClassQueryWmiRegInfo = DiskPdoQueryWmiRegInfo; + InitializationData.PdoData.ClassWmiInfo.ClassQueryWmiDataBlock = DiskPdoQueryWmiDataBlock; + InitializationData.PdoData.ClassWmiInfo.ClassSetWmiDataBlock = DiskPdoSetWmiDataBlock; + InitializationData.PdoData.ClassWmiInfo.ClassSetWmiDataItem = DiskPdoSetWmiDataItem; + InitializationData.PdoData.ClassWmiInfo.ClassExecuteWmiMethod = DiskPdoExecuteWmiMethod; + InitializationData.PdoData.ClassWmiInfo.ClassWmiFunctionControl = DiskWmiFunctionControl; +#endif + + InitializationData.ClassUnload = DiskUnload; + + // + // Initialize regregistration data structures + // + + DiskInitializeReregistration(); + + // + // Call the class init routine + // + + status = ClassInitialize( DriverObject, RegistryPath, &InitializationData); + +#if defined(_X86_) + if(NT_SUCCESS(status)) { + IoRegisterBootDriverReinitialization(DriverObject, + DiskDriverReinitialization, + NULL); + } +#endif + + // + // Call class init Ex routine to register a + // PCLASS_QUERY_WMI_REGINFO_EX routine + // + RtlZeroMemory(&classQueryWmiRegInfoExList, sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST)); + classQueryWmiRegInfoExList.Size = sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST); + classQueryWmiRegInfoExList.ClassFdoQueryWmiRegInfoEx = DiskFdoQueryWmiRegInfoEx; + + ClassInitializeEx(DriverObject, + &guidQueryRegInfoEx, + &classQueryWmiRegInfoExList); + + return status; + +} // end DriverEntry() + + +VOID +DiskUnload( + IN PDRIVER_OBJECT DriverObject + ) +{ + PAGED_CODE(); + +#if defined(_X86_) + DiskCleanupDetectInfo(DriverObject); +#endif + return; +} + + +NTSTATUS +DiskCreateFdo( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject, + IN PULONG DeviceCount, + IN BOOLEAN DasdAccessOnly + ) + +/*++ + +Routine Description: + + This routine creates an object for the functional device + +Arguments: + + DriverObject - Pointer to driver object created by system. + + PhysicalDeviceObject - Lower level driver we should attach to + + DeviceCount - Number of previously installed devices. + + DasdAccessOnly - indicates whether or not a file system is allowed to mount + on this device object. Used to avoid double-mounting of + file systems on super-floppies (which can unfortunately be + fixed disks). If set the i/o system will only allow rawfs + to be mounted. + +Return Value: + + NTSTATUS + +--*/ + +{ + CCHAR ntNameBuffer[MAXIMUM_FILENAME_LENGTH]; + STRING ntNameString; + UNICODE_STRING ntUnicodeString; + + PUCHAR deviceName = NULL; + + OBJECT_ATTRIBUTES objectAttributes; + HANDLE handle; + + NTSTATUS status; + + PDEVICE_OBJECT lowerDevice = NULL; + PDEVICE_OBJECT deviceObject = NULL; + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + STORAGE_PROPERTY_ID propertyId; + PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; + + PAGED_CODE(); + + *DeviceCount = 0; + + // + // Set up an object directory to contain the objects for this + // device and all its partitions. + // + + do { + + WCHAR buffer[64]; + UNICODE_STRING unicodeDirectoryName; + + swprintf(buffer, L"\\Device\\Harddisk%d", *DeviceCount); + + RtlInitUnicodeString(&unicodeDirectoryName, buffer); + + InitializeObjectAttributes(&objectAttributes, + &unicodeDirectoryName, + OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, + NULL, + NULL); + + status = ZwCreateDirectoryObject(&handle, + DIRECTORY_ALL_ACCESS, + &objectAttributes); + + (*DeviceCount)++; + + } while((status == STATUS_OBJECT_NAME_COLLISION) || + (status == STATUS_OBJECT_NAME_EXISTS)); + + if (!NT_SUCCESS(status)) { + + DebugPrint((1, "DiskCreateFdo: Could not create directory - %lx\n", + status)); + + return(status); + } + + // + // When this loop exits the count is inflated by one - fix that. + // + + (*DeviceCount)--; + + // + // Claim the device. + // + + lowerDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject); + + status = ClassClaimDevice(lowerDevice, FALSE); + + if (!NT_SUCCESS(status)) { + ZwMakeTemporaryObject(handle); + ZwClose(handle); + ObDereferenceObject(lowerDevice); + return status; + } + + // + // Create a device object for this device. Each physical disk will + // have at least one device object. The required device object + // describes the entire device. Its directory path is + // \Device\HarddiskN\Partition0, where N = device number. + // + + status = DiskGenerateDeviceName(TRUE, + *DeviceCount, + 0, + NULL, + NULL, + &deviceName); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskCreateFdo - couldn't create name %lx\n", + status)); + + goto DiskCreateFdoExit; + + } + + status = ClassCreateDeviceObject(DriverObject, + deviceName, + PhysicalDeviceObject, + TRUE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + + DebugPrint((1, + "DiskCreateFdo: Can not create device object %s\n", + ntNameBuffer)); + + goto DiskCreateFdoExit; + } + + // + // Indicate that IRPs should include MDLs for data transfers. + // + + SET_FLAG(deviceObject->Flags, DO_DIRECT_IO); + + fdoExtension = deviceObject->DeviceExtension; + + if(DasdAccessOnly) { + + // + // Inidicate that only RAW should be allowed to mount on the root + // partition object. This ensures that a file system can't doubly + // mount on a super-floppy by mounting once on P0 and once on P1. + // + + SET_FLAG(deviceObject->Vpb->Flags, VPB_RAW_MOUNT); + } + + // + // Initialize lock count to zero. The lock count is used to + // disable the ejection mechanism on devices that support + // removable media. Only the lock count in the physical + // device extension is used. + // + + fdoExtension->LockCount = 0; + + // + // Save system disk number. + // + + fdoExtension->DeviceNumber = *DeviceCount; + + // + // Set the alignment requirements for the device based on the + // host adapter requirements + // + + if (lowerDevice->AlignmentRequirement > deviceObject->AlignmentRequirement) { + deviceObject->AlignmentRequirement = lowerDevice->AlignmentRequirement; + } + + // + // Finally, attach to the pdo + // + + fdoExtension->LowerPdo = PhysicalDeviceObject; + + fdoExtension->CommonExtension.LowerDeviceObject = + IoAttachDeviceToDeviceStack( + deviceObject, + PhysicalDeviceObject); + + if(fdoExtension->CommonExtension.LowerDeviceObject == NULL) { + + // + // Uh - oh, we couldn't attach + // cleanup and return + // + + status = STATUS_UNSUCCESSFUL; + goto DiskCreateFdoExit; + } + + { + PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData; + + // + // Initialize the partitioning lock as it may be used in the remove + // code. + // + + KeInitializeEvent(&(diskData->PartitioningEvent), + SynchronizationEvent, + TRUE); + } + + + // + // Clear the init flag. + // + + CLEAR_FLAG(deviceObject->Flags, DO_DEVICE_INITIALIZING); + + // + // Store a handle to the device object directory for this disk + // + + fdoExtension->DeviceDirectory = handle; + + ObDereferenceObject(lowerDevice); + + return STATUS_SUCCESS; + +DiskCreateFdoExit: + + // + // Release the device since an error occurred. + // + + if (deviceObject != NULL) { + IoDeleteDevice(deviceObject); + } + + // + // Delete directory and return. + // + + if (!NT_SUCCESS(status)) { + ZwMakeTemporaryObject(handle); + ZwClose(handle); + } + + ObDereferenceObject(lowerDevice); + + return(status); +} + + +NTSTATUS +DiskReadWriteVerification( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + I/O System entry for read and write requests to SCSI disks. + +Arguments: + + DeviceObject - Pointer to driver object created by system. + Irp - IRP involved. + +Return Value: + + NT Status + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG transferByteCount = currentIrpStack->Parameters.Read.Length; + LARGE_INTEGER startingOffset; + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = + commonExtension->PartitionZeroExtension; + + ULONG residualBytes; + NTSTATUS status; + + // + // Verify parameters of this request. + // Check that ending sector is within partition and + // that number of bytes to transfer is a multiple of + // the sector size. + // + + startingOffset.QuadPart = + (currentIrpStack->Parameters.Read.ByteOffset.QuadPart + + transferByteCount); + + residualBytes = transferByteCount & + (fdoExtension->DiskGeometry.BytesPerSector - 1); + + + if ((startingOffset.QuadPart > commonExtension->PartitionLength.QuadPart) || + (residualBytes != 0)) { + + // + // This error may be caused by the fact that the drive is not ready. + // + + status = ((PDISK_DATA) commonExtension->DriverData)->ReadyStatus; + + if (!NT_SUCCESS(status)) { + + // + // Flag this as a user errror so that a popup is generated. + // + + DebugPrint((1, "DiskReadWriteVerification: ReadyStatus is %lx\n", + status)); + + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); + + // + // status will keep the current error + // + + ASSERT( status != STATUS_INSUFFICIENT_RESOURCES ); + + } else if((commonExtension->IsFdo == TRUE) && (residualBytes == 0)) { + + // + // This failed because we think the physical disk is too small. + // Send it down to the drive and let the hardware decide for + // itself. + // + + status = STATUS_SUCCESS; + + } else { + + // + // Note fastfat depends on this parameter to determine when to + // remount due to a sector size change. + // + + status = STATUS_INVALID_PARAMETER; + + } + + } else { + + // + // the drive is ready, so ok the read/write + // + + status = STATUS_SUCCESS; + + } + + Irp->IoStatus.Status = status; + return status; + +} // end DiskReadWrite() + + + +NTSTATUS +DiskDetermineMediaTypes( + IN PDEVICE_OBJECT Fdo, + IN PIRP Irp, + IN UCHAR MediumType, + IN UCHAR DensityCode, + IN BOOLEAN MediaPresent, + IN BOOLEAN IsWritable + ) + +/*++ + +Routine Description: + + Determines number of types based on the physical device, validates the user buffer + and builds the MEDIA_TYPE information. + +Arguments: + + DeviceObject - Pointer to functional device object created by system. + Irp - IOCTL_STORAGE_GET_MEDIA_TYPES_EX Irp. + MediumType - byte returned in mode data header. + DensityCode - byte returned in mode data block descriptor. + NumberOfTypes - pointer to be updated based on actual device. + +Return Value: + + Status is returned. + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PPHYSICAL_DEVICE_EXTENSION pdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + + PGET_MEDIA_TYPES mediaTypes = Irp->AssociatedIrp.SystemBuffer; + PDEVICE_MEDIA_INFO mediaInfo = &mediaTypes->MediaInfo[0]; + BOOLEAN deviceMatched = FALSE; + + PAGED_CODE(); + + // + // this should be checked prior to calling into this routine + // as we use the buffer as mediaTypes + // + ASSERT(irpStack->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(GET_MEDIA_TYPES)); + + + // + // Determine if this device is removable or fixed. + // + + if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { + + // + // Fixed disk. + // + + mediaTypes->DeviceType = FILE_DEVICE_DISK; + mediaTypes->MediaInfoCount = 1; + + mediaInfo->DeviceSpecific.DiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart; + mediaInfo->DeviceSpecific.DiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder; + mediaInfo->DeviceSpecific.DiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack; + mediaInfo->DeviceSpecific.DiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector; + mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1; + + mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics = (MEDIA_CURRENTLY_MOUNTED | MEDIA_READ_WRITE); + + if (!IsWritable) { + SET_FLAG(mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics, + MEDIA_WRITE_PROTECTED); + } + + mediaInfo->DeviceSpecific.DiskInfo.MediaType = FixedMedia; + + + } else { + + PUCHAR vendorId = (PUCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->VendorIdOffset; + PUCHAR productId = (PUCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->ProductIdOffset; + PUCHAR productRevision = (PUCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->ProductRevisionOffset; + DISK_MEDIA_TYPES_LIST const *mediaListEntry; + ULONG currentMedia; + ULONG i; + ULONG j; + ULONG sizeNeeded; + + DebugPrint((1, + "DiskDetermineMediaTypes: Vendor %s, Product %s\n", + vendorId, + productId)); + + // + // Run through the list until we find the entry with a NULL Vendor Id. + // + + for (i = 0; DiskMediaTypes[i].VendorId != NULL; i++) { + + mediaListEntry = &DiskMediaTypes[i]; + + if (strncmp(mediaListEntry->VendorId,vendorId,strlen(mediaListEntry->VendorId))) { + continue; + } + + if ((mediaListEntry->ProductId != NULL) && + strncmp(mediaListEntry->ProductId, productId, strlen(mediaListEntry->ProductId))) { + continue; + } + + if ((mediaListEntry->Revision != NULL) && + strncmp(mediaListEntry->Revision, productRevision, strlen(mediaListEntry->Revision))) { + continue; + } + + deviceMatched = TRUE; + + mediaTypes->DeviceType = FILE_DEVICE_DISK; + mediaTypes->MediaInfoCount = mediaListEntry->NumberOfTypes; + + // + // Ensure that buffer is large enough. + // + + sizeNeeded = FIELD_OFFSET(GET_MEDIA_TYPES, MediaInfo[0]) + + (mediaListEntry->NumberOfTypes * + sizeof(DEVICE_MEDIA_INFO) + ); + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeNeeded) { + + // + // Buffer too small + // + + Irp->IoStatus.Information = sizeNeeded; + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + return STATUS_BUFFER_TOO_SMALL; + } + + for (j = 0; j < mediaListEntry->NumberOfTypes; j++) { + + mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart; + mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder; + mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack; + mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector; + mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = mediaListEntry->NumberOfSides; + + // + // Set the type. + // + + mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = mediaListEntry->MediaTypes[j]; + + if (mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType == MO_5_WO) { + mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_WRITE_ONCE; + } else { + mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE; + } + + // + // Status will either be success, if media is present, or no media. + // It would be optimal to base from density code and medium type, but not all devices + // have values for these fields. + // + + if (MediaPresent) { + + // + // The usage of MediumType and DensityCode is device specific, so this may need + // to be extended to further key off of product/vendor ids. + // Currently, the MO units are the only devices that return this information. + // + + if (MediumType == 2) { + currentMedia = MO_5_WO; + } else if (MediumType == 3) { + currentMedia = MO_5_RW; + + if (DensityCode == 0x87) { + + // + // Indicate that the pinnacle 4.6 G media + // is present. Other density codes will default to normal + // RW MO media. + // + + currentMedia = PINNACLE_APEX_5_RW; + } + } else { + currentMedia = 0; + } + + if (currentMedia) { + if (mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType == (STORAGE_MEDIA_TYPE)currentMedia) { + SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED); + } + + } else { + SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED); + } + } + + if (!IsWritable) { + SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_WRITE_PROTECTED); + } + + // + // Advance to next entry. + // + + mediaInfo++; + } + } + + if (!deviceMatched) { + + DebugPrint((1, + "DiskDetermineMediaTypes: Unknown device. Vendor: %s Product: %s Revision: %s\n", + vendorId, + productId, + productRevision)); + // + // Build an entry for unknown. + // + + mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart; + mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder; + mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack; + mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector; + + // + // Set the type. + // + + mediaTypes->DeviceType = FILE_DEVICE_DISK; + mediaTypes->MediaInfoCount = 1; + + mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = RemovableMedia; + mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1; + + mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE; + if (MediaPresent) { + SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED); + } + + if (!IsWritable) { + SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_WRITE_PROTECTED); + } + } + } + + Irp->IoStatus.Information = + FIELD_OFFSET(GET_MEDIA_TYPES, MediaInfo[0]) + + (mediaTypes->MediaInfoCount * sizeof(DEVICE_MEDIA_INFO)); + + return STATUS_SUCCESS; + +} + + +NTSTATUS +DiskDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + I/O system entry for device controls to SCSI disks. + +Arguments: + + Fdo - Pointer to functional device object created by system. + Irp - IRP involved. + +Return Value: + + Status is returned. + +--*/ + +#define SendToFdo(Dev, Irp, Rval) { \ + PCOMMON_DEVICE_EXTENSION ce = Dev->DeviceExtension; \ + ASSERT_PDO(Dev); \ + IoCopyCurrentIrpStackLocationToNext(Irp); \ + Rval = IoCallDriver(ce->LowerDeviceObject, Irp); \ + } + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + PMODE_PARAMETER_HEADER modeData; + PIRP irp2; + ULONG length; + NTSTATUS status; + KEVENT event; + IO_STATUS_BLOCK ioStatus; + + BOOLEAN b = FALSE; + + srb = ExAllocatePoolWithTag(NonPagedPool, + SCSI_REQUEST_BLOCK_SIZE, + DISK_TAG_SRB); + Irp->IoStatus.Information = 0; + + if (srb == NULL) { + + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Write zeros to Srb. + // + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + cdb = (PCDB)srb->Cdb; + + switch (irpStack->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_DISK_GET_CACHE_INFORMATION: + b = TRUE; + case IOCTL_DISK_SET_CACHE_INFORMATION: { + + BOOLEAN getCaching = b; + PDISK_CACHE_INFORMATION cacheInfo = Irp->AssociatedIrp.SystemBuffer; + + if(!commonExtension->IsFdo) { + + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + // + // Validate the request. + // + + if((getCaching) && + (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(DISK_CACHE_INFORMATION)) + ) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(DISK_CACHE_INFORMATION); + break; + } + + if ((!getCaching) && + (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(DISK_CACHE_INFORMATION)) + ) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + ASSERT(Irp->AssociatedIrp.SystemBuffer != NULL); + + if (getCaching) { + + status = DiskGetCacheInformation(fdoExtension, cacheInfo); + + if (NT_SUCCESS(status)) { + Irp->IoStatus.Information = sizeof(DISK_CACHE_INFORMATION); + } + + } else { + + if (!cacheInfo->WriteCacheEnabled) + { + if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_DISABLE_WRITE_CACHE_NOT_SUPPORTED)) + { + // + // This request wants to disable write cache, which is + // not supported on this device. Instead of sending it + // down only to see it fail, return the error code now + // + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + } + else + { + if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_DISABLE_WRITE_CACHE)) + { + // + // This request wants to enable write cache, which + // has been disabled to protect data integrity. So + // fail this request with access denied + // + status = STATUS_ACCESS_DENIED; + break; + } + } + + status = DiskSetCacheInformation(fdoExtension, cacheInfo); + + if (NT_SUCCESS(status)) + { + // + // Store the user-defined override in the registry + // + ClassSetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceUserWriteCacheSetting, + (cacheInfo->WriteCacheEnabled) ? DiskWriteCacheEnable : DiskWriteCacheDisable); + } + else if (status == STATUS_INVALID_DEVICE_REQUEST) + { + if (cacheInfo->WriteCacheEnabled == FALSE) + { + // + // This device does not allow for + // the write cache to be disabled + // + ULONG specialFlags = 0; + + ClassGetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceSpecialFlags, + &specialFlags); + + SET_FLAG(specialFlags, HackDisableWriteCacheNotSupported); + + SET_FLAG(fdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_DISABLE_WRITE_CACHE_NOT_SUPPORTED); + + ClassSetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceSpecialFlags, + specialFlags); + } + } + } + + break; + } + +#if(_WIN32_WINNT >= 0x0500) + case IOCTL_DISK_GET_WRITE_CACHE_STATE: { + + PDISK_WRITE_CACHE_STATE writeCacheState = (PDISK_WRITE_CACHE_STATE)Irp->AssociatedIrp.SystemBuffer; + + if(!commonExtension->IsFdo) { + + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + // + // Validate the request. + // + + if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_WRITE_CACHE_STATE)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(DISK_WRITE_CACHE_STATE); + break; + } + + *writeCacheState = DiskWriteCacheNormal; + + // + // Determine whether it is possible to disable the write cache + // + + if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE_NOT_SUPPORTED)) + { + *writeCacheState = DiskWriteCacheDisableNotSupported; + } + + // + // Determine whether it is safe to toggle the write cache + // + + if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE)) + { + *writeCacheState = DiskWriteCacheForceDisable; + } + + Irp->IoStatus.Information = sizeof(DISK_WRITE_CACHE_STATE); + status = STATUS_SUCCESS; + break; + } +#endif + + case SMART_GET_VERSION: { + + PUCHAR buffer; + PSRB_IO_CONTROL srbControl; + PGETVERSIONINPARAMS versionParams; + + if(!commonExtension->IsFdo) { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(GETVERSIONINPARAMS)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(GETVERSIONINPARAMS); + break; + } + + // + // Create notification event object to be used to signal the + // request completion. + // + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + srbControl = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SRB_IO_CONTROL) + + sizeof(GETVERSIONINPARAMS), + DISK_TAG_SMART); + + if (!srbControl) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlZeroMemory(srbControl, + sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS) + ); + + // + // fill in srbControl fields + // + + srbControl->HeaderLength = sizeof(SRB_IO_CONTROL); + RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8); + srbControl->Timeout = fdoExtension->TimeOutValue; + srbControl->Length = sizeof(GETVERSIONINPARAMS); + srbControl->ControlCode = IOCTL_SCSI_MINIPORT_SMART_VERSION; + + // + // Point to the 'buffer' portion of the SRB_CONTROL + // + + buffer = (PUCHAR)srbControl; + buffer += srbControl->HeaderLength; + + // + // Ensure correct target is set in the cmd parameters. + // + + versionParams = (PGETVERSIONINPARAMS)buffer; + versionParams->bIDEDeviceMap = diskData->ScsiAddress.TargetId; + + // + // Copy the IOCTL parameters to the srb control buffer area. + // + + RtlMoveMemory(buffer, + Irp->AssociatedIrp.SystemBuffer, + sizeof(GETVERSIONINPARAMS)); + + ClassSendDeviceIoControlSynchronous( + IOCTL_SCSI_MINIPORT, + commonExtension->LowerDeviceObject, + srbControl, + sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS), + sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS), + FALSE, + &ioStatus); + + status = ioStatus.Status; + + // + // If successful, copy the data received into the output buffer. + // This should only fail in the event that the IDE driver is older + // than this driver. + // + + if (NT_SUCCESS(status)) { + + buffer = (PUCHAR)srbControl; + buffer += srbControl->HeaderLength; + + RtlMoveMemory (Irp->AssociatedIrp.SystemBuffer, buffer, + sizeof(GETVERSIONINPARAMS)); + Irp->IoStatus.Information = sizeof(GETVERSIONINPARAMS); + } + + ExFreePool(srbControl); + break; + } + + case SMART_RCV_DRIVE_DATA: { + + PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer); + ULONG controlCode = 0; + PSRB_IO_CONTROL srbControl; + PUCHAR buffer; + + if(!commonExtension->IsFdo) { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + (sizeof(SENDCMDINPARAMS) - 1)) { + status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + break; + + } else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + (sizeof(SENDCMDOUTPARAMS) + 512 - 1)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) + 512 - 1; + break; + } + + // + // Create notification event object to be used to signal the + // request completion. + // + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + // + // use controlCode as a sort of 'STATUS_SUCCESS' to see if it's + // a valid request type + // + + if (cmdInParameters->irDriveRegs.bCommandReg == ID_CMD) { + + length = IDENTIFY_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS); + controlCode = IOCTL_SCSI_MINIPORT_IDENTIFY; + + } else if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) { + switch (cmdInParameters->irDriveRegs.bFeaturesReg) { + case READ_ATTRIBUTES: + controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; + length = READ_ATTRIBUTE_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS); + break; + case READ_THRESHOLDS: + controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; + length = READ_THRESHOLD_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS); + break; + default: + status = STATUS_INVALID_PARAMETER; + break; + } + } else { + + status = STATUS_INVALID_PARAMETER; + } + + if (controlCode == 0) { + status = STATUS_INVALID_PARAMETER; + break; + } + + srbControl = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SRB_IO_CONTROL) + length, + DISK_TAG_SMART); + + if (!srbControl) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // fill in srbControl fields + // + + srbControl->HeaderLength = sizeof(SRB_IO_CONTROL); + RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8); + srbControl->Timeout = fdoExtension->TimeOutValue; + srbControl->Length = length; + srbControl->ControlCode = controlCode; + + // + // Point to the 'buffer' portion of the SRB_CONTROL + // + + buffer = (PUCHAR)srbControl; + buffer += srbControl->HeaderLength; + + // + // Ensure correct target is set in the cmd parameters. + // + + cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId; + + // + // Copy the IOCTL parameters to the srb control buffer area. + // + + RtlMoveMemory(buffer, + Irp->AssociatedIrp.SystemBuffer, + sizeof(SENDCMDINPARAMS) - 1); + + irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT, + commonExtension->LowerDeviceObject, + srbControl, + sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1, + srbControl, + sizeof(SRB_IO_CONTROL) + length, + FALSE, + &event, + &ioStatus); + + if (irp2 == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(srbControl); + break; + } + + // + // Call the port driver with the request and wait for it to complete. + // + + status = IoCallDriver(commonExtension->LowerDeviceObject, irp2); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + // + // Copy the data received into the output buffer. Since the status buffer + // contains error information also, always perform this copy. IO will will + // either pass this back to the app, or zero it, in case of error. + // + + buffer = (PUCHAR)srbControl; + buffer += srbControl->HeaderLength; + + if (NT_SUCCESS(status)) { + + RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, length - 1); + Irp->IoStatus.Information = length - 1; + + } else { + + RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, (sizeof(SENDCMDOUTPARAMS) - 1)); + Irp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) - 1; + + } + + ExFreePool(srbControl); + break; + + } + + case SMART_SEND_DRIVE_COMMAND: { + + PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer); + PSRB_IO_CONTROL srbControl; + ULONG controlCode = 0; + PUCHAR buffer; + + if(!commonExtension->IsFdo) { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + (sizeof(SENDCMDINPARAMS) - 1)) { + status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + break; + + } else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + (sizeof(SENDCMDOUTPARAMS) - 1)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) - 1; + break; + } + + // + // Create notification event object to be used to signal the + // request completion. + // + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + length = 0; + + if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) { + switch (cmdInParameters->irDriveRegs.bFeaturesReg) { + + case ENABLE_SMART: + controlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART; + break; + + case DISABLE_SMART: + controlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART; + break; + + case RETURN_SMART_STATUS: + + // + // Ensure bBuffer is at least 2 bytes (to hold the values of + // cylinderLow and cylinderHigh). + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + (sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS))) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = + sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS); + break; + } + + controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS; + length = sizeof(IDEREGS); + break; + + case ENABLE_DISABLE_AUTOSAVE: + controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; + break; + + case SAVE_ATTRIBUTE_VALUES: + controlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; + break; + + case EXECUTE_OFFLINE_DIAGS: + // + // Validate that this is an ok self test command + // + if (DiskIsValidSmartSelfTest(cmdInParameters->irDriveRegs.bSectorNumberReg)) + { + controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; + } + break; + + case ENABLE_DISABLE_AUTO_OFFLINE: + controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; + break; + + default: + status = STATUS_INVALID_PARAMETER; + break; + } + } else { + + status = STATUS_INVALID_PARAMETER; + } + + if (controlCode == 0) { + status = STATUS_INVALID_PARAMETER; + break; + } + + length += (sizeof(SENDCMDOUTPARAMS) > sizeof(SENDCMDINPARAMS)) ? sizeof(SENDCMDOUTPARAMS) : sizeof(SENDCMDINPARAMS);; + srbControl = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SRB_IO_CONTROL) + length, + DISK_TAG_SMART); + + if (!srbControl) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // fill in srbControl fields + // + + srbControl->HeaderLength = sizeof(SRB_IO_CONTROL); + RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8); + srbControl->Timeout = fdoExtension->TimeOutValue; + srbControl->Length = length; + + // + // Point to the 'buffer' portion of the SRB_CONTROL + // + + buffer = (PUCHAR)srbControl; + buffer += srbControl->HeaderLength; + + // + // Ensure correct target is set in the cmd parameters. + // + + cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId; + + // + // Copy the IOCTL parameters to the srb control buffer area. + // + + RtlMoveMemory(buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1); + + srbControl->ControlCode = controlCode; + + irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT, + commonExtension->LowerDeviceObject, + srbControl, + sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1, + srbControl, + sizeof(SRB_IO_CONTROL) + length, + FALSE, + &event, + &ioStatus); + + if (irp2 == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + ExFreePool(srbControl); + break; + } + + // + // Call the port driver with the request and wait for it to complete. + // + + status = IoCallDriver(commonExtension->LowerDeviceObject, irp2); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + // + // Copy the data received into the output buffer. Since the status buffer + // contains error information also, always perform this copy. IO will will + // either pass this back to the app, or zero it, in case of error. + // + + buffer = (PUCHAR)srbControl; + buffer += srbControl->HeaderLength; + + // + // Update the return buffer size based on the sub-command. + // + + if (cmdInParameters->irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS) { + length = sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS); + } else { + length = sizeof(SENDCMDOUTPARAMS) - 1; + } + + RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, length); + Irp->IoStatus.Information = length; + + ExFreePool(srbControl); + break; + + } + + case IOCTL_STORAGE_GET_MEDIA_TYPES_EX: { + + PMODE_PARAMETER_BLOCK blockDescriptor; + ULONG modeLength; + ULONG retries = 4; + BOOLEAN writable = FALSE; + BOOLEAN mediaPresent = FALSE; + + DebugPrint((3, + "Disk.DiskDeviceControl: GetMediaTypes\n")); + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(GET_MEDIA_TYPES)) { + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(GET_MEDIA_TYPES); + break; + } + + if(!commonExtension->IsFdo) { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + // + // Send a TUR to determine if media is present. + // + + srb->CdbLength = 6; + cdb = (PCDB)srb->Cdb; + cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; + + // + // Set timeout value. + // + + srb->TimeOutValue = fdoExtension->TimeOutValue; + + status = ClassSendSrbSynchronous(DeviceObject, + srb, + NULL, + 0, + FALSE); + + + if (NT_SUCCESS(status)) { + mediaPresent = TRUE; + } + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + // + // Allocate memory for mode header and block descriptor. + // + + modeLength = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK); + modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + modeLength, + DISK_TAG_MODE_DATA); + + if (modeData == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlZeroMemory(modeData, modeLength); + + // + // Build the MODE SENSE CDB. + // + + srb->CdbLength = 6; + cdb = (PCDB)srb->Cdb; + + // + // Set timeout value from device extension. + // + + srb->TimeOutValue = fdoExtension->TimeOutValue; + + // + // Page code of 0 will return header and block descriptor only. + // + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = 0; + cdb->MODE_SENSE.AllocationLength = (UCHAR)modeLength; + +Retry: + status = ClassSendSrbSynchronous(DeviceObject, + srb, + modeData, + modeLength, + FALSE); + + + if (status == STATUS_VERIFY_REQUIRED) { + + if (retries--) { + + // + // Retry request. + // + + goto Retry; + } + } else if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) { + status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(status) || (status == STATUS_NO_MEDIA_IN_DEVICE)) { + + // + // Get the block descriptor. + // + + blockDescriptor = (PMODE_PARAMETER_BLOCK)modeData; + blockDescriptor = (PMODE_PARAMETER_BLOCK)((ULONG_PTR)blockDescriptor + sizeof(MODE_PARAMETER_HEADER)); + + // + // Do some validation. + // + + if (modeData->BlockDescriptorLength != sizeof(MODE_PARAMETER_BLOCK)) { + + DebugPrint((1, + "DiskDeviceControl: BlockDescriptor length - " + "Expected %x, actual %x\n", + modeData->BlockDescriptorLength, + sizeof(MODE_PARAMETER_BLOCK))); + } + + DebugPrint((1, + "DiskDeviceControl: DensityCode %x, MediumType %x\n", + blockDescriptor->DensityCode, + modeData->MediumType)); + + if (TEST_FLAG(modeData->DeviceSpecificParameter, + MODE_DSP_WRITE_PROTECT)) { + writable = FALSE; + } else { + writable = TRUE; + } + + status = DiskDetermineMediaTypes(DeviceObject, + Irp, + modeData->MediumType, + blockDescriptor->DensityCode, + mediaPresent, + writable); + + // + // If the buffer was too small, DetermineMediaTypes updated the status and information and the request will fail. + // + + } else { + DebugPrint((1, + "DiskDeviceControl: Mode sense for header/bd failed. %lx\n", + status)); + } + + ExFreePool(modeData); + break; + } + + case IOCTL_DISK_GET_DRIVE_GEOMETRY: { + + DebugPrint((2, "IOCTL_DISK_GET_DRIVE_GEOMETRY to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((2, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(DISK_GEOMETRY)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); + break; + } + + if(!commonExtension->IsFdo) { + + // + // Pdo should issue this request to the lower device object + // + + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + // DiskAcquirePartitioningLock(fdoExtension); + + if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) { + + // + // Issue ReadCapacity to update device extension + // with information for current media. + // + + status = DiskReadDriveCapacity( + commonExtension->PartitionZeroExtension->DeviceObject); + + // + // Note whether the drive is ready. + // + + diskData->ReadyStatus = status; + + if (!NT_SUCCESS(status)) { + // DiskReleasePartitioningLock(fdoExtension); + break; + } + } + + // + // Copy drive geometry information from device extension. + // + + RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, + &(fdoExtension->DiskGeometry), + sizeof(DISK_GEOMETRY)); + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); + // DiskReleasePartitioningLock(fdoExtension); + break; + } + + case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: { + DebugPrint((1, "IOCTL_DISK_GET_DRIVE_GEOMETRY_EX to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((1, "Device Is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + + if (!commonExtension->IsFdo) { + + // + // Pdo should issue this request to the lower device object + // + + ClassReleaseRemoveLock (DeviceObject, Irp); + ExFreePool (srb); + SendToFdo (DeviceObject, Irp, status); + return status; + + } else { + + status = DiskIoctlGetDriveGeometryEx( DeviceObject, Irp ); + } + + break; + } + + case IOCTL_STORAGE_PREDICT_FAILURE : { + + PSTORAGE_PREDICT_FAILURE checkFailure; + STORAGE_FAILURE_PREDICT_STATUS diskSmartStatus; + + DebugPrint((2, "IOCTL_STORAGE_PREDICT_FAILURE to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((2, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + checkFailure = (PSTORAGE_PREDICT_FAILURE)Irp->AssociatedIrp.SystemBuffer; + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(STORAGE_PREDICT_FAILURE)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(STORAGE_PREDICT_FAILURE); + break; + } + + if(!commonExtension->IsFdo) { + + // + // Pdo should issue this request to the lower device object + // + + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + // + // See if the disk is predicting failure + // + + if (diskData->FailurePredictionCapability == FailurePredictionSense) { + ULONG readBufferSize; + PUCHAR readBuffer; + PIRP readIrp; + IO_STATUS_BLOCK ioStatus; + PDEVICE_OBJECT topOfStack; + + checkFailure->PredictFailure = 0; + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + topOfStack = IoGetAttachedDeviceReference(DeviceObject); + + // + // SCSI disks need to have a read sent down to provoke any + // failures to be reported. + // + // Issue a normal read operation. The error-handling code in + // classpnp will take care of a failure prediction by logging the + // correct event. + // + + readBufferSize = fdoExtension->DiskGeometry.BytesPerSector; + readBuffer = ExAllocatePoolWithTag(NonPagedPool, + readBufferSize, + DISK_TAG_SMART); + + if (readBuffer != NULL) { + LARGE_INTEGER offset; + + offset.QuadPart = 0; + readIrp = IoBuildSynchronousFsdRequest( + IRP_MJ_READ, + topOfStack, + readBuffer, + readBufferSize, + &offset, + &event, + &ioStatus); + + + if (readIrp != NULL) { + status = IoCallDriver(topOfStack, readIrp); + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + } + + ExFreePool(readBuffer); + } + ObDereferenceObject(topOfStack); + } + + if ((diskData->FailurePredictionCapability == FailurePredictionSmart) || + (diskData->FailurePredictionCapability == FailurePredictionSense)) + { + status = DiskReadFailurePredictStatus(fdoExtension, + &diskSmartStatus); + + if (NT_SUCCESS(status)) + { + status = DiskReadFailurePredictData(fdoExtension, + Irp->AssociatedIrp.SystemBuffer); + + if (diskSmartStatus.PredictFailure) + { + checkFailure->PredictFailure = 1; + } else { + checkFailure->PredictFailure = 0; + } + + Irp->IoStatus.Information = sizeof(STORAGE_PREDICT_FAILURE); + } + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + + break; + } + + case IOCTL_DISK_VERIFY: { + + PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer; + LARGE_INTEGER byteOffset; + + DebugPrint((2, "IOCTL_DISK_VERIFY to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((2, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + // + // Validate buffer length. + // + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(VERIFY_INFORMATION)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + // + // Add disk offset to starting sector. + // + + byteOffset.QuadPart = commonExtension->StartingOffset.QuadPart + + verifyInfo->StartingOffset.QuadPart; + + if(!commonExtension->IsFdo) { + + // + // Adjust the request and forward it down + // + + verifyInfo->StartingOffset.QuadPart = byteOffset.QuadPart; + + ClassReleaseRemoveLock(DeviceObject, Irp); + SendToFdo(DeviceObject, Irp, status); + ExFreePool(srb); + return status; + } + + // + // Perform a bounds check on the sector range + // + + if ((verifyInfo->StartingOffset.QuadPart > commonExtension->PartitionLength.QuadPart) || + (verifyInfo->StartingOffset.QuadPart < 0)) + { + status = STATUS_NONEXISTENT_SECTOR; + break; + } + else + { + ULONGLONG bytesRemaining = commonExtension->PartitionLength.QuadPart - verifyInfo->StartingOffset.QuadPart; + + if ((ULONGLONG)verifyInfo->Length > bytesRemaining) + { + status = STATUS_NONEXISTENT_SECTOR; + break; + } + } + + { + PDISK_VERIFY_WORKITEM_CONTEXT Context = NULL; + + Context = ExAllocatePoolWithTag(NonPagedPool, + sizeof(DISK_VERIFY_WORKITEM_CONTEXT), + DISK_TAG_WI_CONTEXT); + + if (Context) + { + Context->Irp = Irp; + Context->Srb = srb; + Context->WorkItem = IoAllocateWorkItem(DeviceObject); + + if (Context->WorkItem) + { + IoMarkIrpPending(Irp); + + IoQueueWorkItem(Context->WorkItem, + (PIO_WORKITEM_ROUTINE)DiskIoctlVerify, + DelayedWorkQueue, + Context); + + return STATUS_PENDING; + } + + ExFreePool(Context); + } + + status = STATUS_INSUFFICIENT_RESOURCES; + } + + break; + } + + case IOCTL_DISK_CREATE_DISK: { + + if (!commonExtension->IsFdo) { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + status = DiskIoctlCreateDisk ( + DeviceObject, + Irp + ); + break; + } + + case IOCTL_DISK_GET_DRIVE_LAYOUT: { + + DebugPrint((1, "IOCTL_DISK_GET_DRIVE_LAYOUT to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((1, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + if (!commonExtension->IsFdo) { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + status = DiskIoctlGetDriveLayout( + DeviceObject, + Irp); + break; + } + + case IOCTL_DISK_GET_DRIVE_LAYOUT_EX: { + + DebugPrint((1, "IOCTL_DISK_GET_DRIVE_LAYOUT_EX to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((1, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + if (!commonExtension->IsFdo) { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + status = DiskIoctlGetDriveLayoutEx( + DeviceObject, + Irp); + break; + + } + + case IOCTL_DISK_SET_DRIVE_LAYOUT: { + + DebugPrint((1, "IOCTL_DISK_SET_DRIVE_LAYOUT to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((1, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + if(!commonExtension->IsFdo) { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + status = DiskIoctlSetDriveLayout(DeviceObject, Irp); + + // + // Notify everyone that the disk layout has changed + // + { + TARGET_DEVICE_CUSTOM_NOTIFICATION Notification; + + Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE; + Notification.Version = 1; + Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer); + Notification.FileObject = NULL; + Notification.NameBufferOffset = -1; + + IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo, + &Notification, + NULL, + NULL); + } + + break; + } + + case IOCTL_DISK_SET_DRIVE_LAYOUT_EX: { + + DebugPrint((1, "IOCTL_DISK_SET_DRIVE_LAYOUT_EX to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((1, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + if (!commonExtension->IsFdo) { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + + return status; + } + + status = DiskIoctlSetDriveLayoutEx( + DeviceObject, + Irp); + + // + // Notify everyone that the disk layout has changed + // + { + TARGET_DEVICE_CUSTOM_NOTIFICATION Notification; + + Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE; + Notification.Version = 1; + Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer); + Notification.FileObject = NULL; + Notification.NameBufferOffset = -1; + + IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo, + &Notification, + NULL, + NULL); + } + + break; + } + + case IOCTL_DISK_GET_PARTITION_INFO: { + + DebugPrint((1, "IOCTL_DISK_GET_PARTITION_INFO to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((1, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + status = DiskIoctlGetPartitionInfo( + DeviceObject, + Irp); + break; + } + + case IOCTL_DISK_GET_PARTITION_INFO_EX: { + + DebugPrint((1, "IOCTL_DISK_GET_PARTITION_INFO to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((1, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + status = DiskIoctlGetPartitionInfoEx( + DeviceObject, + Irp); + break; + } + + case IOCTL_DISK_GET_LENGTH_INFO: { + DebugPrint((1, "IOCTL_DISK_GET_LENGTH_INFO to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((1, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + status = DiskIoctlGetLengthInfo( + DeviceObject, + Irp); + break; + } + + case IOCTL_DISK_SET_PARTITION_INFO: { + + DebugPrint((1, "IOCTL_DISK_SET_PARTITION_INFO to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((1, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + + status = DiskIoctlSetPartitionInfo ( + DeviceObject, + Irp); + break; + } + + + case IOCTL_DISK_SET_PARTITION_INFO_EX: { + + DebugPrint((1, "IOCTL_DISK_SET_PARTITION_INFO_EX to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((1, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + status = DiskIoctlSetPartitionInfoEx( + DeviceObject, + Irp); + break; + } + + case IOCTL_DISK_DELETE_DRIVE_LAYOUT: { + + CREATE_DISK CreateDiskInfo; + + // + // Update the disk with new partition information. + // + + DebugPrint((1, "IOCTL_DISK_DELETE_DRIVE_LAYOUT to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((1, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + if(!commonExtension->IsFdo) { + + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + DiskAcquirePartitioningLock(fdoExtension); + + DiskInvalidatePartitionTable(fdoExtension, TRUE); + + // + // IoCreateDisk called with a partition style of raw + // will remove any partition tables from the disk. + // + + RtlZeroMemory (&CreateDiskInfo, sizeof (CreateDiskInfo)); + CreateDiskInfo.PartitionStyle = PARTITION_STYLE_RAW; + + status = IoCreateDisk( + DeviceObject, + &CreateDiskInfo); + + + DiskReleasePartitioningLock(fdoExtension); + ClassInvalidateBusRelations(DeviceObject); + + Irp->IoStatus.Status = status; + + break; + } + + case IOCTL_DISK_REASSIGN_BLOCKS: { + + // + // Map defective blocks to new location on disk. + // + + PREASSIGN_BLOCKS badBlocks = Irp->AssociatedIrp.SystemBuffer; + ULONG bufferSize; + ULONG blockNumber; + ULONG blockCount; + + DebugPrint((2, "IOCTL_DISK_REASSIGN_BLOCKS to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((2, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + // + // Validate buffer length. + // + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(REASSIGN_BLOCKS)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + // + // Send to FDO + // + + if(!commonExtension->IsFdo) { + + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + bufferSize = sizeof(REASSIGN_BLOCKS) + + ((badBlocks->Count - 1) * sizeof(ULONG)); + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + bufferSize) { + + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + // + // Build the data buffer to be transferred in the input buffer. + // The format of the data to the device is: + // + // 2 bytes Reserved + // 2 bytes Length + // x * 4 btyes Block Address + // + // All values are big endian. + // + + badBlocks->Reserved = 0; + blockCount = badBlocks->Count; + + // + // Convert # of entries to # of bytes. + // + + blockCount *= 4; + badBlocks->Count = (USHORT) ((blockCount >> 8) & 0XFF); + badBlocks->Count |= (USHORT) ((blockCount << 8) & 0XFF00); + + // + // Convert back to number of entries. + // + + blockCount /= 4; + + for (; blockCount > 0; blockCount--) { + + blockNumber = badBlocks->BlockNumber[blockCount-1]; + + REVERSE_BYTES((PFOUR_BYTE) &badBlocks->BlockNumber[blockCount-1], + (PFOUR_BYTE) &blockNumber); + } + + srb->CdbLength = 6; + + cdb->CDB6GENERIC.OperationCode = SCSIOP_REASSIGN_BLOCKS; + + // + // Set timeout value. + // + + srb->TimeOutValue = fdoExtension->TimeOutValue; + + status = ClassSendSrbSynchronous(DeviceObject, + srb, + badBlocks, + bufferSize, + TRUE); + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + ExFreePool(srb); + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + + return(status); + } + + case IOCTL_DISK_IS_WRITABLE: { + + // + // This routine mimics IOCTL_STORAGE_GET_MEDIA_TYPES_EX + // + + ULONG modeLength; + ULONG retries = 4; + + DebugPrint((3, "Disk.DiskDeviceControl: IOCTL_DISK_IS_WRITABLE\n")); + + if (!commonExtension->IsFdo) + { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + // + // Allocate memory for a mode header and then some + // for port drivers that need to convert to MODE10 + // or always return the MODE_PARAMETER_BLOCK (even + // when memory was not allocated for this purpose) + // + + modeLength = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK); + modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + modeLength, + DISK_TAG_MODE_DATA); + + if (modeData == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlZeroMemory(modeData, modeLength); + + // + // Build the MODE SENSE CDB + // + + srb->CdbLength = 6; + cdb = (PCDB)srb->Cdb; + + // + // Set the timeout value from the device extension + // + + srb->TimeOutValue = fdoExtension->TimeOutValue; + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = MODE_SENSE_RETURN_ALL; + cdb->MODE_SENSE.AllocationLength = (UCHAR)modeLength; + + while (retries != 0) + { + status = ClassSendSrbSynchronous(DeviceObject, + srb, + modeData, + modeLength, + FALSE); + + if (status != STATUS_VERIFY_REQUIRED) + { + if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) + { + status = STATUS_SUCCESS; + } + + break; + } + + retries--; + } + + if (NT_SUCCESS(status)) + { + if (TEST_FLAG(modeData->DeviceSpecificParameter, MODE_DSP_WRITE_PROTECT)) + { + status = STATUS_MEDIA_WRITE_PROTECTED; + } + } + + ExFreePool(modeData); + break; + } + + case IOCTL_DISK_INTERNAL_SET_VERIFY: { + + // + // If the caller is kernel mode, set the verify bit. + // + + if (Irp->RequestorMode == KernelMode) { + + SET_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME); + + if(commonExtension->IsFdo) { + + Irp->IoStatus.Information = 0; + } + } + + DiskInvalidatePartitionTable(fdoExtension, FALSE); + + status = STATUS_SUCCESS; + break; + } + + case IOCTL_DISK_INTERNAL_CLEAR_VERIFY: { + + // + // If the caller is kernel mode, clear the verify bit. + // + + if (Irp->RequestorMode == KernelMode) { + CLEAR_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME); + } + status = STATUS_SUCCESS; + break; + } + + case IOCTL_DISK_UPDATE_DRIVE_SIZE: { + + DebugPrint((2, "IOCTL_DISK_UPDATE_DRIVE_SIZE to device %p " + "through irp %p\n", + DeviceObject, Irp)); + + DebugPrint((2, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(DISK_GEOMETRY)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); + break; + } + + if(!commonExtension->IsFdo) { + + // + // Pdo should issue this request to the lower device object. + // + + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + DiskAcquirePartitioningLock(fdoExtension); + + // + // Invalidate the cached partition table. + // + + DiskInvalidatePartitionTable(fdoExtension, TRUE); + + // + // At this point, commonExtension *is* the FDO extension. This + // should be the same as PartitionZeroExtension. + // + + ASSERT(commonExtension == + &(commonExtension->PartitionZeroExtension->CommonExtension)); + + // + // Issue ReadCapacity to update device extension with information + // for current media. + // + + status = DiskReadDriveCapacity(DeviceObject); + + // + // Note whether the drive is ready. + // + + diskData->ReadyStatus = status; + + // + // The disk's partition tables may be invalid after the drive geometry + // has been updated. The call to IoValidatePartitionTable (below) will + // fix it if this is the case. + // + + if (NT_SUCCESS(status)) { + + status = DiskVerifyPartitionTable (fdoExtension, TRUE); + } + + + if (NT_SUCCESS(status)) { + + // + // Copy drive geometry information from the device extension. + // + + RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, + &(fdoExtension->DiskGeometry), + sizeof(DISK_GEOMETRY)); + + Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); + status = STATUS_SUCCESS; + + } + + DiskReleasePartitioningLock(fdoExtension); + + break; + } + + case IOCTL_DISK_GROW_PARTITION: { + + PDISK_GROW_PARTITION inputBuffer; + + // PDEVICE_OBJECT pdo; + PCOMMON_DEVICE_EXTENSION pdoExtension; + + LARGE_INTEGER bytesPerCylinder; + LARGE_INTEGER newStoppingOffset; + LARGE_INTEGER newPartitionLength; + + PPHYSICAL_DEVICE_EXTENSION sibling; + + PDRIVE_LAYOUT_INFORMATION_EX layoutInfo; + PPARTITION_INFORMATION_EX pdoPartition; + PPARTITION_INFORMATION_EX containerPartition; + ULONG partitionIndex; + + DebugPrint((2, "IOCTL_DISK_GROW_PARTITION to device %p through " + "irp %p\n", + DeviceObject, Irp)); + + DebugPrint((2, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + Irp->IoStatus.Information = 0; + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(DISK_GROW_PARTITION)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + Irp->IoStatus.Information = sizeof(DISK_GROW_PARTITION); + break; + } + + if(!commonExtension->IsFdo) { + + // + // Pdo should issue this request to the lower device object + // + + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject, Irp, status); + return status; + } + + DiskAcquirePartitioningLock(fdoExtension); + ClassAcquireChildLock(fdoExtension); + + // + // At this point, commonExtension *is* the FDO extension. This should + // be the same as PartitionZeroExtension. + // + + ASSERT(commonExtension == + &(commonExtension->PartitionZeroExtension->CommonExtension)); + + // + // Get the input parameters + // + + inputBuffer = (PDISK_GROW_PARTITION) Irp->AssociatedIrp.SystemBuffer; + + ASSERT(inputBuffer); + + // + // Make sure that we are actually being asked to grow the partition. + // + + if(inputBuffer->BytesToGrow.QuadPart == 0) { + + status = STATUS_INVALID_PARAMETER; + ClassReleaseChildLock(fdoExtension); + DiskReleasePartitioningLock(fdoExtension); + break; + } + + // + // Find the partition that matches the supplied number + // + + pdoExtension = &commonExtension->ChildList->CommonExtension; + + while(pdoExtension != NULL) { + + // + // Is this the partition we are searching for? + // + + if(inputBuffer->PartitionNumber == pdoExtension->PartitionNumber) { + break; + } + + pdoExtension = &pdoExtension->ChildList->CommonExtension; + } + + // Did we find the partition? + + if(pdoExtension == NULL) { + status = STATUS_INVALID_PARAMETER; + ClassReleaseChildLock(fdoExtension); + DiskReleasePartitioningLock(fdoExtension); + break; + } + + ASSERT(pdoExtension); + + // + // Compute the new values for the partition to grow. + // + + newPartitionLength.QuadPart = + (pdoExtension->PartitionLength.QuadPart + + inputBuffer->BytesToGrow.QuadPart); + + newStoppingOffset.QuadPart = + (pdoExtension->StartingOffset.QuadPart + + newPartitionLength.QuadPart - 1); + + // + // Test the partition alignment before getting to involved. + // + // NOTE: + // All partition stopping offsets should be one byte less + // than a cylinder boundary offset. Also, all first partitions + // (within partition0 and within an extended partition) start + // on the second track while all other partitions start on a + // cylinder boundary. + // + bytesPerCylinder.QuadPart = + ((LONGLONG) fdoExtension->DiskGeometry.TracksPerCylinder * + (LONGLONG) fdoExtension->DiskGeometry.SectorsPerTrack * + (LONGLONG) fdoExtension->DiskGeometry.BytesPerSector); + + // Temporarily adjust up to cylinder boundary. + + newStoppingOffset.QuadPart += 1; + + if(newStoppingOffset.QuadPart % bytesPerCylinder.QuadPart) { + + // Adjust the length first... + newPartitionLength.QuadPart -= + (newStoppingOffset.QuadPart % bytesPerCylinder.QuadPart); + + // ...and then the stopping offset. + newStoppingOffset.QuadPart -= + (newStoppingOffset.QuadPart % bytesPerCylinder.QuadPart); + + DebugPrint((2, "IOCTL_DISK_GROW_PARTITION: " + "Adjusted the requested partition size to cylinder boundary")); + } + + // Restore to one byte less than a cylinder boundary. + newStoppingOffset.QuadPart -= 1; + + // + // Will the new partition fit within Partition0? + // Remember: commonExtension == &PartitionZeroExtension->CommonExtension + // + + if(newStoppingOffset.QuadPart > + (commonExtension->StartingOffset.QuadPart + + commonExtension->PartitionLength.QuadPart - 1)) { + + // + // The new partition falls outside Partition0 + // + + status = STATUS_UNSUCCESSFUL; + ClassReleaseChildLock(fdoExtension); + DiskReleasePartitioningLock(fdoExtension); + break; + } + + // + // Search for any partition that will conflict with the new partition. + // This is done before testing for any containing partitions to + // simplify the container handling. + // + + sibling = commonExtension->ChildList; + + while(sibling != NULL) { + LARGE_INTEGER sibStoppingOffset; + PCOMMON_DEVICE_EXTENSION siblingExtension; + + siblingExtension = &(sibling->CommonExtension); + + ASSERT( siblingExtension ); + + sibStoppingOffset.QuadPart = + (siblingExtension->StartingOffset.QuadPart + + siblingExtension->PartitionLength.QuadPart - 1); + + // + // Only check the siblings that start beyond the new partition + // starting offset. Also, assume that since the starting offset + // has not changed, it will not be in conflict with any other + // partitions; only the new stopping offset needs to be tested. + // + + if((inputBuffer->PartitionNumber != + siblingExtension->PartitionNumber) && + + (siblingExtension->StartingOffset.QuadPart > + pdoExtension->StartingOffset.QuadPart) && + + (newStoppingOffset.QuadPart >= + siblingExtension->StartingOffset.QuadPart)) { + + // + // We have a conflict; bail out leaving pdoSibling set. + // + + break; + } + sibling = siblingExtension->ChildList; + } + + + // + // If there is a sibling that conflicts, it will be in pdoSibling; there + // could be more than one, but this is the first one detected. + // + + if(sibling != NULL) { + // + // Report the conflict and abort the grow request. + // + + status = STATUS_UNSUCCESSFUL; + ClassReleaseChildLock(fdoExtension); + DiskReleasePartitioningLock(fdoExtension); + break; + } + + // + // Read the partition table. Since we're planning on modifying it + // we should bypass the cache. + // + + status = DiskReadPartitionTableEx(fdoExtension, TRUE, &layoutInfo ); + + if( !NT_SUCCESS(status) ) { + ClassReleaseChildLock(fdoExtension); + DiskReleasePartitioningLock(fdoExtension); + break; + } + + ASSERT( layoutInfo ); + + // + // Search the layout for the partition that matches the + // PDO in hand. + // + + pdoPartition = + DiskPdoFindPartitionEntry( + (PPHYSICAL_DEVICE_EXTENSION) pdoExtension, + layoutInfo); + + if(pdoPartition == NULL) { + // Looks like something is wrong interally-- error ok? + status = STATUS_DRIVER_INTERNAL_ERROR; + layoutInfo = NULL; + ClassReleaseChildLock(fdoExtension); + DiskReleasePartitioningLock(fdoExtension); + break; + } + + // + // Search the on-disk partition information to find the root containing + // partition (top-to-bottom). + // + // Remember: commonExtension == &PartitionZeroExtension->CommonExtension + // + + // + // All affected containers will have a new stopping offset + // that is equal to the new partition (logical drive) + // stopping offset. Walk the layout information from + // bottom-to-top searching for logical drive containers and + // propagating the change. + // + + containerPartition = + DiskFindContainingPartition( + layoutInfo, + pdoPartition, + FALSE); + + // + // This loop should only execute at most 2 times; once for + // the logical drive container, and once for the root + // extended partition container. If the growing partition + // is not contained, the loop does not run. + // + + while(containerPartition != NULL) { + LARGE_INTEGER containerStoppingOffset; + PPARTITION_INFORMATION_EX nextContainerPartition; + + // + // Plan ahead and get the container's container before + // modifing the current size. + // + + nextContainerPartition = + DiskFindContainingPartition( + layoutInfo, + containerPartition, + FALSE); + + // + // Figure out where the current container ends and test + // to see if it already encompasses the containee. + // + + containerStoppingOffset.QuadPart = + (containerPartition->StartingOffset.QuadPart + + containerPartition->PartitionLength.QuadPart - 1); + + if(newStoppingOffset.QuadPart <= + containerStoppingOffset.QuadPart) { + + // + // No need to continue since this container fits + // + break; + } + + // + // Adjust the container to have a stopping offset that + // matches the grown partition stopping offset. + // + + containerPartition->PartitionLength.QuadPart = + newStoppingOffset.QuadPart + 1 - + containerPartition->StartingOffset.QuadPart; + + containerPartition->RewritePartition = TRUE; + + // Continue with the next container + containerPartition = nextContainerPartition; + } + + // + // Wait until after searching the containers to update the + // partition size. + // + + pdoPartition->PartitionLength.QuadPart = + newPartitionLength.QuadPart; + + pdoPartition->RewritePartition = TRUE; + + // + // Commit the changes to disk + // + + status = DiskWritePartitionTableEx(fdoExtension, layoutInfo ); + + if( NT_SUCCESS(status) ) { + + // + // Everything looks good so commit the new length to the + // PDO. This has to be done carefully. We may potentially + // grow the partition in three steps: + // * increase the high-word of the partition length + // to be just below the new size - the high word should + // be greater than or equal to the current length. + // + // * change the low-word of the partition length to the + // new value - this value may potentially be lower than + // the current value (if the high part was changed which + // is why we changed that first) + // + // * change the high part to the correct value. + // + + if(newPartitionLength.HighPart > + pdoExtension->PartitionLength.HighPart) { + + // + // Swap in one less than the high word. + // + + InterlockedExchange( + &(pdoExtension->PartitionLength.HighPart), + (newPartitionLength.HighPart - 1)); + } + + // + // Swap in the low part. + // + + InterlockedExchange( + &(pdoExtension->PartitionLength.LowPart), + newPartitionLength.LowPart); + + if(newPartitionLength.HighPart != + pdoExtension->PartitionLength.HighPart) { + + // + // Swap in one less than the high word. + // + + InterlockedExchange( + &(pdoExtension->PartitionLength.HighPart), + newPartitionLength.HighPart); + } + } + + // + // Invalidate and free the cached partition table. + // + + DiskInvalidatePartitionTable(fdoExtension, TRUE); + + // + // Free the partition buffer regardless of the status + // + + ClassReleaseChildLock(fdoExtension); + DiskReleasePartitioningLock(fdoExtension); + + break; + } + + + case IOCTL_DISK_UPDATE_PROPERTIES: { + + // + // Invalidate the partition table and re-enumerate the device. + // + + if(DiskInvalidatePartitionTable(fdoExtension, FALSE)) { + IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations); + } + status = STATUS_SUCCESS; + + break; + } + + case IOCTL_DISK_MEDIA_REMOVAL: { + + // + // If the disk is not removable then don't allow this command. + // + + DebugPrint((2, "IOCTL_DISK_MEDIA_REMOVAL to device %p through irp %p\n", + DeviceObject, Irp)); + DebugPrint((2, "Device is a%s.\n", + commonExtension->IsFdo ? "n fdo" : " pdo")); + + if(!commonExtension->IsFdo) { + ClassReleaseRemoveLock(DeviceObject, Irp); + ExFreePool(srb); + SendToFdo(DeviceObject,Irp,status); + return status; + } + + if (!TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + // + // Fall through and let the class driver process the request. + // + goto defaultHandler; + + } + + + +defaultHandler: + default: { + + // + // Free the Srb, since it is not needed. + // + + ExFreePool(srb); + + // + // Pass the request to the common device control routine. + // + + return(ClassDeviceControl(DeviceObject, Irp)); + + break; + } + + } // end switch + + Irp->IoStatus.Status = status; + + if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) { + + IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); + } + + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + ExFreePool(srb); + return(status); + +} // end DiskDeviceControl() + + +NTSTATUS +DiskShutdownFlush ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called for a shutdown and flush IRPs. These are sent by the + system before it actually shuts down or when the file system does a flush. + A synchronize cache command is sent to the device if it is write caching. + If the device is removable an unlock command will be sent. This routine + will sent a shutdown or flush Srb to the port driver. + +Arguments: + + DriverObject - Pointer to device object to being shutdown by system. + + Irp - IRP involved. + +Return Value: + + NT Status + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension; + + PIO_STACK_LOCATION irpStack; + PSCSI_REQUEST_BLOCK srb; + NTSTATUS status; + PCDB cdb; + + // + // Send partition flush requests to the FDO + // + + if(!commonExtension->IsFdo) { + + PDEVICE_OBJECT lowerDevice = commonExtension->LowerDeviceObject; + + ClassReleaseRemoveLock(DeviceObject, Irp); + IoMarkIrpPending(Irp); + IoCopyCurrentIrpStackLocationToNext(Irp); + IoCallDriver(lowerDevice, Irp); + return STATUS_PENDING; + } + + // + // Allocate SCSI request block. + // + + srb = ExAllocatePoolWithTag(NonPagedPool, + sizeof(SCSI_REQUEST_BLOCK), + DISK_TAG_SRB); + + if (srb == NULL) { + + // + // Set the status and complete the request. + // + + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + ClassReleaseRemoveLock(DeviceObject, Irp); + ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + // + // Write length to SRB. + // + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set timeout value and mark the request as not being a tagged request. + // + + srb->TimeOutValue = fdoExtension->TimeOutValue * 4; + srb->QueueTag = SP_UNTAGGED; + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb->SrbFlags = fdoExtension->SrbFlags; + + // + // If the write cache is enabled then send a synchronize cache request. + // + + if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE)) { + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->CdbLength = 10; + + srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE; + + status = ClassSendSrbSynchronous(DeviceObject, + srb, + NULL, + 0, + TRUE); + + DebugPrint((1, "DiskShutdownFlush: Synchonize cache sent. Status = %lx\n", status )); + } + + // + // Unlock the device if it is removable and this is a shutdown. + // + + irpStack = IoGetCurrentIrpStackLocation(Irp); + + if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA) && + irpStack->MajorFunction == IRP_MJ_SHUTDOWN) { + + srb->CdbLength = 6; + cdb = (PVOID) srb->Cdb; + cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL; + cdb->MEDIA_REMOVAL.Prevent = FALSE; + + // + // Set timeout value. + // + + srb->TimeOutValue = fdoExtension->TimeOutValue; + status = ClassSendSrbSynchronous(DeviceObject, + srb, + NULL, + 0, + TRUE); + + DebugPrint((1, "DiskShutdownFlush: Unlock device request sent. Status = %lx\n", status )); + } + + srb->CdbLength = 0; + + // + // Save a few parameters in the current stack location. + // + + srb->Function = irpStack->MajorFunction == IRP_MJ_SHUTDOWN ? + SRB_FUNCTION_SHUTDOWN : SRB_FUNCTION_FLUSH; + + // + // Set the retry count to zero. + // + + irpStack->Parameters.Others.Argument4 = (PVOID) 0; + + // + // Set up IoCompletion routine address. + // + + IoSetCompletionRoutine(Irp, ClassIoComplete, srb, TRUE, TRUE, TRUE); + + // + // Get next stack location and + // set major function code. + // + + irpStack = IoGetNextIrpStackLocation(Irp); + + irpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Set up SRB for execute scsi request. + // Save SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = srb; + + // + // Set up Irp Address. + // + + srb->OriginalRequest = Irp; + + // + // Call the port driver to process the request. + // + + IoMarkIrpPending(Irp); + IoCallDriver(commonExtension->LowerDeviceObject, Irp); + return STATUS_PENDING; +} // end DiskShutdown() + + +NTSTATUS +DiskModeSelect( + IN PDEVICE_OBJECT Fdo, + IN PCHAR ModeSelectBuffer, + IN ULONG Length, + IN BOOLEAN SavePage + ) + +/*++ + +Routine Description: + + This routine sends a mode select command. + +Arguments: + + DeviceObject - Supplies the device object associated with this request. + + ModeSelectBuffer - Supplies a buffer containing the page data. + + Length - Supplies the length in bytes of the mode select buffer. + + SavePage - Indicates that parameters should be written to disk. + +Return Value: + + Length of the transferred data is returned. + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCDB cdb; + SCSI_REQUEST_BLOCK srb; + ULONG retries = 1; + ULONG length2; + NTSTATUS status; + PULONG buffer; + PMODE_PARAMETER_BLOCK blockDescriptor; + + PAGED_CODE(); + + ASSERT_FDO(Fdo); + + length2 = Length + sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK); + + // + // Allocate buffer for mode select header, block descriptor, and mode page. + // + + buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + length2, + DISK_TAG_MODE_DATA); + + if(buffer == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(buffer, length2); + + // + // Set length in header to size of mode page. + // + + ((PMODE_PARAMETER_HEADER)buffer)->BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK); + + blockDescriptor = (PMODE_PARAMETER_BLOCK)(buffer + 1); + + // + // Set size + // + + blockDescriptor->BlockLength[1]=0x02; + + // + // Copy mode page to buffer. + // + + RtlCopyMemory(buffer + 3, ModeSelectBuffer, Length); + + // + // Zero SRB. + // + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + // + // Build the MODE SELECT CDB. + // + + srb.CdbLength = 6; + cdb = (PCDB)srb.Cdb; + + // + // Set timeout value from device extension. + // + + srb.TimeOutValue = fdoExtension->TimeOutValue * 2; + + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.SPBit = SavePage; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = (UCHAR)(length2); + +Retry: + + status = ClassSendSrbSynchronous(Fdo, + &srb, + buffer, + length2, + TRUE); + + if (status == STATUS_VERIFY_REQUIRED) { + + // + // Routine ClassSendSrbSynchronous does not retry requests returned with + // this status. + // + + if (retries--) { + + // + // Retry request. + // + + goto Retry; + } + + } else if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) { + status = STATUS_SUCCESS; + } + + ExFreePool(buffer); + + return status; +} // end DiskModeSelect() + + +// +// This routine is structured as a work-item routine +// +VOID +DisableWriteCache( + IN PDEVICE_OBJECT Fdo, + IN PIO_WORKITEM WorkItem + ) + +{ + ULONG specialFlags = 0; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + DISK_CACHE_INFORMATION cacheInfo; + NTSTATUS status; + + PAGED_CODE(); + + fdoExtension = Fdo->DeviceExtension; + + ASSERT(fdoExtension->CommonExtension.IsFdo); + + DebugPrint((1, "Disk.DisableWriteCache: Disabling Write Cache\n")); + + ClassGetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceSpecialFlags, + &specialFlags); + + RtlZeroMemory(&cacheInfo, sizeof(DISK_CACHE_INFORMATION)); + + status = DiskGetCacheInformation(fdoExtension, &cacheInfo); + + if (NT_SUCCESS(status) && (cacheInfo.WriteCacheEnabled == TRUE)) { + + cacheInfo.WriteCacheEnabled = FALSE; + + status = DiskSetCacheInformation(fdoExtension, &cacheInfo); + + if (status == STATUS_INVALID_DEVICE_REQUEST) + { + // + // This device does not allow for + // the write cache to be disabled + // + SET_FLAG(specialFlags, HackDisableWriteCacheNotSupported); + + SET_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE_NOT_SUPPORTED); + } + + // + // ISSUE ( April 5, 2001 ) : This should happen inside of DiskSetCacheInformation + // + CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE); + } + + // + // Set a flag in the registry to help + // identify this device across boots + // + SET_FLAG(specialFlags, HackDisableWriteCache); + + SET_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE); + + ClassSetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceSpecialFlags, + specialFlags); + + IoFreeWorkItem(WorkItem); +} + + +// +// This routine is structured as a work-item routine +// +VOID +DiskIoctlVerify( + IN PDEVICE_OBJECT Fdo, + IN PDISK_VERIFY_WORKITEM_CONTEXT Context + ) + +{ + PIRP Irp = Context->Irp; + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension = Fdo->DeviceExtension; + PDISK_DATA DiskData = (PDISK_DATA)FdoExtension->CommonExtension.DriverData; + PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer; + PSCSI_REQUEST_BLOCK Srb = Context->Srb; + PCDB Cdb = (PCDB)Srb->Cdb; + LARGE_INTEGER byteOffset; + ULONG sectorOffset; + USHORT sectorCount; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + ASSERT(FdoExtension->CommonExtension.IsFdo); + + // + // We don't need to hold on to this memory as + // the following operation may take some time + // + + IoFreeWorkItem(Context->WorkItem); + + DebugPrint((1, "Disk.DiskIoctlVerify: Spliting up the request\n")); + + // + // Add disk offset to starting the sector + // + + byteOffset.QuadPart = FdoExtension->CommonExtension.StartingOffset.QuadPart + + verifyInfo->StartingOffset.QuadPart; + + // + // Convert byte offset to the sector offset + // + + sectorOffset = (ULONG)(byteOffset.QuadPart >> FdoExtension->SectorShift); + + // + // Convert ULONG byte count to USHORT sector count. + // + + sectorCount = (USHORT)(verifyInfo->Length >> FdoExtension->SectorShift); + + // + // Make sure that all previous verify requests have indeed completed + // This greatly reduces the possibility of a Denial-of-Service attack + // + + KeWaitForMutexObject(&DiskData->VerifyMutex, + Executive, + KernelMode, + FALSE, + NULL); + + while (NT_SUCCESS(status) && (sectorCount != 0)) + { + USHORT numSectors = min(sectorCount, MAX_SECTORS_PER_VERIFY); + + RtlZeroMemory(Srb, SCSI_REQUEST_BLOCK_SIZE); + + Srb->CdbLength = 10; + + Cdb->CDB10.OperationCode = SCSIOP_VERIFY; + + // + // Move little endian values into CDB in big endian format + // + + Cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)§orOffset)->Byte3; + Cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)§orOffset)->Byte2; + Cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)§orOffset)->Byte1; + Cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)§orOffset)->Byte0; + + Cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numSectors)->Byte1; + Cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numSectors)->Byte0; + + // + // Calculate the request timeout value based + // on the number of sectors being verified + // + + Srb->TimeOutValue = ((numSectors + 0x7F) >> 7) * FdoExtension->TimeOutValue; + + status = ClassSendSrbSynchronous(Fdo, + Srb, + NULL, + 0, + FALSE); + + ASSERT(status != STATUS_NONEXISTENT_SECTOR); + + sectorCount -= numSectors; + sectorOffset += numSectors; + } + + KeReleaseMutex(&DiskData->VerifyMutex, FALSE); + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + ClassReleaseRemoveLock(Fdo, Irp); + ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT); + + ExFreePool(Srb); + ExFreePool(Context); +} + + + +VOID +DiskFdoProcessError( + PDEVICE_OBJECT Fdo, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ) + +/*++ + +Routine Description: + + This routine checks the type of error. If the error indicates an underrun + then indicate the request should be retried. + +Arguments: + + Fdo - Supplies a pointer to the functional device object. + + Srb - Supplies a pointer to the failing Srb. + + Status - Status with which the IRP will be completed. + + Retry - Indication of whether the request will be retried. + +Return Value: + + None. + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCDB cdb = (PCDB)(Srb->Cdb); + + ASSERT(fdoExtension->CommonExtension.IsFdo); + + if (*Status == STATUS_DATA_OVERRUN && + ( cdb->CDB10.OperationCode == SCSIOP_WRITE || + cdb->CDB10.OperationCode == SCSIOP_READ)) { + + *Retry = TRUE; + + // + // Update the error count for the device. + // + + fdoExtension->ErrorCount++; + + } else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_ERROR && + Srb->ScsiStatus == SCSISTAT_BUSY) { + + // + // a disk drive should never be busy this long. Reset the scsi bus + // maybe this will clear the condition. + // + + ResetBus(Fdo); + + // + // Update the error count for the device. + // + + fdoExtension->ErrorCount++; + + } else { + + BOOLEAN invalidatePartitionTable = FALSE; + + // + // See if this might indicate that something on the drive has changed. + // + + if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && + (Srb->SenseInfoBufferLength >= + FIELD_OFFSET(SENSE_DATA, CommandSpecificInformation))) { + + PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer; + ULONG senseKey = senseBuffer->SenseKey & 0xf; + ULONG asc = senseBuffer->AdditionalSenseCode; + ULONG ascq = senseBuffer->AdditionalSenseCodeQualifier; + + switch (senseKey) { + + case SCSI_SENSE_ILLEGAL_REQUEST: { + + switch (asc) { + + case SCSI_ADSENSE_INVALID_CDB: { + + if (((cdb->CDB10.OperationCode == SCSIOP_READ) || + (cdb->CDB10.OperationCode == SCSIOP_WRITE)) && + (cdb->CDB10.ForceUnitAccess) && + TEST_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE)) { + + // + // This device does not permit FUA while + // the DEV_WRITE_CACHE flag is turned on + // + + PIO_WORKITEM workItem = IoAllocateWorkItem(Fdo); + if (workItem) { + + IoQueueWorkItem(workItem, + (PIO_WORKITEM_ROUTINE)DisableWriteCache, + CriticalWorkQueue, + workItem); + } + + cdb->CDB10.ForceUnitAccess = FALSE; + *Retry = TRUE; + } + + break; + } + } // end switch(asc) + break; + } + + case SCSI_SENSE_NOT_READY: { + + switch (asc) { + case SCSI_ADSENSE_LUN_NOT_READY: { + switch (ascq) { + case SCSI_SENSEQ_BECOMING_READY: + case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: + case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: { + invalidatePartitionTable = TRUE; + break; + } + } // end switch(ascq) + break; + } + + case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: { + invalidatePartitionTable = TRUE; + break; + } + } // end switch(asc) + break; + } + + case SCSI_SENSE_MEDIUM_ERROR: { + invalidatePartitionTable = TRUE; + break; + } + + case SCSI_SENSE_HARDWARE_ERROR: { + invalidatePartitionTable = TRUE; + break; + } + + case SCSI_SENSE_UNIT_ATTENTION: { + switch (senseBuffer->AdditionalSenseCode) { + case SCSI_ADSENSE_MEDIUM_CHANGED: { + invalidatePartitionTable = TRUE; + break; + } + } + break; + } + + case SCSI_SENSE_RECOVERED_ERROR: { + invalidatePartitionTable = TRUE; + break; + } + + } // end switch(senseKey) + } else { + + // + // On any exceptional scsi condition which might indicate that the + // device was changed we will flush out the state of the partition + // table. + // + + switch (SRB_STATUS(Srb->SrbStatus)) { + case SRB_STATUS_INVALID_LUN: + case SRB_STATUS_INVALID_TARGET_ID: + case SRB_STATUS_NO_DEVICE: + case SRB_STATUS_NO_HBA: + case SRB_STATUS_INVALID_PATH_ID: + case SRB_STATUS_COMMAND_TIMEOUT: + case SRB_STATUS_TIMEOUT: + case SRB_STATUS_SELECTION_TIMEOUT: + case SRB_STATUS_REQUEST_FLUSHED: + case SRB_STATUS_UNEXPECTED_BUS_FREE: + case SRB_STATUS_PARITY_ERROR: + case SRB_STATUS_ERROR: { + invalidatePartitionTable = TRUE; + break; + } + } // end switch(Srb->SrbStatus) + } + + if(invalidatePartitionTable) { + if(DiskInvalidatePartitionTable(fdoExtension, FALSE)) { + IoInvalidateDeviceRelations(fdoExtension->LowerPdo, + BusRelations); + } + } + } + return; +} + + +VOID +DiskSetSpecialHacks( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN ULONG_PTR Data + ) + +/*++ + +Routine Description: + + This function checks to see if an SCSI logical unit requires speical + flags to be set. + +Arguments: + + Fdo - Supplies the device object to be tested. + + InquiryData - Supplies the inquiry data returned by the device of interest. + + AdapterDescriptor - Supplies the capabilities of the device object. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_OBJECT fdo = FdoExtension->DeviceObject; + + PAGED_CODE(); + + DebugPrint((1, "Disk SetSpecialHacks, Setting Hacks %p\n", Data)); + + // + // Found a listed controller. Determine what must be done. + // + + if (TEST_FLAG(Data, HackDisableTaggedQueuing)) { + + // + // Disable tagged queuing. + // + + CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); + } + + if (TEST_FLAG(Data, HackDisableSynchronousTransfers)) { + + // + // Disable synchronous data transfers. + // + + SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + + } + + if (TEST_FLAG(Data, HackDisableSpinDown)) { + + // + // Disable spinning down of drives. + // + + SET_FLAG(FdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_DISABLE_SPIN_DOWN); + + } + + if (TEST_FLAG(Data, HackDisableWriteCache)) { + + // + // Disable the drive's write cache + // + + SET_FLAG(FdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_DISABLE_WRITE_CACHE); + + } + + if (TEST_FLAG(Data, HackCauseNotReportableHack)) { + + SET_FLAG(FdoExtension->ScanForSpecialFlags, + CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK); + } + + if (TEST_FLAG(fdo->Characteristics, FILE_REMOVABLE_MEDIA) && + TEST_FLAG(Data, HackRequiresStartUnitCommand) + ) { + + // + // this is a list of vendors who require the START_UNIT command + // + + DebugPrint((1, "DiskScanForSpecial (%p) => This unit requires " + " START_UNITS\n", fdo)); + SET_FLAG(FdoExtension->DeviceFlags, DEV_SAFE_START_UNIT); + + } + + return; +} + + +VOID +DiskScanRegistryForSpecial( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) + +/*++ + +Routine Description: + + This function checks the registry to see if the SCSI logical unit + requires special attention. + +Arguments: + + Fdo - Supplies the device object to be tested. + +Return Value: + + None. + +--*/ + +{ + ULONG specialFlags = 0; + + PAGED_CODE(); + + ClassGetDeviceParameter(FdoExtension, DiskDeviceParameterSubkey, DiskDeviceSpecialFlags, &specialFlags); + + if (TEST_FLAG(specialFlags, HackDisableWriteCache)) + { + // + // This device had previously failed to perform an FUA with the DEV_WRITE_CACHE + // flag turned on. Set a bit to inform DiskStartFdo() to disable the write cache + // + + SET_FLAG(FdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE); + } + + if (TEST_FLAG(specialFlags, HackDisableWriteCacheNotSupported)) + { + // + // This device does not permit disabling of the write cache + // + + SET_FLAG(FdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE_NOT_SUPPORTED); + } +} + + +VOID +ResetBus( + IN PDEVICE_OBJECT Fdo + ) + +/*++ + +Routine Description: + + This command sends a reset bus command to the SCSI port driver. + +Arguments: + + Fdo - The functional device object for the logical unit with hardware problem. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION irpStack; + PIRP irp; + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PSCSI_REQUEST_BLOCK srb; + PCOMPLETION_CONTEXT context; + + DebugPrint((1, "Disk ResetBus: Sending reset bus request to port driver.\n")); + + // + // Allocate Srb from nonpaged pool. + // + + context = ExAllocatePoolWithTag(NonPagedPool, + sizeof(COMPLETION_CONTEXT), + DISK_TAG_CCONTEXT); + + if(context == NULL) { + return; + } + + // + // Save the device object in the context for use by the completion + // routine. + // + + context->DeviceObject = Fdo; + srb = &context->Srb; + + // + // Zero out srb. + // + + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + // + // Write length to SRB. + // + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + srb->Function = SRB_FUNCTION_RESET_BUS; + + // + // Build the asynchronous request to be sent to the port driver. + // Since this routine is called from a DPC the IRP should always be + // available. + // + + irp = IoAllocateIrp(Fdo->StackSize, FALSE); + + if(irp == NULL) { + ExFreePool(context); + return; + } + + ClassAcquireRemoveLock(Fdo, irp); + + IoSetCompletionRoutine(irp, + (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion, + context, + TRUE, + TRUE, + TRUE); + + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->MajorFunction = IRP_MJ_SCSI; + + srb->OriginalRequest = irp; + + // + // Store the SRB address in next stack for port driver. + // + + irpStack->Parameters.Scsi.Srb = srb; + + // + // Call the port driver with the IRP. + // + + IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp); + + return; + +} // end ResetBus() + + +NTSTATUS +DiskQueryPnpCapabilities( + IN PDEVICE_OBJECT DeviceObject, + IN PDEVICE_CAPABILITIES Capabilities + ) + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = commonExtension->DriverData; + + PAGED_CODE(); + + ASSERT(DeviceObject); + ASSERT(Capabilities); + + if(commonExtension->IsFdo) { + return STATUS_NOT_IMPLEMENTED; + } else { + + PPHYSICAL_DEVICE_EXTENSION physicalExtension = + DeviceObject->DeviceExtension; + + Capabilities->SilentInstall = 1; + Capabilities->RawDeviceOK = 1; + Capabilities->Address = commonExtension->PartitionNumber; + + if(!TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) { + + // + // Media's not removable, deviceId/DeviceInstance should be + // globally unique. + // + + Capabilities->UniqueID = 1; + } else { + Capabilities->UniqueID = 0; + } + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +DiskGetCacheInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PDISK_CACHE_INFORMATION CacheInfo + ) + +{ + PMODE_PARAMETER_HEADER modeData; + PMODE_CACHING_PAGE pageData; + + ULONG length; + + NTSTATUS status; + + PAGED_CODE(); + + modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + MODE_DATA_SIZE, + DISK_TAG_DISABLE_CACHE); + + if (modeData == NULL) { + + DebugPrint((1, "DiskGetSetCacheInformation: Unable to allocate mode " + "data buffer\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(modeData, MODE_DATA_SIZE); + + length = ClassModeSense(FdoExtension->DeviceObject, + (PUCHAR) modeData, + MODE_DATA_SIZE, + MODE_SENSE_RETURN_ALL); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + // + // Retry the request in case of a check condition. + // + + length = ClassModeSense(FdoExtension->DeviceObject, + (PUCHAR) modeData, + MODE_DATA_SIZE, + MODE_SENSE_RETURN_ALL); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + + DebugPrint((1, "Disk.DisableWriteCache: Mode Sense failed\n")); + + ExFreePool(modeData); + return STATUS_IO_DEVICE_ERROR; + } + } + + // + // If the length is greater than length indicated by the mode data reset + // the data to the mode data. + // + + if (length > (ULONG) (modeData->ModeDataLength + 1)) { + length = modeData->ModeDataLength + 1; + } + + // + // Check to see if the write cache is enabled. + // + + pageData = ClassFindModePage((PUCHAR) modeData, + length, + MODE_PAGE_CACHING, + TRUE); + + // + // Check if valid caching page exists. + // + + if (pageData == NULL) { + ExFreePool(modeData); + return STATUS_NOT_SUPPORTED; + } + + // + // Copy the parameters over. + // + + RtlZeroMemory(CacheInfo, sizeof(DISK_CACHE_INFORMATION)); + + CacheInfo->ParametersSavable = pageData->PageSavable; + + CacheInfo->ReadCacheEnabled = !(pageData->ReadDisableCache); + CacheInfo->WriteCacheEnabled = pageData->WriteCacheEnable; + + CacheInfo->ReadRetentionPriority = pageData->ReadRetensionPriority; + CacheInfo->WriteRetentionPriority = pageData->WriteRetensionPriority; + + CacheInfo->DisablePrefetchTransferLength = + ((pageData->DisablePrefetchTransfer[0] << 8) + + pageData->DisablePrefetchTransfer[1]); + + CacheInfo->ScalarPrefetch.Minimum = + ((pageData->MinimumPrefetch[0] << 8) + pageData->MinimumPrefetch[1]); + + CacheInfo->ScalarPrefetch.Maximum = + ((pageData->MaximumPrefetch[0] << 8) + pageData->MaximumPrefetch[1]); + + if(pageData->MultiplicationFactor) { + CacheInfo->PrefetchScalar = TRUE; + CacheInfo->ScalarPrefetch.MaximumBlocks = + ((pageData->MaximumPrefetchCeiling[0] << 8) + + pageData->MaximumPrefetchCeiling[1]); + } + + ExFreePool(modeData); + return STATUS_SUCCESS; +} + + +NTSTATUS +DiskSetCacheInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PDISK_CACHE_INFORMATION CacheInfo + ) + +{ + PMODE_PARAMETER_HEADER modeData; + ULONG length; + + PMODE_CACHING_PAGE pageData; + + ULONG i; + + ULONG errorCode; + NTSTATUS status; + + PAGED_CODE(); + + modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + MODE_DATA_SIZE, + DISK_TAG_DISABLE_CACHE); + + if (modeData == NULL) { + + DebugPrint((1, "DiskSetCacheInformation: Unable to allocate mode " + "data buffer\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(modeData, MODE_DATA_SIZE); + + length = ClassModeSense(FdoExtension->DeviceObject, + (PUCHAR) modeData, + MODE_DATA_SIZE, + MODE_PAGE_CACHING); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + // + // Retry the request in case of a check condition. + // + + length = ClassModeSense(FdoExtension->DeviceObject, + (PUCHAR) modeData, + MODE_DATA_SIZE, + MODE_PAGE_CACHING); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + + DebugPrint((1, "Disk.DisableWriteCache: Mode Sense failed\n")); + + ExFreePool(modeData); + return STATUS_IO_DEVICE_ERROR; + } + } + + // + // If the length is greater than length indicated by the mode data reset + // the data to the mode data. + // + + if (length > (ULONG) (modeData->ModeDataLength + 1)) { + length = modeData->ModeDataLength + 1; + } + + // + // Check to see if the write cache is enabled. + // + + pageData = ClassFindModePage((PUCHAR) modeData, + length, + MODE_PAGE_CACHING, + TRUE); + + // + // Check if valid caching page exists. + // + + if (pageData == NULL) { + ExFreePool(modeData); + return STATUS_NOT_SUPPORTED; + } + + // + // Don't touch any of the normal parameters - not all drives actually + // use the correct size of caching mode page. Just change the things + // which the user could have modified. + // + + pageData->PageSavable = FALSE; + + pageData->ReadDisableCache = !(CacheInfo->ReadCacheEnabled); + pageData->MultiplicationFactor = CacheInfo->PrefetchScalar; + pageData->WriteCacheEnable = CacheInfo->WriteCacheEnabled; + + pageData->WriteRetensionPriority = (UCHAR) CacheInfo->WriteRetentionPriority; + pageData->ReadRetensionPriority = (UCHAR) CacheInfo->ReadRetentionPriority; + + pageData->DisablePrefetchTransfer[0] = + (UCHAR) (CacheInfo->DisablePrefetchTransferLength >> 8); + pageData->DisablePrefetchTransfer[1] = + (UCHAR) (CacheInfo->DisablePrefetchTransferLength & 0x00ff); + + pageData->MinimumPrefetch[0] = + (UCHAR) (CacheInfo->ScalarPrefetch.Minimum >> 8); + pageData->MinimumPrefetch[1] = + (UCHAR) (CacheInfo->ScalarPrefetch.Minimum & 0x00ff); + + pageData->MaximumPrefetch[0] = + (UCHAR) (CacheInfo->ScalarPrefetch.Maximum >> 8); + pageData->MaximumPrefetch[1] = + (UCHAR) (CacheInfo->ScalarPrefetch.Maximum & 0x00ff); + + if(pageData->MultiplicationFactor) { + + pageData->MaximumPrefetchCeiling[0] = + (UCHAR) (CacheInfo->ScalarPrefetch.MaximumBlocks >> 8); + pageData->MaximumPrefetchCeiling[1] = + (UCHAR) (CacheInfo->ScalarPrefetch.MaximumBlocks & 0x00ff); + } + + // + // We will attempt (twice) to issue the mode select with the page. + // + + // + // First save away the current state of the disk cache so we know what to + // log if the request fails. + // + + if(TEST_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE)) { + errorCode = IO_WRITE_CACHE_ENABLED; + } else { + errorCode = IO_WRITE_CACHE_DISABLED; + } + + for(i = 0; i < 2; i++) { + status = DiskModeSelect(FdoExtension->DeviceObject, + (PUCHAR) pageData, + (pageData->PageLength + 2), + CacheInfo->ParametersSavable); + + if(NT_SUCCESS(status)) { + if(CacheInfo->WriteCacheEnabled) { + SET_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE); + errorCode = IO_WRITE_CACHE_ENABLED; + } else { + CLEAR_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE); + errorCode = IO_WRITE_CACHE_DISABLED; + } + + break; + } + } + + { + PIO_ERROR_LOG_PACKET logEntry; + + // + // Log the appropriate informational or error entry. + // + + logEntry = IoAllocateErrorLogEntry( + FdoExtension->DeviceObject, + sizeof(IO_ERROR_LOG_PACKET) + (4 * sizeof(ULONG))); + + if (logEntry != NULL) { + + PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData; + + logEntry->FinalStatus = status; + logEntry->ErrorCode = errorCode; + logEntry->SequenceNumber = 0; + logEntry->MajorFunctionCode = IRP_MJ_SCSI; + logEntry->IoControlCode = 0; + logEntry->RetryCount = 0; + logEntry->UniqueErrorValue = 0x1; + logEntry->DumpDataSize = 4; + + logEntry->DumpData[0] = diskData->ScsiAddress.PathId; + logEntry->DumpData[1] = diskData->ScsiAddress.TargetId; + logEntry->DumpData[2] = diskData->ScsiAddress.Lun; + logEntry->DumpData[3] = CacheInfo->WriteCacheEnabled; + + // + // Write the error log packet. + // + + IoWriteErrorLogEntry(logEntry); + } + } + + ExFreePool(modeData); + return status; +} + + +PPARTITION_INFORMATION_EX +DiskPdoFindPartitionEntry( + IN PPHYSICAL_DEVICE_EXTENSION Pdo, + IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo + ) + +{ + PCOMMON_DEVICE_EXTENSION commonExtension= &(Pdo->CommonExtension); + ULONG partitionIndex; + + PAGED_CODE(); + + + DebugPrint((1, "DiskPdoFindPartitionEntry: Searching layout for " + "matching partition.\n")); + + for(partitionIndex = 0; + partitionIndex < LayoutInfo->PartitionCount; + partitionIndex++) { + + PPARTITION_INFORMATION_EX partitionInfo; + + // + // Get the partition entry + // + + partitionInfo = &LayoutInfo->PartitionEntry[partitionIndex]; + + // + // See if it is the one we are looking for... + // + + if( LayoutInfo->PartitionStyle == PARTITION_STYLE_MBR && + (partitionInfo->Mbr.PartitionType == PARTITION_ENTRY_UNUSED || + IsContainerPartition(partitionInfo->Mbr.PartitionType)) ) { + + continue; + } + + if( LayoutInfo->PartitionStyle == PARTITION_STYLE_GPT && + DiskCompareGuid (&partitionInfo->Gpt.PartitionType, &GUID_NULL) == 00) { + + continue; + } + + if( (commonExtension->StartingOffset.QuadPart == + partitionInfo->StartingOffset.QuadPart) && + (commonExtension->PartitionLength.QuadPart == + partitionInfo->PartitionLength.QuadPart)) { + + // + // Found it! + // + + DebugPrint((1, "DiskPdoFindPartitionEntry: Found matching " + "partition.\n")); + return partitionInfo; + } + } + + return NULL; +} + + +PPARTITION_INFORMATION_EX +DiskFindAdjacentPartition( + IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo, + IN PPARTITION_INFORMATION_EX BasePartition + ) +{ + ULONG partitionIndex; + LONGLONG baseStoppingOffset; + LONGLONG adjacentStartingOffset; + PPARTITION_INFORMATION_EX adjacentPartition = 0; + + ASSERT(LayoutInfo && BasePartition); + + PAGED_CODE(); + + DebugPrint((1, "DiskPdoFindAdjacentPartition: Searching layout for adjacent partition.\n")); + + // + // Construct the base stopping offset for comparison + // + + baseStoppingOffset = (BasePartition->StartingOffset.QuadPart + + BasePartition->PartitionLength.QuadPart - + 1); + + adjacentStartingOffset = MAXLONGLONG; + + for(partitionIndex = 0; + partitionIndex < LayoutInfo->PartitionCount; + partitionIndex++) { + + PPARTITION_INFORMATION_EX partitionInfo; + + // + // Get the partition entry + // + + partitionInfo = &LayoutInfo->PartitionEntry[partitionIndex]; + + // + // See if it is the one we are looking for... + // + + if( LayoutInfo->PartitionStyle == PARTITION_STYLE_MBR && + partitionInfo->Mbr.PartitionType == PARTITION_ENTRY_UNUSED ) { + + continue; + } + + if( LayoutInfo->PartitionStyle == PARTITION_STYLE_GPT && + DiskCompareGuid (&partitionInfo->Gpt.PartitionType, &GUID_NULL) == 00 ) { + + continue; + } + + + if((partitionInfo->StartingOffset.QuadPart > baseStoppingOffset) && + (partitionInfo->StartingOffset.QuadPart < adjacentStartingOffset)) { + + // Found a closer neighbor...update and remember. + adjacentPartition = partitionInfo; + + adjacentStartingOffset = adjacentPartition->StartingOffset.QuadPart; + + DebugPrint((1, "DiskPdoFindAdjacentPartition: Found adjacent " + "partition.\n")); + } + } + return adjacentPartition; +} + + +PPARTITION_INFORMATION_EX +DiskFindContainingPartition( + IN PDRIVE_LAYOUT_INFORMATION_EX LayoutInfo, + IN PPARTITION_INFORMATION_EX BasePartition, + IN BOOLEAN SearchTopToBottom + ) + +{ + + LONG partitionIndex; + LONG startIndex; + LONG stopIndex; + LONG stepIndex; + + LONGLONG baseStoppingOffset; + LONGLONG containerStoppingOffset; + + PPARTITION_INFORMATION_EX partitionInfo = 0; + PPARTITION_INFORMATION_EX containerPartition = 0; + + PAGED_CODE(); + + ASSERT( LayoutInfo && BasePartition); + + DebugPrint((1, "DiskFindContainingPartition: Searching for extended partition.\n")); + + if( LayoutInfo->PartitionCount != 0) { + + baseStoppingOffset = (BasePartition->StartingOffset.QuadPart + + BasePartition->PartitionLength.QuadPart - 1); + + // + // Determine the search direction and setup the loop + // + if(SearchTopToBottom == TRUE) { + + startIndex = 0; + stopIndex = LayoutInfo->PartitionCount; + stepIndex = +1; + } else { + startIndex = LayoutInfo->PartitionCount - 1; + stopIndex = -1; + stepIndex = -1; + } + + // + // Using the loop parameters, walk the layout information and + // return the first containing partition. + // + + for(partitionIndex = startIndex; + partitionIndex != stopIndex; + partitionIndex += stepIndex) { + + // + // Get the next partition entry + // + + partitionInfo = &LayoutInfo->PartitionEntry[partitionIndex]; + + containerStoppingOffset = (partitionInfo->StartingOffset.QuadPart + + partitionInfo->PartitionLength.QuadPart - + 1); + + // + // Search for a containing partition without detecting the + // same partition as a container of itself. The starting + // offset of a partition and its container should never be + // the same; however, the stopping offset can be the same. + // + + // + // NOTE: Container partitions are MBR only. + // + + if((LayoutInfo->PartitionStyle == PARTITION_STYLE_MBR) && + (IsContainerPartition(partitionInfo->Mbr.PartitionType)) && + (BasePartition->StartingOffset.QuadPart > + partitionInfo->StartingOffset.QuadPart) && + (baseStoppingOffset <= containerStoppingOffset)) { + + containerPartition = partitionInfo; + + DebugPrint((1, "DiskFindContainingPartition: Found a " + "containing extended partition.\n")); + + break; + } + } + } + + return containerPartition; +} + + +NTSTATUS +DiskGetInfoExceptionInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PMODE_INFO_EXCEPTIONS ReturnPageData + ) +{ + PMODE_PARAMETER_HEADER modeData; + PMODE_INFO_EXCEPTIONS pageData; + ULONG length; + + NTSTATUS status; + + PAGED_CODE(); + + // + // ReturnPageData is allocated by the caller + // + + modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + MODE_DATA_SIZE, + DISK_TAG_INFO_EXCEPTION); + + if (modeData == NULL) { + + DebugPrint((1, "DiskGetInfoExceptionInformation: Unable to allocate mode " + "data buffer\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(modeData, MODE_DATA_SIZE); + + length = ClassModeSense(FdoExtension->DeviceObject, + (PUCHAR) modeData, + MODE_DATA_SIZE, + MODE_PAGE_FAULT_REPORTING); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + // + // Retry the request in case of a check condition. + // + + length = ClassModeSense(FdoExtension->DeviceObject, + (PUCHAR) modeData, + MODE_DATA_SIZE, + MODE_PAGE_FAULT_REPORTING); + + if (length < sizeof(MODE_PARAMETER_HEADER)) { + + + DebugPrint((1, "Disk.DisableWriteCache: Mode Sense failed\n")); + + ExFreePool(modeData); + return STATUS_IO_DEVICE_ERROR; + } + } + + // + // If the length is greater than length indicated by the mode data reset + // the data to the mode data. + // + + if (length > (ULONG) (modeData->ModeDataLength + 1)) { + length = modeData->ModeDataLength + 1; + } + + // + // Find the mode page for info exceptions + // + + pageData = ClassFindModePage((PUCHAR) modeData, + length, + MODE_PAGE_FAULT_REPORTING, + TRUE); + + if (pageData != NULL) { + RtlCopyMemory(ReturnPageData, pageData, sizeof(MODE_INFO_EXCEPTIONS)); + status = STATUS_SUCCESS; + } else { + status = STATUS_NOT_SUPPORTED; + } + + DebugPrint((3, "DiskGetInfoExceptionInformation: %s support SMART for device %x\n", + NT_SUCCESS(status) ? "does" : "does not", + FdoExtension->DeviceObject)); + + + ExFreePool(modeData); + return(status); +} + + +NTSTATUS +DiskSetInfoExceptionInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PMODE_INFO_EXCEPTIONS PageData + ) + +{ + ULONG i; + NTSTATUS status; + + PAGED_CODE(); + + // + // We will attempt (twice) to issue the mode select with the page. + // Make the setting persistant so that we don't have to turn it back + // on after a bus reset. + // + + for (i = 0; i < 2; i++) + { + status = DiskModeSelect(FdoExtension->DeviceObject, + (PUCHAR) PageData, + sizeof(MODE_INFO_EXCEPTIONS), + TRUE); + + } + + DebugPrint((3, "DiskSetInfoExceptionInformation: %s for device %p\n", + NT_SUCCESS(status) ? "succeeded" : "failed", + FdoExtension->DeviceObject)); + + return status; +} + + +#if 0 +#if defined(_X86_) + +NTSTATUS +DiskQuerySuggestedLinkName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + The routine try to find a suggested link name from registry for Removable + using device object names of NT4 and NT3.51. + +Arguments: + + DeviceObject - Pointer to driver object created by system. + Irp - IRP involved. + +Return Value: + + NTSTATUS + +--*/ + +{ + PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName; + WCHAR driveLetterNameBuffer[10]; + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + PWSTR valueName; + UNICODE_STRING driveLetterName; + NTSTATUS status; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION p0Extension = commonExtension->PartitionZeroExtension; + ULONG i, diskCount; + PCONFIGURATION_INFORMATION configurationInformation; + + PAGED_CODE(); + + DebugPrint((1, "DISK: IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME to device %#08lx" + " through irp %#08lx\n", + DeviceObject, Irp)); + + DebugPrint((1, " - DeviceNumber %d, - PartitionNumber %d\n", + p0Extension->DeviceNumber, + commonExtension->PartitionNumber)); + + if (!TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) { + + status = STATUS_NOT_FOUND; + return status; + } + + if (commonExtension->PartitionNumber == 0) { + + status = STATUS_NOT_FOUND; + return status; + } + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) { + + status = STATUS_INVALID_PARAMETER; + return status; + } + + valueName = ExAllocatePoolWithTag(PagedPool, + sizeof(WCHAR) * 64, + DISK_TAG_NEC_98); + + if (!valueName) { + status = STATUS_INSUFFICIENT_RESOURCES; + return status; + } + + // + // Look for a device object name of NT4. + // + swprintf(valueName, L"\\Device\\Harddisk%d\\Partition%d", + p0Extension->DeviceNumber, + commonExtension->PartitionNumber); + + driveLetterName.Buffer = driveLetterNameBuffer; + driveLetterName.MaximumLength = 20; + driveLetterName.Length = 0; + + RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE)); + queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | + RTL_QUERY_REGISTRY_DIRECT; + queryTable[0].Name = valueName; + queryTable[0].EntryContext = &driveLetterName; + + status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + L"\\Registry\\Machine\\System\\DISK", + queryTable, NULL, NULL); + + if (!NT_SUCCESS(status)) { + + // + // Look for a device object name of NT3.51. + // scsimo.sys on NT3.51 created it as \Device\OpticalDiskX. + // The number X were a serial number from zero on only Removable, + // so we look for it serially without above DeviceNumber and PartitionNumber. + // + + configurationInformation = IoGetConfigurationInformation(); + diskCount = configurationInformation->DiskCount; + + for (i = 0; i < diskCount; i++) { + swprintf(valueName, L"\\Device\\OpticalDisk%d",i); + + driveLetterName.Buffer = driveLetterNameBuffer; + driveLetterName.MaximumLength = 20; + driveLetterName.Length = 0; + + RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE)); + queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | + RTL_QUERY_REGISTRY_DIRECT; + queryTable[0].Name = valueName; + queryTable[0].EntryContext = &driveLetterName; + + status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, + L"\\Registry\\Machine\\System\\DISK", + queryTable, NULL, NULL); + + if (NT_SUCCESS(status)) { + break; + } + } + + if (!NT_SUCCESS(status)) { + ExFreePool(valueName); + return status; + } + } + + if (driveLetterName.Length != 4 || + driveLetterName.Buffer[0] < 'A' || + driveLetterName.Buffer[0] > 'Z' || + driveLetterName.Buffer[1] != ':') { + + status = STATUS_NOT_FOUND; + ExFreePool(valueName); + return status; + } + + suggestedName = Irp->AssociatedIrp.SystemBuffer; + suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE; + suggestedName->NameLength = 28; + + Irp->IoStatus.Information = + FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28; + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + Irp->IoStatus.Information) { + + Irp->IoStatus.Information = + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME); + status = STATUS_BUFFER_OVERFLOW; + ExFreePool(valueName); + return status; + } + + RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, + L"\\Registry\\Machine\\System\\DISK", + valueName); + + ExFreePool(valueName); + + RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24); + suggestedName->Name[12] = driveLetterName.Buffer[0]; + suggestedName->Name[13] = ':'; + + return status; +} +#endif +#endif + + +NTSTATUS +DiskIoctlCreateDisk( + IN OUT PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp + ) + +/*++ + +Routine Description: + + Handler for IOCTL_DISK_CREATE_DISK ioctl. + +Arguments: + + DeviceObject - Device object representing a disk that will be created or + erased. + + Irp - The IRP for this request. + +Return Values: + + NTSTATUS code. + +--*/ + +{ + NTSTATUS status; + PCOMMON_DEVICE_EXTENSION commonExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PIO_STACK_LOCATION irpStack; + PDISK_DATA diskData; + PCREATE_DISK createDiskInfo; + + + PAGED_CODE (); + + ASSERT ( DeviceObject != NULL ); + ASSERT ( Irp != NULL ); + + // + // Initialization + // + + commonExtension = DeviceObject->DeviceExtension; + fdoExtension = DeviceObject->DeviceExtension; + + irpStack = IoGetCurrentIrpStackLocation(Irp); + diskData = (PDISK_DATA)(commonExtension->DriverData); + + + ASSERT (commonExtension->IsFdo); + + // + // Check the input buffer size. + // + + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof (CREATE_DISK) ) { + + return STATUS_INFO_LENGTH_MISMATCH; + } + + // + // If we are being asked to create a GPT disk on a system that doesn't + // support GPT, fail. + // + + createDiskInfo = (PCREATE_DISK)Irp->AssociatedIrp.SystemBuffer; + + if (DiskDisableGpt && + createDiskInfo->PartitionStyle == PARTITION_STYLE_GPT) { + + return STATUS_INVALID_PARAMETER; + } + + // + // Call the lower level Io routine to do the dirty work of writing a + // new partition table. + // + + DiskAcquirePartitioningLock(fdoExtension); + + DiskInvalidatePartitionTable(fdoExtension, TRUE); + + status = IoCreateDisk ( + commonExtension->PartitionZeroExtension->CommonExtension.DeviceObject, + Irp->AssociatedIrp.SystemBuffer + ); + DiskReleasePartitioningLock(fdoExtension); + ClassInvalidateBusRelations(DeviceObject); + + Irp->IoStatus.Status = status; + + return status; +} + + + +NTSTATUS +DiskIoctlGetDriveLayout( + IN OUT PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp + ) + +/*++ + +Routine Description: + + Handler for IOCTL_DISK_GET_DRIVE_LAYOUT ioctl. + + This ioctl has been replace by IOCTL_DISK_GET_DRIVE_LAYOUT_EX. + +Arguments: + + DeviceObject - Device object representing a disk the layout information + will be obtained for. + + Irp - The IRP for this request. + + +Return Values: + + NTSTATUS code. + +--*/ + +{ + NTSTATUS status; + ULONG size; + PDRIVE_LAYOUT_INFORMATION partitionList; + PDRIVE_LAYOUT_INFORMATION_EX partitionListEx; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PPHYSICAL_DEVICE_EXTENSION pdoExtension; + PCOMMON_DEVICE_EXTENSION commonExtension; + PIO_STACK_LOCATION irpStack; + PDISK_DATA diskData; + BOOLEAN invalidateBusRelations; + + + PAGED_CODE (); + + ASSERT ( DeviceObject ); + ASSERT ( Irp ); + + // + // Initialization + // + + partitionListEx = NULL; + partitionList = NULL; + fdoExtension = DeviceObject->DeviceExtension; + commonExtension = DeviceObject->DeviceExtension; + + irpStack = IoGetCurrentIrpStackLocation(Irp); + diskData = (PDISK_DATA)(commonExtension->DriverData); + + // + // Issue a read capacity to update the apparent size of the disk. + // + + DiskReadDriveCapacity(fdoExtension->DeviceObject); + + DiskAcquirePartitioningLock(fdoExtension); + + status = DiskReadPartitionTableEx(fdoExtension, FALSE, &partitionListEx); + + if (!NT_SUCCESS(status)) { + DiskReleasePartitioningLock(fdoExtension); + return status; + } + + // + // This ioctl is only supported on MBR partitioned disks. Fail the + // call otherwise. + // + + if (partitionListEx->PartitionStyle != PARTITION_STYLE_MBR) { + DiskReleasePartitioningLock(fdoExtension); + return STATUS_INVALID_DEVICE_REQUEST; + } + + + // + // The disk layout has been returned in the partitionListEx + // buffer. Determine its size and, if the data will fit + // into the intermediate buffer, return it. + // + + size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]); + size += partitionListEx->PartitionCount * sizeof(PARTITION_INFORMATION); + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + size) { + + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = size; + + DiskReleasePartitioningLock(fdoExtension); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Update the partition device objects and set valid partition + // numbers + // + + ASSERT(diskData->UpdatePartitionRoutine != NULL); + diskData->UpdatePartitionRoutine(DeviceObject, partitionListEx); + + // + // Convert the extended drive layout structure to a regular drive layout + // structure to return. DiskConvertExtendedToLayout() allocates pool + // that we must free. + // + + partitionList = DiskConvertExtendedToLayout(partitionListEx); + + if (partitionList == NULL) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + DiskReleasePartitioningLock (fdoExtension); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // We're done with the extended partition list now. + // + + partitionListEx = NULL; + + // + // Copy partition information to system buffer. + // + + RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, + partitionList, + size); + + Irp->IoStatus.Information = size; + Irp->IoStatus.Status = status; + + // + // Finally, free the buffer allocated by reading the + // partition table. + // + + ExFreePool(partitionList); + DiskReleasePartitioningLock(fdoExtension); + ClassInvalidateBusRelations(DeviceObject); + + return status; +} + + + +NTSTATUS +DiskIoctlGetDriveLayoutEx( + IN OUT PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp + ) + +/*++ + +Routine Description: + + Handler for IOCTL_DISK_GET_DRIVE_LAYOUT_EX ioctl. + + This ioctl replaces IOCTL_DISK_GET_DRIVE_LAYOUT. + +Arguments: + + DeviceObject - Device object representing a disk the layout information + will be obtained for. + + Irp - The IRP for this request. + + +Return Values: + + NTSTATUS code. + +--*/ + +{ + NTSTATUS status; + ULONG size; + PDRIVE_LAYOUT_INFORMATION_EX partitionList; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PPHYSICAL_DEVICE_EXTENSION pdoExtension; + PCOMMON_DEVICE_EXTENSION commonExtension; + PIO_STACK_LOCATION irpStack; + PDISK_DATA diskData; + BOOLEAN invalidateBusRelations; + + + PAGED_CODE (); + + ASSERT ( DeviceObject ); + ASSERT ( Irp ); + + // + // Initialization + // + + fdoExtension = DeviceObject->DeviceExtension; + pdoExtension = DeviceObject->DeviceExtension; + commonExtension = DeviceObject->DeviceExtension; + + irpStack = IoGetCurrentIrpStackLocation(Irp); + diskData = (PDISK_DATA)(commonExtension->DriverData); + + // + // Issue a read capacity to update the apparent size of the disk. + // + + DiskReadDriveCapacity(fdoExtension->DeviceObject); + + // + // Get the drive layout information. + // + + DiskAcquirePartitioningLock (fdoExtension); + + status = DiskReadPartitionTableEx (fdoExtension, FALSE, &partitionList); + + if ( !NT_SUCCESS (status) ) { + DiskReleasePartitioningLock (fdoExtension); + return status; + } + + // + // Update the partition device objects and set valid partition + // numbers. + // + + ASSERT(diskData->UpdatePartitionRoutine != NULL); + diskData->UpdatePartitionRoutine(DeviceObject, partitionList); + + + size = FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]) + + partitionList->PartitionCount * sizeof (PARTITION_INFORMATION_EX); + + + // + // If the output buffer is large enough, copy data to the output buffer, + // otherwise, fail. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >= + size ) { + + RtlCopyMemory (Irp->AssociatedIrp.SystemBuffer, + partitionList, + size + ); + + Irp->IoStatus.Information = size; + Irp->IoStatus.Status = status; + invalidateBusRelations = TRUE; + + } else { + + Irp->IoStatus.Information = size; + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + status = STATUS_BUFFER_TOO_SMALL; + invalidateBusRelations = FALSE; + } + + DiskReleasePartitioningLock(fdoExtension); + + if ( invalidateBusRelations ) { + ClassInvalidateBusRelations(DeviceObject); + } + + return status; +} + + +NTSTATUS +DiskIoctlSetDriveLayout( + IN OUT PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp + ) + +/*++ + +Routine Description: + + Handler for IOCTL_DISK_SET_DRIVE_LAYOUT ioctl. + + This ioctl has been replaced by IOCTL_DISK_SET_DRIVE_LAYOUT_EX. + +Arguments: + + DeviceObject - Device object for which partition table should be written. + + Irp - IRP involved. + +Return Values: + + NTSTATUS code. + +--*/ + +{ + NTSTATUS status; + PDRIVE_LAYOUT_INFORMATION partitionList; + PDRIVE_LAYOUT_INFORMATION_EX partitionListEx; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PPHYSICAL_DEVICE_EXTENSION pdoExtension; + PCOMMON_DEVICE_EXTENSION commonExtension; + PIO_STACK_LOCATION irpStack; + PDISK_DATA diskData; + BOOLEAN invalidateBusRelations; + SIZE_T listSize; + SIZE_T inputBufferLength; + SIZE_T outputBufferLength; + + PAGED_CODE (); + + ASSERT ( DeviceObject ); + ASSERT ( Irp ); + + // + // Initialization + // + + partitionListEx = NULL; + partitionList = NULL; + fdoExtension = DeviceObject->DeviceExtension; + pdoExtension = DeviceObject->DeviceExtension; + commonExtension = DeviceObject->DeviceExtension; + + irpStack = IoGetCurrentIrpStackLocation(Irp); + diskData = (PDISK_DATA)(commonExtension->DriverData); + partitionList = Irp->AssociatedIrp.SystemBuffer; + + inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength; + outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; + + // + // Update the partition table. + // + + if (inputBufferLength < sizeof (DRIVE_LAYOUT_INFORMATION)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + Irp->IoStatus.Information = sizeof (DRIVE_LAYOUT_INFORMATION); + return status; + } + + DiskAcquirePartitioningLock(fdoExtension); + + listSize = (partitionList->PartitionCount - 1); + listSize *= sizeof(PARTITION_INFORMATION); + listSize += sizeof(DRIVE_LAYOUT_INFORMATION); + + if (inputBufferLength < listSize) { + + // + // The remaning size of the input buffer not big enough to + // hold the additional partition entries + // + + status = STATUS_INFO_LENGTH_MISMATCH; + Irp->IoStatus.Information = listSize; + DiskReleasePartitioningLock(fdoExtension); + return status; + } + + // + // Convert the parititon information structure into an extended + // structure. + // + + partitionListEx = DiskConvertLayoutToExtended (partitionList); + + if ( partitionListEx == NULL ) { + + status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Status = status; + DiskReleasePartitioningLock(fdoExtension); + return status; + } + + // + // Redo all the partition numbers in the partition information + // + + ASSERT(diskData->UpdatePartitionRoutine != NULL); + diskData->UpdatePartitionRoutine(DeviceObject, partitionListEx); + + // + // Write changes to disk. + // + + status = DiskWritePartitionTableEx(fdoExtension, partitionListEx); + + // + // Update IRP with bytes returned. Make sure we don't claim to be + // returning more bytes than the caller is expecting to get back. + // + + if (NT_SUCCESS (status)) { + if (outputBufferLength < listSize) { + Irp->IoStatus.Information = outputBufferLength; + } else { + ULONG i; + + Irp->IoStatus.Information = listSize; + + // + // Also update the partition numbers. + // + + for (i = 0; i < partitionList->PartitionCount; i++) { + + PPARTITION_INFORMATION partition; + PPARTITION_INFORMATION_EX partitionEx; + + partition = &partitionList->PartitionEntry[i]; + partitionEx = &partitionListEx->PartitionEntry[i]; + partition->PartitionNumber = partitionEx->PartitionNumber; + + } + } + } + + ExFreePool (partitionListEx); + DiskReleasePartitioningLock(fdoExtension); + ClassInvalidateBusRelations(DeviceObject); + + Irp->IoStatus.Status = status; + return status; +} + + + +NTSTATUS +DiskIoctlSetDriveLayoutEx( + IN OUT PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp + ) + +/*++ + +Routine Description: + + Handler for IOCTL_DISK_SET_DRIVE_LAYOUT_EX ioctl. + + This ioctl replaces IOCTL_DISK_SET_DRIVE_LAYOUT. + +Arguments: + + DeviceObject - Device object for which partition table should be written. + + Irp - IRP involved. + +Return Values: + + NTSTATUS code. + +--*/ + +{ + + NTSTATUS status; + PDRIVE_LAYOUT_INFORMATION_EX partitionListEx; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PCOMMON_DEVICE_EXTENSION commonExtension; + + PIO_STACK_LOCATION irpStack; + PDISK_DATA diskData; + BOOLEAN invalidateBusRelations; + SIZE_T listSize; + SIZE_T inputBufferLength; + SIZE_T outputBufferLength; + + PAGED_CODE (); + + ASSERT ( DeviceObject ); + ASSERT ( Irp ); + + // + // Initialization + // + + partitionListEx = NULL; + fdoExtension = DeviceObject->DeviceExtension; + commonExtension = DeviceObject->DeviceExtension; + + irpStack = IoGetCurrentIrpStackLocation(Irp); + diskData = (PDISK_DATA)(commonExtension->DriverData); + partitionListEx = Irp->AssociatedIrp.SystemBuffer; + + inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength; + outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; + + // + // Update the partition table. + // + + if (inputBufferLength < + FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry)) { + status = STATUS_INFO_LENGTH_MISMATCH; + Irp->IoStatus.Information = + FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry); + return status; + } + + DiskAcquirePartitioningLock(fdoExtension); + + listSize = partitionListEx->PartitionCount; + listSize *= sizeof(PARTITION_INFORMATION_EX); + listSize += FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry); + + if (inputBufferLength < listSize) { + + // + // The remaning size of the input buffer not big enough to + // hold the additional partition entries + // + + status = STATUS_INFO_LENGTH_MISMATCH; + Irp->IoStatus.Information = listSize; + DiskReleasePartitioningLock(fdoExtension); + return status; + } + + + // + // If the partition count is zero, this is a request to clear + // the partition table. + // + + if (partitionListEx->PartitionCount == 0) { + + CREATE_DISK CreateDiskInfo; + + RtlZeroMemory (&CreateDiskInfo, sizeof (CreateDiskInfo)); + CreateDiskInfo.PartitionStyle = diskData->PartitionStyle; + if (diskData->PartitionStyle == PARTITION_STYLE_MBR) { + CreateDiskInfo.Mbr.Signature = partitionListEx->Mbr.Signature; + } else { + ASSERT (diskData->PartitionStyle == PARTITION_STYLE_GPT); + CreateDiskInfo.Gpt.DiskId = partitionListEx->Gpt.DiskId; + // + // NB: Setting MaxPartitionCount to zero will + // force the GPT partition table writing code + // to use the default minimum for this value. + // + CreateDiskInfo.Gpt.MaxPartitionCount = 0; + } + DiskInvalidatePartitionTable(fdoExtension, TRUE); + + + status = IoCreateDisk(DeviceObject, &CreateDiskInfo); + + } else { + + // + // Redo all the partition numbers in the partition information + // + + ASSERT(diskData->UpdatePartitionRoutine != NULL); + diskData->UpdatePartitionRoutine(DeviceObject, partitionListEx); + + // + // Write changes to disk. + // + + status = DiskWritePartitionTableEx(fdoExtension, partitionListEx); + } + + // + // Update IRP with bytes returned. Make sure we don't claim to be + // returning more bytes than the caller is expecting to get back. + // + + if (NT_SUCCESS(status)) { + if (outputBufferLength < listSize) { + Irp->IoStatus.Information = outputBufferLength; + } else { + Irp->IoStatus.Information = listSize; + } + } + + DiskReleasePartitioningLock(fdoExtension); + ClassInvalidateBusRelations(DeviceObject); + + Irp->IoStatus.Status = status; + return status; +} + + +NTSTATUS +DiskIoctlGetPartitionInfo( + IN OUT PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp + ) + +/*++ + +Routine Description: + + Handle the IOCTL_DISK_GET_PARTITION_INFO ioctl. Return the information + about the partition specified by the device object. Note that no + information is ever returned about the size or partition type of the + physical disk, as this doesn't make any sense. + + This ioctl has been replaced by IOCTL_DISK_GET_PARTITION_INFO_EX. + +Arguments: + + DeviceObject - + + Irp - + +Return Values: + + NTSTATUS code. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpStack; + PDISK_DATA diskData; + PPARTITION_INFORMATION partitionInfo; + PFUNCTIONAL_DEVICE_EXTENSION p0Extension; + PCOMMON_DEVICE_EXTENSION commonExtension; + PDISK_DATA partitionZeroData; + NTSTATUS oldReadyStatus; + + + PAGED_CODE (); + + ASSERT ( DeviceObject ); + ASSERT ( Irp ); + + + // + // Initialization + // + + commonExtension = DeviceObject->DeviceExtension; + irpStack = IoGetCurrentIrpStackLocation(Irp); + diskData = (PDISK_DATA)(commonExtension->DriverData); + p0Extension = commonExtension->PartitionZeroExtension; + partitionZeroData = ((PDISK_DATA) p0Extension->CommonExtension.DriverData); + + + // + // Check that the buffer is large enough. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(PARTITION_INFORMATION)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); + return status; + } + + // + // Update the geometry in case it has changed + // + + status = DiskReadDriveCapacity(p0Extension->DeviceObject); + + // + // Note whether the drive is ready. If the status has changed then + // notify pnp. + // + + oldReadyStatus = InterlockedExchange( + &(partitionZeroData->ReadyStatus), + status); + + if(partitionZeroData->ReadyStatus != oldReadyStatus) { + IoInvalidateDeviceRelations(p0Extension->LowerPdo, + BusRelations); + } + + if(!NT_SUCCESS(status)) { + return status; + } + + + // + // Partition zero, the partition representing the entire disk, is + // special cased. The logic below allows for sending this ioctl to + // a GPT disk only for partition zero. This allows us to obtain + // the size of a GPT disk using Win2k compatible IOCTLs. + // + + if (commonExtension->PartitionNumber == 0) { + + partitionInfo = (PPARTITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer; + + partitionInfo->PartitionType = PARTITION_ENTRY_UNUSED; + partitionInfo->StartingOffset = commonExtension->StartingOffset; + partitionInfo->PartitionLength = commonExtension->PartitionLength; + partitionInfo->HiddenSectors = 0; + partitionInfo->PartitionNumber = commonExtension->PartitionNumber; + partitionInfo->BootIndicator = FALSE; + partitionInfo->RewritePartition = FALSE; + partitionInfo->RecognizedPartition = FALSE; + + } else { + + // + // We do not support this IOCTL on an EFI partitioned disk + // for any partition other than partition zero. + // + + if (diskData->PartitionStyle != PARTITION_STYLE_MBR) { + status = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Status = status; + return status; + } + + DiskEnumerateDevice(p0Extension->DeviceObject); + DiskAcquirePartitioningLock(p0Extension); + + + partitionInfo = (PPARTITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer; + + partitionInfo->PartitionType = diskData->Mbr.PartitionType; + partitionInfo->StartingOffset = commonExtension->StartingOffset; + partitionInfo->PartitionLength = commonExtension->PartitionLength; + partitionInfo->HiddenSectors = diskData->Mbr.HiddenSectors; + partitionInfo->PartitionNumber = commonExtension->PartitionNumber; + partitionInfo->BootIndicator = diskData->Mbr.BootIndicator; + partitionInfo->RewritePartition = FALSE; + partitionInfo->RecognizedPartition = + IsRecognizedPartition(diskData->Mbr.PartitionType); + + DiskReleasePartitioningLock(p0Extension); + } + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); + + return status; +} + + +NTSTATUS +DiskIoctlGetPartitionInfoEx( + IN OUT PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp + ) +{ + NTSTATUS status; + PIO_STACK_LOCATION irpStack; + PDISK_DATA diskData; + PPARTITION_INFORMATION_EX partitionInfo; + PFUNCTIONAL_DEVICE_EXTENSION p0Extension; + PCOMMON_DEVICE_EXTENSION commonExtension; + PDISK_DATA partitionZeroData; + NTSTATUS oldReadyStatus; + + + PAGED_CODE (); + + ASSERT ( DeviceObject ); + ASSERT ( Irp ); + + + // + // Initialization + // + + commonExtension = DeviceObject->DeviceExtension; + irpStack = IoGetCurrentIrpStackLocation(Irp); + diskData = (PDISK_DATA)(commonExtension->DriverData); + p0Extension = commonExtension->PartitionZeroExtension; + partitionZeroData = ((PDISK_DATA) p0Extension->CommonExtension.DriverData); + + + // + // Check that the buffer is large enough. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(PARTITION_INFORMATION_EX)) { + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); + return status; + } + + // + // Update the geometry in case it has changed + // + + status = DiskReadDriveCapacity(p0Extension->DeviceObject); + + // + // Note whether the drive is ready. If the status has changed then + // notify pnp. + // + + oldReadyStatus = InterlockedExchange( + &(partitionZeroData->ReadyStatus), + status); + + if(partitionZeroData->ReadyStatus != oldReadyStatus) { + IoInvalidateDeviceRelations(p0Extension->LowerPdo, + BusRelations); + } + + if(!NT_SUCCESS(status)) { + return status; + } + + // + // If this is something other than partition 0 then do a + // re-enumeration to make sure we've got up-to-date information. + // + + if(commonExtension->PartitionNumber != 0) { + DiskEnumerateDevice(p0Extension->DeviceObject); + DiskAcquirePartitioningLock(p0Extension); + } + + partitionInfo = (PPARTITION_INFORMATION_EX) Irp->AssociatedIrp.SystemBuffer; + + partitionInfo->StartingOffset = commonExtension->StartingOffset; + partitionInfo->PartitionLength = commonExtension->PartitionLength; + partitionInfo->RewritePartition = FALSE; + partitionInfo->PartitionNumber = commonExtension->PartitionNumber; + partitionInfo->PartitionStyle = diskData->PartitionStyle; + + if ( diskData->PartitionStyle == PARTITION_STYLE_MBR ) { + + partitionInfo->Mbr.PartitionType = diskData->Mbr.PartitionType; + partitionInfo->Mbr.HiddenSectors = diskData->Mbr.HiddenSectors; + partitionInfo->Mbr.BootIndicator = diskData->Mbr.BootIndicator; + partitionInfo->Mbr.RecognizedPartition = + IsRecognizedPartition(diskData->Mbr.PartitionType); + + } else { + + // + // ISSUE - 2000/02/09 - math: Review for Partition0. + // Is this correct for Partition0? + // + + partitionInfo->Gpt.PartitionType = diskData->Efi.PartitionType; + partitionInfo->Gpt.PartitionId = diskData->Efi.PartitionId; + partitionInfo->Gpt.Attributes = diskData->Efi.Attributes; + RtlCopyMemory ( + partitionInfo->Gpt.Name, + diskData->Efi.PartitionName, + sizeof (partitionInfo->Gpt.Name) + ); + } + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX); + + if(commonExtension->PartitionNumber != 0) { + DiskReleasePartitioningLock(p0Extension); + } + + return status; +} + + + +NTSTATUS +DiskIoctlGetLengthInfo( + IN OUT PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp + ) +{ + NTSTATUS status; + PIO_STACK_LOCATION irpStack; + PDISK_DATA diskData; + PGET_LENGTH_INFORMATION lengthInfo; + PFUNCTIONAL_DEVICE_EXTENSION p0Extension; + PCOMMON_DEVICE_EXTENSION commonExtension; + PDISK_DATA partitionZeroData; + NTSTATUS oldReadyStatus; + + + PAGED_CODE (); + + ASSERT ( DeviceObject ); + ASSERT ( Irp ); + + + // + // Initialization + // + + commonExtension = DeviceObject->DeviceExtension; + irpStack = IoGetCurrentIrpStackLocation(Irp); + diskData = (PDISK_DATA)(commonExtension->DriverData); + p0Extension = commonExtension->PartitionZeroExtension; + partitionZeroData = ((PDISK_DATA) p0Extension->CommonExtension.DriverData); + + + // + // Check that the buffer is large enough. + // + + if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(GET_LENGTH_INFORMATION)) { + + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Update the geometry in case it has changed + // + + status = DiskReadDriveCapacity(p0Extension->DeviceObject); + + // + // Note whether the drive is ready. If the status has changed then + // notify pnp. + // + + oldReadyStatus = InterlockedExchange( + &(partitionZeroData->ReadyStatus), + status); + + if(partitionZeroData->ReadyStatus != oldReadyStatus) { + IoInvalidateDeviceRelations(p0Extension->LowerPdo, + BusRelations); + } + + if(!NT_SUCCESS(status)) { + return status; + } + + // + // If this is something other than partition 0 then do a + // re-enumeration to make sure we've got up-to-date information. + // + + if(commonExtension->PartitionNumber != 0) { + DiskEnumerateDevice(p0Extension->DeviceObject); + DiskAcquirePartitioningLock(p0Extension); + } + + lengthInfo = (PGET_LENGTH_INFORMATION) Irp->AssociatedIrp.SystemBuffer; + + lengthInfo->Length = commonExtension->PartitionLength; + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); + + if(commonExtension->PartitionNumber != 0) { + DiskReleasePartitioningLock(p0Extension); + } + + return status; +} + + + +NTSTATUS +DiskIoctlSetPartitionInfo( + IN OUT PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp + ) +{ + NTSTATUS status; + PSET_PARTITION_INFORMATION inputBuffer; + PDISK_DATA diskData; + PIO_STACK_LOCATION irpStack; + PCOMMON_DEVICE_EXTENSION commonExtension; + + + PAGED_CODE (); + + ASSERT ( DeviceObject != NULL ); + ASSERT ( Irp != NULL ); + + + // + // Initialization + // + + commonExtension = DeviceObject->DeviceExtension; + irpStack = IoGetCurrentIrpStackLocation(Irp); + diskData = (PDISK_DATA)(commonExtension->DriverData); + inputBuffer = (PSET_PARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + if(commonExtension->IsFdo) { + + return STATUS_UNSUCCESSFUL; + } + + + if (diskData->PartitionStyle != PARTITION_STYLE_MBR) { + return STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Validate buffer length + // + + if(irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(SET_PARTITION_INFORMATION)) { + + return STATUS_INFO_LENGTH_MISMATCH; + } + + DiskAcquirePartitioningLock(commonExtension->PartitionZeroExtension); + + // + // The HAL routines IoGet- and IoSetPartitionInformation were + // developed before support of dynamic partitioning and therefore + // don't distinguish between partition ordinal (that is the order + // of a paritition on a disk) and the partition number. (The + // partition number is assigned to a partition to identify it to + // the system.) Use partition ordinals for these legacy calls. + // + + status = DiskSetPartitionInformation( + commonExtension->PartitionZeroExtension, + commonExtension->PartitionZeroExtension->DiskGeometry.BytesPerSector, + diskData->PartitionOrdinal, + inputBuffer->PartitionType); + + if(NT_SUCCESS(status)) { + + diskData->Mbr.PartitionType = inputBuffer->PartitionType; + } + + DiskReleasePartitioningLock(commonExtension->PartitionZeroExtension); + + return status; +} + + +NTSTATUS +DiskIoctlSetPartitionInfoEx( + IN OUT PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp + ) +{ + NTSTATUS status; + PSET_PARTITION_INFORMATION_EX inputBuffer; + PDISK_DATA diskData; + PIO_STACK_LOCATION irpStack; + PCOMMON_DEVICE_EXTENSION commonExtension; + + + PAGED_CODE (); + + ASSERT ( DeviceObject != NULL ); + ASSERT ( Irp != NULL ); + + + // + // Initialization + // + + commonExtension = DeviceObject->DeviceExtension; + irpStack = IoGetCurrentIrpStackLocation(Irp); + diskData = (PDISK_DATA)(commonExtension->DriverData); + inputBuffer = (PSET_PARTITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer; + + if(commonExtension->IsFdo) { + + return STATUS_UNSUCCESSFUL; + } + + // + // Validate buffer length + // + + if(irpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(SET_PARTITION_INFORMATION_EX)) { + + return STATUS_INFO_LENGTH_MISMATCH; + } + + DiskAcquirePartitioningLock(commonExtension->PartitionZeroExtension); + + // + // The HAL routines IoGet- and IoSetPartitionInformation were + // developed before support of dynamic partitioning and therefore + // don't distinguish between partition ordinal (that is the order + // of a paritition on a disk) and the partition number. (The + // partition number is assigned to a partition to identify it to + // the system.) Use partition ordinals for these legacy calls. + // + + status = DiskSetPartitionInformationEx( + commonExtension->PartitionZeroExtension, + diskData->PartitionOrdinal, + inputBuffer + ); + + if(NT_SUCCESS(status)) { + + if (diskData->PartitionStyle == PARTITION_STYLE_MBR) { + + diskData->Mbr.PartitionType = inputBuffer->Mbr.PartitionType; + + } else { + + ASSERT ( diskData->PartitionStyle == PARTITION_STYLE_MBR ); + + diskData->Efi.PartitionType = inputBuffer->Gpt.PartitionType; + diskData->Efi.PartitionId = inputBuffer->Gpt.PartitionId; + diskData->Efi.Attributes = inputBuffer->Gpt.Attributes; + + RtlCopyMemory ( + diskData->Efi.PartitionName, + inputBuffer->Gpt.Name, + sizeof (diskData->Efi.PartitionName) + ); + } + } + + DiskReleasePartitioningLock(commonExtension->PartitionZeroExtension); + + return status; +} + +typedef struct _DISK_GEOMETRY_EX_INTERNAL { + DISK_GEOMETRY Geometry; + LARGE_INTEGER DiskSize; + DISK_PARTITION_INFO Partition; + DISK_DETECTION_INFO Detection; +} DISK_GEOMETRY_EX_INTERNAL, *PDISK_GEOMETRY_EX_INTERNAL; + + + + +NTSTATUS +DiskIoctlGetDriveGeometryEx( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp + ) + +/*++ + +Routine Description: + + Obtain the extended geometry information for the drive. + +Arguments: + + DeviceObject - The device object to obtain the geometry for. + + Irp - IRP with a return buffer large enough to receive the + extended geometry information. + +Return Value: + + NTSTATUS code + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpStack; + PCOMMON_DEVICE_EXTENSION commonExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PDISK_DATA diskData; + PDISK_GEOMETRY_EX_INTERNAL geometryEx; + ULONG OutputBufferLength; + + // + // Verification + // + + PAGED_CODE (); + + ASSERT ( DeviceObject != NULL ); + ASSERT ( Irp != NULL ); + + // + // Setup parameters + // + + commonExtension = DeviceObject->DeviceExtension; + fdoExtension = DeviceObject->DeviceExtension; + diskData = (PDISK_DATA)(commonExtension->DriverData); + irpStack = IoGetCurrentIrpStackLocation ( Irp ); + geometryEx = NULL; + OutputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; + + // + // This is only valid for the FDO. + // + + ASSERT ( commonExtension->IsFdo ); + + // + // Check that the buffer is large enough. It must be large enough + // to hold at lest the Geometry and DiskSize fields of of the + // DISK_GEOMETRY_EX structure. + // + + if ( OutputBufferLength < FIELD_OFFSET (DISK_GEOMETRY_EX, Data) ) { + + // + // Buffer too small. Bail out, telling the caller the required + // size. + // + + status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Status = FIELD_OFFSET (DISK_GEOMETRY_EX, Data); + return status; + } + + if (TEST_FLAG (DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) { + + // + // Issue a ReadCapacity to update device extension + // with information for the current media. + // + + status = DiskReadDriveCapacity ( + commonExtension->PartitionZeroExtension->DeviceObject); + + diskData->ReadyStatus = status; + + if (!NT_SUCCESS (status)) { + return status; + } + } + + // + // Copy drive geometry. + // + + geometryEx = (PDISK_GEOMETRY_EX_INTERNAL)Irp->AssociatedIrp.SystemBuffer; + geometryEx->Geometry = fdoExtension->DiskGeometry; + geometryEx->DiskSize = commonExtension->PartitionZeroExtension->CommonExtension.PartitionLength; + + // + // If the user buffer is large enough to hold the partition information + // then add that as well. + // + + if (OutputBufferLength >= FIELD_OFFSET (DISK_GEOMETRY_EX_INTERNAL, Detection)) { + + geometryEx->Partition.SizeOfPartitionInfo = sizeof (geometryEx->Partition); + geometryEx->Partition.PartitionStyle = diskData->PartitionStyle; + + switch ( diskData->PartitionStyle ) { + + case PARTITION_STYLE_GPT: + + // + // Copy GPT signature. + // + + geometryEx->Partition.Gpt.DiskId = diskData->Efi.DiskId; + break; + + case PARTITION_STYLE_MBR: + + // + // Copy MBR signature and checksum. + // + + geometryEx->Partition.Mbr.Signature = diskData->Mbr.Signature; + geometryEx->Partition.Mbr.CheckSum = diskData->Mbr.MbrCheckSum; + break; + + default: + + // + // This is a raw disk. Zero out the signature area so + // nobody gets confused. + // + + RtlZeroMemory ( + &geometryEx->Partition, + sizeof (geometryEx->Partition)); + } + } + + // + // If the buffer is large enough to hold the detection information, + // then also add that. + // + + if (OutputBufferLength >= sizeof (DISK_GEOMETRY_EX_INTERNAL)) { + + geometryEx->Detection.SizeOfDetectInfo = + sizeof (geometryEx->Detection); + + status = DiskGetDetectInfo ( + fdoExtension, + &geometryEx->Detection); + + // + // Failed to obtain detection information, set to none. + // + + if (!NT_SUCCESS (status)) { + geometryEx->Detection.DetectionType = DetectNone; + } + } + + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = min (OutputBufferLength, + sizeof (DISK_GEOMETRY_EX_INTERNAL)); + + return status; +} + diff --git a/reactos/drivers/storage/class/disk_new/disk.h b/reactos/drivers/storage/class/disk_new/disk.h new file mode 100644 index 00000000000..2f2c7eb81d4 --- /dev/null +++ b/reactos/drivers/storage/class/disk_new/disk.h @@ -0,0 +1,910 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + disk.c + +Abstract: + + SCSI disk class driver + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#define NTDDI_VERSION NTDDI_WINXP + +#include "ntddk.h" +#include "scsi.h" +#include +#include "classpnp.h" +#if defined(JAPAN) && defined(_X86_) +#include "machine.h" +#endif + +#include + +#if defined(_X86_) +#include "mountdev.h" +#endif + +#ifdef ExAllocatePool +#undef ExAllocatePool +#define ExAllocatePool #assert(FALSE) +#endif + +#define DISK_TAG_GENERAL ' DcS' // "ScD " - generic tag +#define DISK_TAG_SMART 'aDcS' // "ScDa" - SMART allocations +#define DISK_TAG_INFO_EXCEPTION 'ADcS' // "ScDA" - Info Exceptions +#define DISK_TAG_DISABLE_CACHE 'CDcS' // "ScDC" - disable cache paths +#define DISK_TAG_CCONTEXT 'cDcS' // "ScDc" - disk allocated completion context +#define DISK_TAG_DISK_GEOM 'GDcS' // "ScDG" - disk geometry buffer +#define DISK_TAG_UPDATE_GEOM 'gDcS' // "ScDg" - update disk geometry paths +#define DISK_TAG_SENSE_INFO 'IDcS' // "ScDI" - sense info buffers +#define DISK_TAG_PNP_ID 'iDcS' // "ScDp" - pnp ids +#define DISK_TAG_MODE_DATA 'MDcS' // "ScDM" - mode data buffer +#define DISK_CACHE_MBR_CHECK 'mDcS' // "ScDM" - mbr checksum code +#define DISK_TAG_NAME 'NDcS' // "ScDN" - disk name code +#define DISK_TAG_READ_CAP 'PDcS' // "ScDP" - read capacity buffer +#define DISK_TAG_PART_LIST 'pDcS' // "ScDp" - disk partition lists +#define DISK_TAG_SRB 'SDcS' // "ScDS" - srb allocation +#define DISK_TAG_START 'sDcS' // "ScDs" - start device paths +#define DISK_TAG_UPDATE_CAP 'UDcS' // "ScDU" - update capacity path +#define DISK_TAG_WI_CONTEXT 'WDcS' // "ScDW" - work-item context + +typedef +VOID +(*PDISK_UPDATE_PARTITIONS) ( + IN PDEVICE_OBJECT Fdo, + IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList + ); + +#if defined(_X86_) + +// +// Disk device data +// + +typedef enum _DISK_GEOMETRY_SOURCE { + DiskGeometryUnknown, + DiskGeometryFromBios, + DiskGeometryFromPort, + DiskGeometryFromNec98, + DiskGeometryGuessedFromBios, + DiskGeometryFromDefault +} DISK_GEOMETRY_SOURCE, *PDISK_GEOMETRY_SOURCE; +#endif + +// + +typedef struct _DISK_DATA { + + // + // This field is the ordinal of a partition as it appears on a disk. + // + + ULONG PartitionOrdinal; + + // + // How has this disk been partitioned? Either EFI or MBR. + // + + PARTITION_STYLE PartitionStyle; + + union { + + struct { + + // + // Disk signature (from MBR) + // + + ULONG Signature; + + // + // MBR checksum + // + + ULONG MbrCheckSum; + + // + // Number of hidden sectors for BPB. + // + + ULONG HiddenSectors; + + // + // Partition type of this device object + // + // This field is set by: + // + // 1. Initially set according to the partition list entry + // partition type returned by IoReadPartitionTable. + // + // 2. Subsequently set by the + // IOCTL_DISK_SET_PARTITION_INFORMATION I/O control + // function when IoSetPartitionInformation function + // successfully updates the partition type on the disk. + // + + UCHAR PartitionType; + + // + // Boot indicator - indicates whether this partition is a + // bootable (active) partition for this device + // + // This field is set according to the partition list entry boot + // indicator returned by IoReadPartitionTable. + // + + BOOLEAN BootIndicator; + + } Mbr; + + struct { + + // + // The DiskGUID field from the EFI partition header. + // + + GUID DiskId; + + // + // Partition type of this device object. + // + + GUID PartitionType; + + // + // Unique partition identifier for this partition. + // + + GUID PartitionId; + + // + // EFI partition attributes for this partition. + // + + ULONG64 Attributes; + + // + // EFI partition name of this partition. + // + + WCHAR PartitionName[36]; + + } Efi; + + }; // unnamed union + + struct { + // + // This flag is set when the well known name is created (through + // DiskCreateSymbolicLinks) and cleared when destroying it + // (by calling DiskDeleteSymbolicLinks). + // + + BOOLEAN WellKnownNameCreated : 1; + + // + // This flag is set when the PhysicalDriveN link is created (through + // DiskCreateSymbolicLinks) and is cleared when destroying it (through + // DiskDeleteSymbolicLinks) + // + + BOOLEAN PhysicalDriveLinkCreated : 1; + + } LinkStatus; + + // + // ReadyStatus - STATUS_SUCCESS indicates that the drive is ready for + // use. Any error status is to be returned as an explaination for why + // a request is failed. + // + // This was done solely for the zero-length partition case of having no + // media in a removable disk drive. When that occurs, and a read is sent + // to the zero-length non-partition-zero PDO that was created, we had to + // be able to fail the request with a reasonable value. This may not have + // been the best way to do this, but it works. + // + + NTSTATUS ReadyStatus; + + // + // Routine to be called when updating the disk partitions. This routine + // is different for removable and non-removable media and is called by + // (among other things) DiskEnumerateDevice + // + + PDISK_UPDATE_PARTITIONS UpdatePartitionRoutine; + + // + // SCSI address used for SMART operations. + // + + SCSI_ADDRESS ScsiAddress; + + // + // Event used to synchronize partitioning operations and enumerations. + // + + KEVENT PartitioningEvent; + + // + // These unicode strings hold the disk and volume interface strings. If + // the interfaces were not registered or could not be set then the string + // buffer will be NULL. + // + + UNICODE_STRING DiskInterfaceString; + UNICODE_STRING PartitionInterfaceString; + + // + // What type of failure prediction mechanism is available + // + + FAILURE_PREDICTION_METHOD FailurePredictionCapability; + BOOLEAN AllowFPPerfHit; + +#if defined(_X86_) + // + // This flag indiciates that a non-default geometry for this drive has + // already been determined by the disk driver. This field is ignored + // for removable media drives. + // + + DISK_GEOMETRY_SOURCE GeometrySource; + + // + // If GeometryDetermined is TRUE this will contain the geometry which was + // reported by the firmware or by the BIOS. For removable media drives + // this will contain the last geometry used when media was present. + // + + DISK_GEOMETRY RealGeometry; +#endif + + // + // Indicates that the cached partition table is valid when set. + // + + ULONG CachedPartitionTableValid; + + // + // The cached partition table - this is only valid if the previous + // flag is set. When invalidated the cached partition table will be + // freed and replaced the next time one of the partitioning functions is + // called. This allows the error handling routines to invalidate it by + // setting the flag and doesn't require that they obtain a lock. + // + + PDRIVE_LAYOUT_INFORMATION_EX CachedPartitionTable; + + // + // This mutex prevents more than one IOCTL_DISK_VERIFY from being + // sent down to the disk. This greatly reduces the possibility of + // a Denial-of-Service attack + // + + KMUTEX VerifyMutex; + +} DISK_DATA, *PDISK_DATA; + +// Define a general structure of identfing disk controllers with bad +// hardware. +// + +#define HackDisableTaggedQueuing (0x01) +#define HackDisableSynchronousTransfers (0x02) +#define HackDisableSpinDown (0x04) +#define HackDisableWriteCache (0x08) +#define HackCauseNotReportableHack (0x10) +#define HackRequiresStartUnitCommand (0x20) +#define HackDisableWriteCacheNotSupported (0x40) + + +#define DiskDeviceParameterSubkey L"Disk" +#define DiskDeviceSpecialFlags L"SpecialFlags" +#define DiskDeviceUserWriteCacheSetting L"UserWriteCacheSetting" + + +#define FUNCTIONAL_EXTENSION_SIZE sizeof(FUNCTIONAL_DEVICE_EXTENSION) + sizeof(DISK_DATA) +#define PHYSICAL_EXTENSION_SIZE sizeof(PHYSICAL_DEVICE_EXTENSION) + sizeof(DISK_DATA) + +#define MODE_DATA_SIZE 192 +#define VALUE_BUFFER_SIZE 2048 +#define SCSI_DISK_TIMEOUT 10 +#define PARTITION0_LIST_SIZE 4 + +#define MAX_MEDIA_TYPES 4 +typedef struct _DISK_MEDIA_TYPES_LIST { + PCHAR VendorId; + PCHAR ProductId; + PCHAR Revision; + const ULONG NumberOfTypes; + const ULONG NumberOfSides; + const STORAGE_MEDIA_TYPE MediaTypes[MAX_MEDIA_TYPES]; +} DISK_MEDIA_TYPES_LIST, *PDISK_MEDIA_TYPES_LIST; + +// +// WMI reregistration structures used for reregister work item +// +typedef struct +{ + SINGLE_LIST_ENTRY Next; + PDEVICE_OBJECT DeviceObject; + PIRP Irp; +} DISKREREGREQUEST, *PDISKREREGREQUEST; + +// +// Write cache setting as defined by the user +// +typedef enum _DISK_USER_WRITE_CACHE_SETTING +{ + DiskWriteCacheDisable = 0, + DiskWriteCacheEnable = 1, + DiskWriteCacheDefault = -1 + +} DISK_USER_WRITE_CACHE_SETTING, *PDISK_USER_WRITE_CACHE_SETTING; + +#define MAX_SECTORS_PER_VERIFY 0x200 + +// +// This is based off 100ns units +// +#define ONE_MILLI_SECOND ((ULONGLONG)10 * 1000) + +// +// Context for the work-item +// +typedef struct _DISK_VERIFY_WORKITEM_CONTEXT +{ + PIRP Irp; + PSCSI_REQUEST_BLOCK Srb; + PIO_WORKITEM WorkItem; + +} DISK_VERIFY_WORKITEM_CONTEXT, *PDISK_VERIFY_WORKITEM_CONTEXT; + +// +// Poll for Failure Prediction every hour +// +#define DISK_DEFAULT_FAILURE_POLLING_PERIOD 1 * 60 * 60 + +// +// Static global lookup tables. +// + +extern CLASSPNP_SCAN_FOR_SPECIAL_INFO DiskBadControllers[]; +extern const DISK_MEDIA_TYPES_LIST DiskMediaTypes[]; + +// +// Macros +// + +// +// Routine prototypes. +// + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +DiskUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +DiskAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT Pdo + ); + +NTSTATUS +DiskInitFdo( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +DiskInitPdo( + IN PDEVICE_OBJECT Pdo + ); + +NTSTATUS +DiskStartFdo( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +DiskStartPdo( + IN PDEVICE_OBJECT Pdo + ); + +NTSTATUS +DiskStopDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ); + +NTSTATUS +DiskRemoveDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ); + +NTSTATUS +DiskReadWriteVerification( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +DiskFdoProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ); + +NTSTATUS +DiskShutdownFlush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +DiskGetCacheInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PDISK_CACHE_INFORMATION CacheInfo + ); + +NTSTATUS +DiskSetCacheInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PDISK_CACHE_INFORMATION CacheInfo + ); + +VOID +DisableWriteCache( + IN PDEVICE_OBJECT DeviceObject, + IN PIO_WORKITEM WorkItem + ); + +VOID +DiskIoctlVerify( + IN PDEVICE_OBJECT DeviceObject, + IN PDISK_VERIFY_WORKITEM_CONTEXT Context + ); + +NTSTATUS +DiskModeSelect( + IN PDEVICE_OBJECT DeviceObject, + IN PCHAR ModeSelectBuffer, + IN ULONG Length, + IN BOOLEAN SavePage + ); + +// +// We need to validate that the self test subcommand is valid and +// appropriate. Right now we allow subcommands 0, 1 and 2 which are non +// captive mode tests. Once we figure out a way to know if it is safe to +// run a captive test then we can allow captive mode tests. Also if the +// atapi 5 spec is ever updated to denote that bit 7 is the captive +// mode bit, we can allow any request that does not have bit 7 set. Until +// that is done we want to be sure +// +#define DiskIsValidSmartSelfTest(Subcommand) \ + ( ((Subcommand) == SMART_OFFLINE_ROUTINE_OFFLINE) || \ + ((Subcommand) == SMART_SHORT_SELFTEST_OFFLINE) || \ + ((Subcommand) == SMART_EXTENDED_SELFTEST_OFFLINE) ) + + +NTSTATUS +DiskPerformSmartCommand( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN ULONG SrbControlCode, + IN UCHAR Command, + IN UCHAR Feature, + IN UCHAR SectorCount, + IN UCHAR SectorNumber, + IN OUT PSRB_IO_CONTROL SrbControl, + OUT PULONG BufferSize + ); + +NTSTATUS +DiskGetInfoExceptionInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PMODE_INFO_EXCEPTIONS ReturnPageData + ); + +NTSTATUS +DiskSetInfoExceptionInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PMODE_INFO_EXCEPTIONS PageData + ); + +NTSTATUS +DiskDetectFailurePrediction( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PFAILURE_PREDICTION_METHOD FailurePredictCapability + ); + +BOOLEAN +EnumerateBusKey( + IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension, + HANDLE BusKey, + PULONG DiskNumber + ); + +NTSTATUS +DiskCreateFdo( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT LowerDeviceObject, + IN PULONG DeviceCount, + IN BOOLEAN DasdAccessOnly + ); + +VOID +UpdateDeviceObjects( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +DiskSetSpecialHacks( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN ULONG_PTR Data + ); + +VOID +DiskScanRegistryForSpecial( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +VOID +ResetBus( + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +DiskEnumerateDevice( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +DiskQueryId( + IN PDEVICE_OBJECT Pdo, + IN BUS_QUERY_ID_TYPE IdType, + IN PUNICODE_STRING UnicodeIdString + ); + +NTSTATUS +DiskQueryPnpCapabilities( + IN PDEVICE_OBJECT DeviceObject, + IN PDEVICE_CAPABILITIES Capabilities + ); + +NTSTATUS +DiskGenerateDeviceName( + IN BOOLEAN IsFdo, + IN ULONG DeviceNumber, + IN OPTIONAL ULONG PartitionNumber, + IN OPTIONAL PLARGE_INTEGER StartingOffset, + IN OPTIONAL PLARGE_INTEGER PartitionLength, + OUT PUCHAR *RawName + ); + +VOID +DiskCreateSymbolicLinks( + IN PDEVICE_OBJECT DeviceObject + ); + +VOID +DiskUpdatePartitions( + IN PDEVICE_OBJECT Fdo, + IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList + ); + +VOID +DiskUpdateRemovablePartitions( + IN PDEVICE_OBJECT Fdo, + IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList + ); + +NTSTATUS +DiskCreatePdo( + IN PDEVICE_OBJECT Fdo, + IN ULONG PartitionOrdinal, + IN PPARTITION_INFORMATION_EX PartitionEntry, + IN PARTITION_STYLE PartitionStyle, + OUT PDEVICE_OBJECT *Pdo + ); + +VOID +DiskDeleteSymbolicLinks( + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +DiskPdoQueryWmiRegInfo( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName + ); + +NTSTATUS +DiskPdoQueryWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferAvail, + OUT PUCHAR Buffer + ); + +NTSTATUS +DiskPdoSetWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +DiskPdoSetWmiDataItem( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG DataItemId, + IN ULONG BufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +DiskPdoExecuteWmiMethod( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG MethodId, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +DiskFdoQueryWmiRegInfo( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName + ); + +NTSTATUS +DiskFdoQueryWmiRegInfoEx( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName, + OUT PUNICODE_STRING MofName + ); + +NTSTATUS +DiskFdoQueryWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferAvail, + OUT PUCHAR Buffer + ); + +NTSTATUS +DiskFdoSetWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +DiskFdoSetWmiDataItem( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG DataItemId, + IN ULONG BufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +DiskFdoExecuteWmiMethod( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG MethodId, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +DiskWmiFunctionControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN CLASSENABLEDISABLEFUNCTION Function, + IN BOOLEAN Enable + ); + +NTSTATUS +DiskReadFailurePredictStatus( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_STATUS DiskSmartStatus + ); + +NTSTATUS +DiskReadFailurePredictData( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_DATA DiskSmartData + ); + +NTSTATUS +DiskEnableDisableFailurePrediction( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + BOOLEAN Enable + ); + +NTSTATUS +DiskEnableDisableFailurePredictPolling( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + BOOLEAN Enable, + ULONG PollTimeInSeconds + ); + +VOID +DiskAcquirePartitioningLock( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +VOID +DiskReleasePartitioningLock( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +NTSTATUS DiskInitializeReregistration( + void + ); + +extern GUIDREGINFO DiskWmiFdoGuidList[]; +extern GUIDREGINFO DiskWmiPdoGuidList[]; + +#if defined(_X86_) +NTSTATUS +DiskReadDriveCapacity( + IN PDEVICE_OBJECT Fdo + ); +#else +#define DiskReadDriveCapacity(Fdo) ClassReadDriveCapacity(Fdo) +#endif + + +#if defined(_X86_) + +#if 0 +NTSTATUS +DiskQuerySuggestedLinkName( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); +#endif + +NTSTATUS +DiskSaveDetectInfo( + PDRIVER_OBJECT DriverObject + ); + +VOID +DiskCleanupDetectInfo( + IN PDRIVER_OBJECT DriverObject + ); + +VOID +DiskDriverReinitialization ( + IN PDRIVER_OBJECT DriverObject, + IN PVOID Nothing, + IN ULONG Count + ); + +#endif + +VOID +DiskConvertPartitionToExtended( + IN PPARTITION_INFORMATION Partition, + OUT PPARTITION_INFORMATION_EX PartitionEx + ); + +PDRIVE_LAYOUT_INFORMATION_EX +DiskConvertLayoutToExtended( + IN CONST PDRIVE_LAYOUT_INFORMATION Layout + ); + +PDRIVE_LAYOUT_INFORMATION +DiskConvertExtendedToLayout( + IN CONST PDRIVE_LAYOUT_INFORMATION_EX LayoutEx + ); + +NTSTATUS +DiskReadPartitionTableEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN BypassCache, + OUT PDRIVE_LAYOUT_INFORMATION_EX* DriveLayout + ); + +NTSTATUS +DiskWritePartitionTableEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout + ); + +NTSTATUS +DiskSetPartitionInformationEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN ULONG PartitionNumber, + IN struct _SET_PARTITION_INFORMATION_EX* PartitionInfo + ); + +NTSTATUS +DiskSetPartitionInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN ULONG SectorSize, + IN ULONG PartitionNumber, + IN ULONG PartitionType + ); + +NTSTATUS +DiskVerifyPartitionTable( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN FixErrors + ); + +BOOLEAN +DiskInvalidatePartitionTable( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN PartitionLockHeld + ); + +#if defined (_X86_) +NTSTATUS +DiskGetDetectInfo( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PDISK_DETECTION_INFO DetectInfo + ); + +NTSTATUS +DiskReadSignature( + IN PDEVICE_OBJECT Fdo + ); + +#else +#define DiskGetDetectInfo(FdoExtension, DetectInfo) (STATUS_UNSUCCESSFUL) +#endif + + +#define DiskHashGuid(Guid) (((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0]) + diff --git a/reactos/drivers/storage/class/disk_new/disk.rc b/reactos/drivers/storage/class/disk_new/disk.rc new file mode 100644 index 00000000000..14825451b14 --- /dev/null +++ b/reactos/drivers/storage/class/disk_new/disk.rc @@ -0,0 +1,23 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (C) Microsoft Corporation, 1996 - 1999 +// +// File: disk.rc +// +//-------------------------------------------------------------------------- + +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "PnP Disk Driver" +#define VER_INTERNALNAME_STR "scsidisk.sys" +#define VER_ORIGINALFILENAME_STR "scsidisk.sys" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/reactos/drivers/storage/class/disk_new/diskdev.inf b/reactos/drivers/storage/class/disk_new/diskdev.inf new file mode 100644 index 00000000000..d14de6688bf --- /dev/null +++ b/reactos/drivers/storage/class/disk_new/diskdev.inf @@ -0,0 +1,96 @@ +; disk.inf +; +; Installation inf for the Disk drive adapter. +; +; SAMPLE INF File for Class Driver +; FOR DDK - Driver Development Kit +; +; (c) Copyright 1999 Microsoft Corp. +; + +[Version] +Signature="$Windows NT$" +Provider=%MS% +ClassGUID={4d36e967-e325-11ce-bfc1-08002be10318} +Class=DiskDrive +; CatalogFile=disk.cat ; Supply your own catalog file + ; see DDK Doc. +DriverVer=08/27/1999 + +[DestinationDirs] +DefaultDestDir = 12 + +; +; Driver information +; + +[Manufacturer] +%MS% = MS.Mfg + +[MS.Mfg] +%MS.DeviceDesc0% = disk, GenDisk +%MS.DeviceDesc1% = disk, GenOptical + + +; +; General installation section +; + +[disk] + +; +; File sections +; + + +; +; Service Installation +; + +[disk.Services] +AddService = disk, 0x00000002 , disk_Service_Inst + +[disk_Service_Inst] +DisplayName = %disk.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 1 ; SERVICE_SYSTEM_START +ErrorControl = 0x1 ; SERVICE_ERROR_NORMAL +LoadOrderGroup = Pointer Port +ServiceBinary = %12%\disk.sys + + +; +; Source file information +; + +[SourceDisksNames.x86] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +; Files for disk Microsoft Corp. Installation Disk #1 (DiskDrive) +disk.sys = 1,, + + +[Strings] + +; +; Non-Localizable Strings +; + +REG_SZ = 0x00000000 +REG_MULTI_SZ = 0x00010000 +REG_EXPAND_SZ = 0x00020000 +REG_BINARY = 0x00000001 +REG_DWORD = 0x00010001 +SERVICEROOT = "System\CurrentControlSet\Services" + +; +; Localizable Strings +; + +MS.DeviceDesc0 = "Disk drive" +MS.DeviceDesc1 = "Optical disk drive" +DiskId1 = "Microsoft Corp. Installation Disk #1 (DiskDrive)" +MS = "Microsoft Corp." +disk.SvcDesc="Disk Drive" + diff --git a/reactos/drivers/storage/class/disk_new/diskwmi.c b/reactos/drivers/storage/class/disk_new/diskwmi.c new file mode 100644 index 00000000000..1ce6dd5aa97 --- /dev/null +++ b/reactos/drivers/storage/class/disk_new/diskwmi.c @@ -0,0 +1,3434 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + diskwmi.c + +Abstract: + + SCSI disk class driver - WMI support routines + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "disk.h" + +NTSTATUS +DiskSendFailurePredictIoctl( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_PREDICT_FAILURE checkFailure + ); + +NTSTATUS +DiskGetIdentifyInfo( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PBOOLEAN SupportSmart + ); + +NTSTATUS +DiskDetectFailurePrediction( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PFAILURE_PREDICTION_METHOD FailurePredictCapability + ); + +NTSTATUS +DiskReadFailurePredictThresholds( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_THRESHOLDS DiskSmartThresholds + ); + +NTSTATUS +DiskReadSmartLog( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN UCHAR SectorCount, + IN UCHAR LogAddress, + OUT PUCHAR Buffer + ); + +NTSTATUS +DiskWriteSmartLog( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN UCHAR SectorCount, + IN UCHAR LogAddress, + IN PUCHAR Buffer + ); + +void DiskReregWorker( + IN PVOID Context + ); + +// +// WMI reregistration globals +// +// Since it will take too long to do a mode sense on some drive, we +// need a good way to effect the mode sense for the info exceptions +// mode page so that we can determine if SMART is supported and enabled +// for the drive. So the strategy is to do an asynchronous mode sense +// when the device starts and then look at the info exceptions mode +// page within the completion routine. Now within the completion +// routine we cannot call IoWMIRegistrationControl since we are at DPC +// level, so we create a stack of device objects that will be processed +// by a single work item that is fired off only when the stack +// transitions from empty to non empty. +// +WORK_QUEUE_ITEM DiskReregWorkItem; +SINGLE_LIST_ENTRY DiskReregHead; +KSPIN_LOCK DiskReregSpinlock; +LONG DiskReregWorkItems; + +GUIDREGINFO DiskWmiFdoGuidList[] = +{ + { + WMI_DISK_GEOMETRY_GUID, + 1, + 0 + }, + + { + WMI_STORAGE_FAILURE_PREDICT_STATUS_GUID, + 1, + WMIREG_FLAG_EXPENSIVE + }, + + { + WMI_STORAGE_FAILURE_PREDICT_DATA_GUID, + 1, + WMIREG_FLAG_EXPENSIVE + }, + + { + WMI_STORAGE_FAILURE_PREDICT_FUNCTION_GUID, + 1, + WMIREG_FLAG_EXPENSIVE + }, + + { + WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID, + 1, + WMIREG_FLAG_EVENT_ONLY_GUID + }, + + { + WMI_STORAGE_FAILURE_PREDICT_THRESHOLDS_GUID, + 1, + WMIREG_FLAG_EXPENSIVE + }, + + { + WMI_STORAGE_SCSI_INFO_EXCEPTIONS_GUID, + 1, + 0 + }, + + +}; + + +GUID DiskPredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID; + +#define DiskGeometryGuid 0 +#define SmartStatusGuid 1 +#define SmartDataGuid 2 +#define SmartPerformFunction 3 + #define AllowDisallowPerformanceHit 1 + #define EnableDisableHardwareFailurePrediction 2 + #define EnableDisableFailurePredictionPolling 3 + #define GetFailurePredictionCapability 4 + #define EnableOfflineDiags 5 + +#define SmartEventGuid 4 +#define SmartThresholdsGuid 5 +#define ScsiInfoExceptionsGuid 6 + +#if 0 + // + // Enable this to add WMI support for PDOs +GUIDREGINFO DiskWmiPdoGuidList[] = +{ + { + // {25007F51-57C2-11d1-A528-00A0C9062910} + { 0x25007f52, 0x57c2, 0x11d1, + { 0xa5, 0x28, 0x0, 0xa0, 0xc9, 0x6, 0x29, 0x10 } }, + 0 + }, + +}; + +ULONG DiskDummyData[4] = { 1, 2, 3, 4}; +#endif + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, DiskWmiFunctionControl) +#pragma alloc_text(PAGE, DiskFdoQueryWmiRegInfo) +#pragma alloc_text(PAGE, DiskFdoQueryWmiDataBlock) +#pragma alloc_text(PAGE, DiskFdoSetWmiDataBlock) +#pragma alloc_text(PAGE, DiskFdoSetWmiDataItem) +#pragma alloc_text(PAGE, DiskFdoExecuteWmiMethod) + +#pragma alloc_text(PAGE, DiskDetectFailurePrediction) +#pragma alloc_text(PAGE, DiskEnableDisableFailurePrediction) +#pragma alloc_text(PAGE, DiskEnableDisableFailurePredictPolling) +#pragma alloc_text(PAGE, DiskReadFailurePredictStatus) +#pragma alloc_text(PAGE, DiskReadFailurePredictData) +#pragma alloc_text(PAGE, DiskReadFailurePredictThresholds) +#pragma alloc_text(PAGE, DiskGetIdentifyInfo) +#pragma alloc_text(PAGE, DiskReadSmartLog) +#pragma alloc_text(PAGE, DiskWriteSmartLog) + +#pragma alloc_text(PAGE, DiskPerformSmartCommand) + +#pragma alloc_text(PAGE, DiskSendFailurePredictIoctl) + +#pragma alloc_text(PAGE, DiskReregWorker) +#pragma alloc_text(PAGE, DiskInitializeReregistration) + +#endif + + +// +// SMART/IDE specific routines + +// +// Read SMART data attributes. +// SrbControl should be sizeof(SRB_IO_CONTROL) + +// (sizeof(SENDCMDINPARAMS)-1) + +// READ_ATTRIBUTE_BUFFER_SIZE +// Attribute data returned at &SendCmdOutParams->bBuffer[0] +// +#define DiskReadSmartData(FdoExtension, \ + SrbControl, \ + BufferSize) \ + DiskPerformSmartCommand(FdoExtension, \ + IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS, \ + SMART_CMD, \ + READ_ATTRIBUTES, \ + 0, \ + 0, \ + (SrbControl), \ + (BufferSize)) + + +// +// Read SMART data thresholds. +// SrbControl should be sizeof(SRB_IO_CONTROL) + +// (sizeof(SENDCMDINPARAMS)-1) + +// READ_THRESHOLD_BUFFER_SIZE +// Attribute data returned at &SendCmdOutParams->bBuffer[0] +// +#define DiskReadSmartThresholds(FdoExtension, \ + SrbControl, \ + BufferSize) \ + DiskPerformSmartCommand(FdoExtension, \ + IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS, \ + SMART_CMD, \ + READ_THRESHOLDS, \ + 0, \ + 0, \ + (SrbControl), \ + (BufferSize)) + + +// +// Read SMART status +// SrbControl should be sizeof(SRB_IO_CONTROL) + +// (sizeof(SENDCMDINPARAMS)-1) + +// sizeof(IDEREGS) +// Failure predicted if cmdOutParameters[3] == 0xf4 and [4] == 0x2c +// +#define DiskReadSmartStatus(FdoExtension, \ + SrbControl, \ + BufferSize) \ + DiskPerformSmartCommand(FdoExtension, \ + IOCTL_SCSI_MINIPORT_RETURN_STATUS, \ + SMART_CMD, \ + RETURN_SMART_STATUS, \ + 0, \ + 0, \ + (SrbControl), \ + (BufferSize)) + + +// +// Read disks IDENTIFY data +// SrbControl should be sizeof(SRB_IO_CONTROL) + +// (sizeof(SENDCMDINPARAMS)-1) + +// sizeof(IDENTIFY_BUFFER_SIZE) +// Identify data returned at &cmdOutParams.bBuffer[0] +// +#define DiskGetIdentifyData(FdoExtension, \ + SrbControl, \ + BufferSize) \ + DiskPerformSmartCommand(FdoExtension, \ + IOCTL_SCSI_MINIPORT_IDENTIFY, \ + ID_CMD, \ + 0, \ + 0, \ + 0, \ + (SrbControl), \ + (BufferSize)) + + +// +// Enable SMART +// +__inline NTSTATUS +DiskEnableSmart( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)]; + ULONG bufferSize = sizeof(srbControl); + + return DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_ENABLE_SMART, + SMART_CMD, + ENABLE_SMART, + 0, + 0, + (PSRB_IO_CONTROL)srbControl, + &bufferSize); +} + +// +// Disable SMART +// +__inline NTSTATUS +DiskDisableSmart( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)]; + ULONG bufferSize = sizeof(srbControl); + return DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_DISABLE_SMART, + SMART_CMD, + DISABLE_SMART, + 0, + 0, + (PSRB_IO_CONTROL)srbControl, + &bufferSize); +} + +// +// Enable Attribute Autosave +// +__inline NTSTATUS +DiskEnableSmartAttributeAutosave( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)]; + ULONG bufferSize = sizeof(srbControl); + return DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE, + SMART_CMD, + ENABLE_DISABLE_AUTOSAVE, + 0xf1, + 0, + (PSRB_IO_CONTROL)srbControl, + &bufferSize); +} + +// +// Disable Attribute Autosave +// +__inline NTSTATUS +DiskDisableSmartAttributeAutosave( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)]; + ULONG bufferSize = sizeof(srbControl); + return DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE, + SMART_CMD, + ENABLE_DISABLE_AUTOSAVE, + 0x00, + 0, + (PSRB_IO_CONTROL)srbControl, + &bufferSize); +} + +// +// Initialize execution of SMART online diagnostics +// +__inline NTSTATUS +DiskExecuteSmartDiagnostics( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + UCHAR Subcommand + ) +{ + UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)]; + ULONG bufferSize = sizeof(srbControl); + return DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS, + SMART_CMD, + EXECUTE_OFFLINE_DIAGS, + 0, + Subcommand, + (PSRB_IO_CONTROL)srbControl, + &bufferSize); +} + + +NTSTATUS +DiskReadSmartLog( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN UCHAR SectorCount, + IN UCHAR LogAddress, + OUT PUCHAR Buffer + ) +{ + PSRB_IO_CONTROL srbControl; + NTSTATUS status; + PSENDCMDOUTPARAMS sendCmdOutParams; + ULONG logSize, bufferSize; + + PAGED_CODE(); + + logSize = SectorCount * SMART_LOG_SECTOR_SIZE; + bufferSize = sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1 + + logSize; + + srbControl = ExAllocatePoolWithTag(NonPagedPool, + bufferSize, + DISK_TAG_SMART); + + if (srbControl != NULL) + { + status = DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_READ_SMART_LOG, + SMART_CMD, + SMART_READ_LOG, + SectorCount, + LogAddress, + srbControl, + &bufferSize); + + if (NT_SUCCESS(status)) + { + sendCmdOutParams = (PSENDCMDOUTPARAMS)((PUCHAR)srbControl + + sizeof(SRB_IO_CONTROL)); + RtlCopyMemory(Buffer, + &sendCmdOutParams->bBuffer[0], + logSize); + } + + ExFreePool(srbControl); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + return(status); +} + + +NTSTATUS +DiskWriteSmartLog( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN UCHAR SectorCount, + IN UCHAR LogAddress, + IN PUCHAR Buffer + ) +{ + PSRB_IO_CONTROL srbControl; + NTSTATUS status; + PSENDCMDINPARAMS sendCmdInParams; + ULONG logSize, bufferSize; + + PAGED_CODE(); + + logSize = SectorCount * SMART_LOG_SECTOR_SIZE; + bufferSize = sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1 + + logSize; + + srbControl = ExAllocatePoolWithTag(NonPagedPool, + bufferSize, + DISK_TAG_SMART); + + if (srbControl != NULL) + { + sendCmdInParams = (PSENDCMDINPARAMS)((PUCHAR)srbControl + + sizeof(SRB_IO_CONTROL)); + RtlCopyMemory(&sendCmdInParams->bBuffer[0], + Buffer, + logSize); + status = DiskPerformSmartCommand(FdoExtension, + IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG, + SMART_CMD, + SMART_WRITE_LOG, + SectorCount, + LogAddress, + srbControl, + &bufferSize); + + ExFreePool(srbControl); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + return(status); +} + +NTSTATUS +DiskPerformSmartCommand( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN ULONG SrbControlCode, + IN UCHAR Command, + IN UCHAR Feature, + IN UCHAR SectorCount, + IN UCHAR SectorNumber, + IN OUT PSRB_IO_CONTROL SrbControl, + OUT PULONG BufferSize + ) +/*++ + +Routine Description: + + This routine will perform some SMART command + +Arguments: + + FdoExtension is the FDO device extension + + SrbControlCode is the SRB control code to use for the request + + Command is the SMART command to be executed. It may be SMART_CMD or + ID_CMD. + + Feature is the value to place in the IDE feature register. + + SectorCount is the value to place in the IDE SectorCount register + + SrbControl is the buffer used to build the SRB_IO_CONTROL and pass + any input parameters. It also returns the output parameters. + + *BufferSize on entry has total size of SrbControl and on return has + the size used in SrbControl. + + + +Return Value: + + status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + PUCHAR buffer; + PSENDCMDINPARAMS cmdInParameters; + PSENDCMDOUTPARAMS cmdOutParameters; + ULONG outBufferSize; + NTSTATUS status; + ULONG availableBufferSize; + KEVENT event; + PIRP irp; + IO_STATUS_BLOCK ioStatus; + SCSI_REQUEST_BLOCK srb; + LARGE_INTEGER startingOffset; + ULONG length; + PIO_STACK_LOCATION irpStack; + + PAGED_CODE(); + + // + // Point to the 'buffer' portion of the SRB_CONTROL and compute how + // much room we have left in the srb control + // + + buffer = (PUCHAR)SrbControl; + buffer += sizeof(SRB_IO_CONTROL); + + cmdInParameters = (PSENDCMDINPARAMS)buffer; + cmdOutParameters = (PSENDCMDOUTPARAMS)buffer; + + availableBufferSize = *BufferSize - sizeof(SRB_IO_CONTROL); + +#if DBG + // + // Ensure control codes and buffer lengths passed are correct + // + { + ULONG controlCode; + ULONG lengthNeeded = sizeof(SENDCMDINPARAMS) - 1; + + if (Command == SMART_CMD) + { + switch (Feature) + { + + case ENABLE_SMART: + { + controlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART; + + break; + } + + case DISABLE_SMART: + { + controlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART; + break; + } + + case RETURN_SMART_STATUS: + { + // + // Ensure bBuffer is at least 2 bytes (to hold the values of + // cylinderLow and cylinderHigh). + // + + lengthNeeded = sizeof(SENDCMDINPARAMS) - 1 + sizeof(IDEREGS); + + controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS; + break; + } + + case ENABLE_DISABLE_AUTOSAVE: + { + controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; + break; + } + + case SAVE_ATTRIBUTE_VALUES: + { + controlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; + break; + } + + + case EXECUTE_OFFLINE_DIAGS: + { + controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; + break; + } + + case READ_ATTRIBUTES: + { + controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; + lengthNeeded = READ_ATTRIBUTE_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) - 1; + break; + } + + case READ_THRESHOLDS: + { + controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; + lengthNeeded = READ_THRESHOLD_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) - 1; + break; + } + + case SMART_READ_LOG: + { + controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; + lengthNeeded = (SectorCount * SMART_LOG_SECTOR_SIZE) + + sizeof(SENDCMDINPARAMS) - 1; + break; + } + + case SMART_WRITE_LOG: + { + controlCode = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; + lengthNeeded = (SectorCount * SMART_LOG_SECTOR_SIZE) + + sizeof(SENDCMDINPARAMS) - 1; + break; + } + + default: + controlCode = 0; + break; + + } + } else if (Command == ID_CMD) { + controlCode = IOCTL_SCSI_MINIPORT_IDENTIFY; + lengthNeeded = IDENTIFY_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) -1; + } else { + controlCode = 0; + ASSERT(FALSE); + } + + ASSERT(controlCode == SrbControlCode); + ASSERT(availableBufferSize >= lengthNeeded); + } +#endif + + // + // Build SrbControl and input to SMART command + // + + SrbControl->HeaderLength = sizeof(SRB_IO_CONTROL); + RtlMoveMemory (SrbControl->Signature, "SCSIDISK", 8); + SrbControl->Timeout = FdoExtension->TimeOutValue; + SrbControl->Length = availableBufferSize; + + SrbControl->ControlCode = SrbControlCode; + + cmdInParameters->cBufferSize = sizeof(SENDCMDINPARAMS); + cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId; + cmdInParameters->irDriveRegs.bFeaturesReg = Feature; + cmdInParameters->irDriveRegs.bSectorCountReg = SectorCount; + cmdInParameters->irDriveRegs.bSectorNumberReg = SectorNumber; + cmdInParameters->irDriveRegs.bCylLowReg = SMART_CYL_LOW; + cmdInParameters->irDriveRegs.bCylHighReg = SMART_CYL_HI; + cmdInParameters->irDriveRegs.bCommandReg = Command; + + + // + // Create and send irp + // + KeInitializeEvent(&event, NotificationEvent, FALSE); + + startingOffset.QuadPart = (LONGLONG) 1; + + length = SrbControl->HeaderLength + SrbControl->Length; + + irp = IoBuildSynchronousFsdRequest( + IRP_MJ_SCSI, + commonExtension->LowerDeviceObject, + SrbControl, + length, + &startingOffset, + &event, + &ioStatus); + + if (irp == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + irpStack = IoGetNextIrpStackLocation(irp); + + // + // Set major and minor codes. + // + + irpStack->MajorFunction = IRP_MJ_SCSI; + irpStack->MinorFunction = 1; + + // + // Fill in SRB fields. + // + + irpStack->Parameters.Others.Argument1 = &srb; + + // + // Zero out the srb. + // + + RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); + + srb.PathId = diskData->ScsiAddress.PathId; + srb.TargetId = diskData->ScsiAddress.TargetId; + srb.Lun = diskData->ScsiAddress.Lun; + + srb.Function = SRB_FUNCTION_IO_CONTROL; + srb.Length = sizeof(SCSI_REQUEST_BLOCK); + + srb.SrbFlags = FdoExtension->SrbFlags; + SET_FLAG(srb.SrbFlags, SRB_FLAGS_DATA_IN); + SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE); + + srb.QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb.QueueTag = SP_UNTAGGED; + + srb.OriginalRequest = irp; + + // + // Set timeout to requested value. + // + + srb.TimeOutValue = SrbControl->Timeout; + + // + // Set the data buffer. + // + + srb.DataBuffer = SrbControl; + srb.DataTransferLength = length; + + // + // Flush the data buffer for output. This will insure that the data is + // written back to memory. Since the data-in flag is the the port driver + // will flush the data again for input which will ensure the data is not + // in the cache. + // + + KeFlushIoBuffers(irp->MdlAddress, FALSE, TRUE); + + // + // Call port driver to handle this request. + // + + status = IoCallDriver(commonExtension->LowerDeviceObject, irp); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + return status; +} + + +NTSTATUS +DiskGetIdentifyInfo( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PBOOLEAN SupportSmart + ) +{ + UCHAR outBuffer[sizeof(SRB_IO_CONTROL) + (sizeof(SENDCMDINPARAMS)-1) + IDENTIFY_BUFFER_SIZE]; + ULONG outBufferSize = sizeof(outBuffer); + NTSTATUS status; + + PAGED_CODE(); + + status = DiskGetIdentifyData(FdoExtension, + (PSRB_IO_CONTROL)outBuffer, + &outBufferSize); + + if (NT_SUCCESS(status)) + { + PUSHORT identifyData = (PUSHORT)&(outBuffer[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS)-1]); + USHORT commandSetSupported = identifyData[82]; + + *SupportSmart = ((commandSetSupported != 0xffff) && + (commandSetSupported != 0) && + ((commandSetSupported & 1) == 1)); + } else { + *SupportSmart = FALSE; + } + + DebugPrint((3, "DiskGetIdentifyInfo: SMART %s supported for device %p, status %lx\n", + *SupportSmart ? "is" : "is not", + FdoExtension->DeviceObject, + status)); + + return status; +} + + +// +// FP Ioctl specific routines +// + +NTSTATUS +DiskSendFailurePredictIoctl( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_PREDICT_FAILURE checkFailure + ) +{ + KEVENT event; + PDEVICE_OBJECT deviceObject; + IO_STATUS_BLOCK ioStatus; + PIRP irp; + NTSTATUS status; + + PAGED_CODE(); + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + deviceObject = IoGetAttachedDeviceReference(FdoExtension->DeviceObject); + + irp = IoBuildDeviceIoControlRequest( + IOCTL_STORAGE_PREDICT_FAILURE, + deviceObject, + NULL, + 0, + checkFailure, + sizeof(STORAGE_PREDICT_FAILURE), + FALSE, + &event, + &ioStatus); + + if (irp != NULL) + { + status = IoCallDriver(deviceObject, irp); + if (status == STATUS_PENDING) + { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = ioStatus.Status; + } + + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + ObDereferenceObject(deviceObject); + + return status; +} + + +// +// FP type independent routines +// + +NTSTATUS +DiskEnableDisableFailurePrediction( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + BOOLEAN Enable + ) +/*++ + +Routine Description: + + Enable or disable failure prediction at the hardware level + +Arguments: + + FdoExtension + + Enable + +Return Value: + + NT Status + +--*/ +{ + NTSTATUS status; + PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension); + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + + PAGED_CODE(); + + switch(diskData->FailurePredictionCapability) + { + case FailurePredictionSmart: + { + + if (Enable) + { + status = DiskEnableSmart(FdoExtension); + } else { + status = DiskDisableSmart(FdoExtension); + } + + break; + } + + case FailurePredictionSense: + case FailurePredictionIoctl: + { + // + // We assume that the drive is already setup properly for + // failure prediction + // + status = STATUS_SUCCESS; + break; + } + + default: + { + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + return status; +} + +NTSTATUS +DiskEnableDisableFailurePredictPolling( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + BOOLEAN Enable, + ULONG PollTimeInSeconds + ) +/*++ + +Routine Description: + + Enable or disable polling for hardware failure detection + +Arguments: + + FdoExtension + + Enable + + PollTimeInSeconds - if 0 then no change to current polling timer + +Return Value: + + NT Status + +--*/ +{ + NTSTATUS status; + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + + PAGED_CODE(); + + if (Enable) + { + status = DiskEnableDisableFailurePrediction(FdoExtension, + Enable); + } else { + status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(status)) + { + status = ClassSetFailurePredictionPoll(FdoExtension, + Enable ? diskData->FailurePredictionCapability : + FailurePredictionNone, + PollTimeInSeconds); + + // + // Even if this failed we do not want to disable FP on the + // hardware. FP is only ever disabled on the hardware by + // specific command of the user. + // + } + + return status; +} + + +NTSTATUS +DiskReadFailurePredictStatus( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_STATUS DiskSmartStatus + ) +/*++ + +Routine Description: + + Obtains current failure prediction status + +Arguments: + + FdoExtension + + DiskSmartStatus + +Return Value: + + NT Status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + NTSTATUS status; + + PAGED_CODE(); + + DiskSmartStatus->PredictFailure = FALSE; + + switch(diskData->FailurePredictionCapability) + { + case FailurePredictionSmart: + { + UCHAR outBuffer[sizeof(SRB_IO_CONTROL) + (sizeof(SENDCMDINPARAMS) - 1 + sizeof(IDEREGS))]; + ULONG outBufferSize = sizeof(outBuffer); + PSENDCMDOUTPARAMS cmdOutParameters; + + status = DiskReadSmartStatus(FdoExtension, + (PSRB_IO_CONTROL)outBuffer, + &outBufferSize); + + if (NT_SUCCESS(status)) + { + cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer + + sizeof(SRB_IO_CONTROL)); + + DiskSmartStatus->Reason = 0; // Unknown; + DiskSmartStatus->PredictFailure = ((cmdOutParameters->bBuffer[3] == 0xf4) && + (cmdOutParameters->bBuffer[4] == 0x2c)); + } + break; + } + + case FailurePredictionSense: + { + DiskSmartStatus->Reason = FdoExtension->FailureReason; + DiskSmartStatus->PredictFailure = FdoExtension->FailurePredicted; + status = STATUS_SUCCESS; + break; + } + + case FailurePredictionIoctl: + case FailurePredictionNone: + default: + { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + } + + return status; +} + +NTSTATUS +DiskReadFailurePredictData( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_DATA DiskSmartData + ) +/*++ + +Routine Description: + + Obtains current failure prediction data. Not available for + FAILURE_PREDICT_SENSE types. + +Arguments: + + FdoExtension + + DiskSmartData + +Return Value: + + NT Status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + NTSTATUS status; + + PAGED_CODE(); + + switch(diskData->FailurePredictionCapability) + { + case FailurePredictionSmart: + { + PUCHAR outBuffer; + ULONG outBufferSize; + PSENDCMDOUTPARAMS cmdOutParameters; + + outBufferSize = sizeof(SRB_IO_CONTROL) + + (sizeof(SENDCMDOUTPARAMS)-1) + + READ_ATTRIBUTE_BUFFER_SIZE; + + outBuffer = ExAllocatePoolWithTag(NonPagedPool, + outBufferSize, + DISK_TAG_SMART); + + if (outBuffer != NULL) + { + status = DiskReadSmartData(FdoExtension, + (PSRB_IO_CONTROL)outBuffer, + &outBufferSize); + + if (NT_SUCCESS(status)) + { + cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer + + sizeof(SRB_IO_CONTROL)); + + DiskSmartData->Length = READ_ATTRIBUTE_BUFFER_SIZE; + RtlCopyMemory(DiskSmartData->VendorSpecific, + cmdOutParameters->bBuffer, + READ_ATTRIBUTE_BUFFER_SIZE); + } + ExFreePool(outBuffer); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + break; + } + + case FailurePredictionSense: + { + DiskSmartData->Length = sizeof(ULONG); + *((PULONG)DiskSmartData->VendorSpecific) = FdoExtension->FailureReason; + + status = STATUS_SUCCESS; + break; + } + + case FailurePredictionIoctl: + case FailurePredictionNone: + default: + { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + } + + return status; +} + +NTSTATUS +DiskReadFailurePredictThresholds( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PSTORAGE_FAILURE_PREDICT_THRESHOLDS DiskSmartThresholds + ) +/*++ + +Routine Description: + + Obtains current failure prediction thresholds. Not available for + FAILURE_PREDICT_SENSE types. + +Arguments: + + FdoExtension + + DiskSmartData + +Return Value: + + NT Status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + NTSTATUS status; + + PAGED_CODE(); + + switch(diskData->FailurePredictionCapability) + { + case FailurePredictionSmart: + { + PUCHAR outBuffer; + PSENDCMDOUTPARAMS cmdOutParameters; + ULONG outBufferSize; + + outBufferSize = sizeof(SRB_IO_CONTROL) + + (sizeof(SENDCMDOUTPARAMS)-1) + + READ_THRESHOLD_BUFFER_SIZE; + + outBuffer = ExAllocatePoolWithTag(NonPagedPool, + outBufferSize, + DISK_TAG_SMART); + + if (outBuffer != NULL) + { + status = DiskReadSmartThresholds(FdoExtension, + (PSRB_IO_CONTROL)outBuffer, + &outBufferSize); + + if (NT_SUCCESS(status)) + { + cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer + + sizeof(SRB_IO_CONTROL)); + + RtlCopyMemory(DiskSmartThresholds->VendorSpecific, + cmdOutParameters->bBuffer, + READ_THRESHOLD_BUFFER_SIZE); + } + ExFreePool(outBuffer); + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + break; + } + + case FailurePredictionSense: + case FailurePredictionIoctl: + case FailurePredictionNone: + default: + { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + } + + return status; +} + +void DiskReregWorker( + IN PVOID Context + ) +{ + PDISKREREGREQUEST reregRequest; + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PIRP irp; + + PAGED_CODE(); + + do + { + reregRequest = (PDISKREREGREQUEST)ExInterlockedPopEntryList( + &DiskReregHead, + &DiskReregSpinlock); + + deviceObject = reregRequest->DeviceObject; + irp = reregRequest->Irp; + + status = IoWMIRegistrationControl(deviceObject, + WMIREG_ACTION_UPDATE_GUIDS); + + if (! NT_SUCCESS(status)) + { + DebugPrint((1, "DiskReregWorker: Reregistration failed %x\n", + status)); + } + + // + // Release remove lock and free irp, now that we are done + // processing this + // + ClassReleaseRemoveLock(deviceObject, irp); + + IoFreeMdl(irp->MdlAddress); + IoFreeIrp(irp); + + ExFreePool(reregRequest); + + } while (InterlockedDecrement(&DiskReregWorkItems)); + + +} + +NTSTATUS DiskInitializeReregistration( + void + ) +{ + PAGED_CODE(); + + // + // Initialize the global work item and spinlock used to manage the + // list of disks reregistering their guids + // + ExInitializeWorkItem( &DiskReregWorkItem, + DiskReregWorker, + NULL ); + + KeInitializeSpinLock(&DiskReregSpinlock); + + return(STATUS_SUCCESS); +} + +NTSTATUS DiskPostReregisterRequest( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +{ + PDISKREREGREQUEST reregRequest; + NTSTATUS status; + + reregRequest = ExAllocatePoolWithTag(NonPagedPool, + sizeof(DISKREREGREQUEST), + DISK_TAG_SMART); + + if (reregRequest != NULL) + { + // + // add the disk that needs reregistration to the stack of disks + // to reregister. If the list is transitioning from empty to + // non empty then also kick off the work item so that the + // reregistration worker can do the reregister. + // + reregRequest->DeviceObject = DeviceObject; + reregRequest->Irp = Irp; + ExInterlockedPushEntryList( + &DiskReregHead, + &reregRequest->Next, + &DiskReregSpinlock); + + if (InterlockedIncrement(&DiskReregWorkItems) == 1) + { + ExQueueWorkItem( &DiskReregWorkItem, DelayedWorkQueue ); + } + status = STATUS_SUCCESS; + } else { + DebugPrint((1, "DiskPostReregisterRequest: could not allocate reregRequest for %p\n", + DeviceObject)); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + return(status); +} + +NTSTATUS DiskInfoExceptionComplete( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK srb = Context; + NTSTATUS status; + BOOLEAN retry; + ULONG retryInterval; + ULONG srbStatus; + BOOLEAN freeLockAndIrp = TRUE; + KIRQL oldIrql; + + ASSERT(fdoExtension->CommonExtension.IsFdo); + + srbStatus = SRB_STATUS(srb->SrbStatus); + + // + // Check SRB status for success of completing request. + // SRB_STATUS_DATA_OVERRUN also indicates success. + // + if ((srbStatus != SRB_STATUS_SUCCESS) && + (srbStatus != SRB_STATUS_DATA_OVERRUN)) + { + DebugPrint((2, "DiskInfoExceptionComplete: IRP %p, SRB %p\n", Irp, srb)); + + retry = ClassInterpretSenseInfo( + DeviceObject, + srb, + irpStack->MajorFunction, + 0, + MAXIMUM_RETRIES - + ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4), + &status, + &retryInterval); + + // + // If the status is verified required and the this request + // should bypass verify required then retry the request. + // + + if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) && + status == STATUS_VERIFY_REQUIRED) + { + status = STATUS_IO_DEVICE_ERROR; + retry = TRUE; + } + + if (retry && irpStack->Parameters.Others.Argument4) + { + irpStack->Parameters.Others.Argument4 = + (PVOID)((ULONG_PTR)irpStack->Parameters.Others.Argument4 - 1); + + // + // Retry request. + // + + DebugPrint((1, "DiskInfoExceptionComplete: Retry request %p\n", Irp)); + + ASSERT(srb->DataBuffer == MmGetMdlVirtualAddress(Irp->MdlAddress)); + + // + // Reset byte count of transfer in SRB Extension. + // + srb->DataTransferLength = Irp->MdlAddress->ByteCount; + + // + // Zero SRB statuses. + // + + srb->SrbStatus = srb->ScsiStatus = 0; + + // + // Set the no disconnect flag, disable synchronous data transfers and + // disable tagged queuing. This fixes some errors. + // + + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + CLEAR_FLAG(srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); + + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb->QueueTag = SP_UNTAGGED; + + // + // Set up major SCSI function. + // + + nextIrpStack->MajorFunction = IRP_MJ_SCSI; + + // + // Save SRB address in next stack for port driver. + // + + nextIrpStack->Parameters.Scsi.Srb = srb; + + + IoSetCompletionRoutine(Irp, + DiskInfoExceptionComplete, + srb, + TRUE, TRUE, TRUE); + + (VOID)IoCallDriver(commonExtension->LowerDeviceObject, Irp); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + } else { + + // + // Get the results from the mode sense + // + PMODE_INFO_EXCEPTIONS pageData; + PMODE_PARAMETER_HEADER modeData; + ULONG modeDataLength; + + modeData = srb->DataBuffer; + modeDataLength = srb->DataTransferLength; + + pageData = ClassFindModePage((PUCHAR) modeData, + modeDataLength, + MODE_PAGE_FAULT_REPORTING, + TRUE); + if (pageData != NULL) + { + DebugPrint((1, "DiskInfoExceptionComplete: %p supports SMART\n", + DeviceObject)); + + if (pageData->Dexcpt == 0) + { + diskData->FailurePredictionCapability = FailurePredictionSense; + status = DiskPostReregisterRequest(DeviceObject, Irp); + + if (NT_SUCCESS(status)) + { + // + // Make sure we won't free the remove lock and the irp + // since we need to keep these until after the work + // item has completed running + // + freeLockAndIrp = FALSE; + } + } else { + DebugPrint((1, "DiskInfoExceptionComplete: %p is not enabled for SMART\n", + DeviceObject)); + + } + + } else { + DebugPrint((1, "DiskInfoExceptionComplete: %p does not supports SMART\n", + DeviceObject)); + + } + + // + // Set status for successful request + // + + status = STATUS_SUCCESS; + + } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS) + + // + // Free the srb + // + ExFreePool(srb->SenseInfoBuffer); + ExFreePool(srb->DataBuffer); + ExFreePool(srb); + + if (freeLockAndIrp) + { + // + // Set status in completing IRP. + // + + Irp->IoStatus.Status = status; + + // + // If pending has be returned for this irp then mark the current stack as + // pending. + // + + if (Irp->PendingReturned) { + IoMarkIrpPending(Irp); + } + + ClassReleaseRemoveLock(DeviceObject, Irp); + IoFreeMdl(Irp->MdlAddress); + IoFreeIrp(Irp); + } + + return(STATUS_MORE_PROCESSING_REQUIRED); + +} + +NTSTATUS DiskInfoExceptionCheck( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PUCHAR modeData; + PSCSI_REQUEST_BLOCK srb; + PCDB cdb; + PIRP irp; + PIO_STACK_LOCATION irpStack; + PVOID senseInfoBuffer; + ULONG isRemoved; + + modeData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + MODE_DATA_SIZE, + DISK_TAG_INFO_EXCEPTION); + if (modeData == NULL) + { + DebugPrint((1, "DiskInfoExceptionCheck: Can't allocate mode data " + "buffer\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + srb = ExAllocatePoolWithTag(NonPagedPool, + SCSI_REQUEST_BLOCK_SIZE, + DISK_TAG_SRB); + if (srb == NULL) + { + ExFreePool(modeData); + DebugPrint((1, "DiskInfoExceptionCheck: Can't allocate srb " + "buffer\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Build the MODE SENSE CDB. + // + RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE); + + cdb = (PCDB)srb->Cdb; + srb->CdbLength = 6; + cdb = (PCDB)srb->Cdb; + + // + // Set timeout value from device extension. + // + srb->TimeOutValue = FdoExtension->TimeOutValue; + + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.PageCode = MODE_PAGE_FAULT_REPORTING; + cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE; + + // + // Write length to SRB. + // + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + + // + // Set SCSI bus address. + // + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + // + // Enable auto request sense. + // + + srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; + + // + // Sense buffer is in aligned nonpaged pool. + // + + senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + SENSE_BUFFER_SIZE, + '7CcS'); + + if (senseInfoBuffer == NULL) + { + ExFreePool(srb); + ExFreePool(modeData); + DebugPrint((1, "DiskInfoExceptionCheck: Can't allocate request sense " + "buffer\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + srb->SenseInfoBuffer = senseInfoBuffer; + srb->DataBuffer = modeData; + + srb->SrbFlags = FdoExtension->SrbFlags; + + + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); + + // + // Disable synchronous transfer for these requests. + // + SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + + // + // Don't freeze the queue on an error + // + SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb->QueueTag = SP_UNTAGGED; + + + // + // Build device I/O control request with METHOD_NEITHER data transfer. + // We'll queue a completion routine to cleanup the MDL's and such ourself. + // + + irp = IoAllocateIrp( + (CCHAR) (FdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1), + FALSE); + + if (irp == NULL) + { + ExFreePool(senseInfoBuffer); + ExFreePool(srb); + ExFreePool(modeData); + DebugPrint((1, "DiskInfoExceptionCheck: Can't allocate Irp\n")); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + isRemoved = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp); + + if (isRemoved) + { + ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp); + IoFreeIrp(irp); + ExFreePool(senseInfoBuffer); + ExFreePool(srb); + ExFreePool(modeData); + DebugPrint((1, "DiskInfoExceptionCheck: RemoveLock says isRemoved\n")); + return(STATUS_DEVICE_DOES_NOT_EXIST); + } + + // + // Get next stack location. + // + + IoSetNextIrpStackLocation(irp); + irpStack = IoGetCurrentIrpStackLocation(irp); + irpStack->DeviceObject = FdoExtension->DeviceObject; + + // + // Save retry count in current Irp stack. + // + irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES; + + + irpStack = IoGetNextIrpStackLocation(irp); + + // + // Set up SRB for execute scsi request. Save SRB address in next stack + // for the port driver. + // + + irpStack->MajorFunction = IRP_MJ_SCSI; + irpStack->Parameters.Scsi.Srb = srb; + + IoSetCompletionRoutine(irp, + DiskInfoExceptionComplete, + srb, + TRUE, + TRUE, + TRUE); + + irp->MdlAddress = IoAllocateMdl( modeData, + MODE_DATA_SIZE, + FALSE, + FALSE, + irp ); + if (irp->MdlAddress == NULL) + { + ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp); + ExFreePool(srb); + ExFreePool(modeData); + ExFreePool(senseInfoBuffer); + IoFreeIrp( irp ); + DebugPrint((1, "DiskINfoExceptionCheck: Can't allocate MDL\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + MmBuildMdlForNonPagedPool(irp->MdlAddress); + + // + // Set the transfer length. + // + srb->DataTransferLength = MODE_DATA_SIZE; + + // + // Zero out status. + // + srb->ScsiStatus = srb->SrbStatus = 0; + srb->NextSrb = 0; + + // + // Set up IRP Address. + // + srb->OriginalRequest = irp; + + // + // Call the port driver with the request and wait for it to complete. + // + + IoMarkIrpPending(irp); + IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, + irp); + + return(STATUS_PENDING); +} + +NTSTATUS +DiskDetectFailurePrediction( + PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + PFAILURE_PREDICTION_METHOD FailurePredictCapability + ) +/*++ + +Routine Description: + + Detect if device has any failure prediction capabilities. First we + check for IDE SMART capability. This is done by sending the drive an + IDENTIFY command and checking if the SMART command set bit is set. + + Next we check if SCSI SMART (aka Information Exception Control Page, + X3T10/94-190 Rev 4). This is done by querying for the Information + Exception mode page. + + Lastly we check if the device has IOCTL failure prediction. This mechanism + a filter driver implements IOCTL_STORAGE_PREDICT_FAILURE and will respond + with the information in the IOCTL. We do this by sending the ioctl and + if the status returned is STATUS_SUCCESS we assume that it is supported. + +Arguments: + + FdoExtension + + *FailurePredictCapability + +Return Value: + + NT Status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + BOOLEAN supportFP; + NTSTATUS status; + STORAGE_PREDICT_FAILURE checkFailure; + STORAGE_FAILURE_PREDICT_STATUS diskSmartStatus; + BOOLEAN logErr; + + PAGED_CODE(); + + // + // Assume no failure predict mechanisms + // + *FailurePredictCapability = FailurePredictionNone; + + // + // See if this is an IDE drive that supports SMART. If so enable SMART + // and then ensure that it suports the SMART READ STATUS command + // + status = DiskGetIdentifyInfo(FdoExtension, + &supportFP); + + if (supportFP) + { + status = DiskEnableSmart(FdoExtension); + if (NT_SUCCESS(status)) + { + *FailurePredictCapability = FailurePredictionSmart; + + status = DiskReadFailurePredictStatus(FdoExtension, + &diskSmartStatus); + + DebugPrint((1, "Disk: Device %p %s IDE SMART\n", + FdoExtension->DeviceObject, + NT_SUCCESS(status) ? "does" : "does not")); + + if (! NT_SUCCESS(status)) + { + *FailurePredictCapability = FailurePredictionNone; + } + } + return(status); + } + + // + // See if there is a a filter driver to intercept + // IOCTL_STORAGE_PREDICT_FAILURE + // + status = DiskSendFailurePredictIoctl(FdoExtension, + &checkFailure); + + DebugPrint((1, "Disk: Device %p %s IOCTL_STORAGE_FAILURE_PREDICT\n", + FdoExtension->DeviceObject, + NT_SUCCESS(status) ? "does" : "does not")); + + if (NT_SUCCESS(status)) + { + *FailurePredictCapability = FailurePredictionIoctl; + if (checkFailure.PredictFailure) + { + checkFailure.PredictFailure = 512; + ClassNotifyFailurePredicted(FdoExtension, + (PUCHAR)&checkFailure, + sizeof(checkFailure), + (BOOLEAN)(FdoExtension->FailurePredicted == FALSE), + 0x11, + diskData->ScsiAddress.PathId, + diskData->ScsiAddress.TargetId, + diskData->ScsiAddress.Lun); + + FdoExtension->FailurePredicted = TRUE; + } + return(status); + } + + // + // Finally we assume it will not be a scsi smart drive. but + // we'll also send off an asynchronous mode sense so that if + // it is SMART we'll reregister the device object + // + + DiskInfoExceptionCheck(FdoExtension); + + *FailurePredictCapability = FailurePredictionNone; + + return(STATUS_SUCCESS); +} + + +NTSTATUS +DiskWmiFunctionControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN CLASSENABLEDISABLEFUNCTION Function, + IN BOOLEAN Enable + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to enabled or disable event + generation or data block collection. A device should only expect a + single enable when the first event or data consumer enables events or + data collection and a single disable when the last event or data + consumer disables events or data collection. Data blocks will only + receive collection enable/disable if they were registered as requiring + it. + + + When NT boots, failure prediction is not automatically enabled, although + it may have been persistantly enabled on a previous boot. Polling is also + not automatically enabled. When the first data block that accesses SMART + such as SmartStatusGuid, SmartDataGuid, SmartPerformFunction, or + SmartEventGuid is accessed then SMART is automatically enabled in the + hardware. Polling is enabled when SmartEventGuid is enabled and disabled + when it is disabled. Hardware SMART is only disabled when the DisableSmart + method is called. Polling is also disabled when this is called regardless + of the status of the other guids or events. + +Arguments: + + DeviceObject is the device whose data block is being queried + + GuidIndex is the index into the list of guids provided when the + device registered + + Function specifies which functionality is being enabled or disabled + + Enable is TRUE then the function is being enabled else disabled + +Return Value: + + status + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + ULONG enableCount; + + PAGED_CODE(); + + if ((Function == DataBlockCollection) && Enable) + { + if ((GuidIndex == SmartStatusGuid) || + (GuidIndex == SmartDataGuid) || + (GuidIndex == SmartThresholdsGuid) || + (GuidIndex == SmartPerformFunction)) + { + status = DiskEnableDisableFailurePrediction(fdoExtension, + TRUE); + DebugPrint((3, "Disk: DeviceObject %p, Irp %p Enable -> %lx\n", + DeviceObject, + Irp, + status)); + + } else { + DebugPrint((3, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for Collection\n", + DeviceObject, Irp, + GuidIndex, + Enable ? "Enabled" : "Disabled")); } + } else if (Function == EventGeneration) { + DebugPrint((3, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for Event Generation\n", + DeviceObject, Irp, + GuidIndex, + Enable ? "Enabled" : "Disabled")); + + + if ((GuidIndex == SmartEventGuid) && Enable) + { + status = DiskEnableDisableFailurePredictPolling(fdoExtension, + Enable, + 0); + DebugPrint((3, "Disk: DeviceObject %p, Irp %p %s -> %lx\n", + DeviceObject, + Irp, + Enable ? "DiskEnableSmartPolling" : "DiskDisableSmartPolling", + status)); + } + +#if DBG + } else { + DebugPrint((3, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for function %d\n", + DeviceObject, Irp, + GuidIndex, + Enable ? "Enabled" : "Disabled", + Function)); +#endif + } + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + return status; +} + + + +NTSTATUS +DiskFdoQueryWmiRegInfo( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to retrieve the list of + guids or data blocks that the driver wants to register with WMI. This + routine may not pend or block. Driver should NOT call + ClassWmiCompleteRequest. + +Arguments: + + DeviceObject is the device whose data block is being queried + + *RegFlags returns with a set of flags that describe the guids being + registered for this device. If the device wants enable and disable + collection callbacks before receiving queries for the registered + guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the + returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case + the instance name is determined from the PDO associated with the + device object. Note that the PDO must have an associated devnode. If + WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique + name for the device. + + InstanceName returns with the instance name for the guids if + WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The + caller will call ExFreePool with the buffer returned. + + +Return Value: + + status + +--*/ +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + NTSTATUS status; + + PAGED_CODE(); + + SET_FLAG(DiskWmiFdoGuidList[SmartThresholdsGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + SET_FLAG(DiskWmiFdoGuidList[ScsiInfoExceptionsGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + + switch (diskData->FailurePredictionCapability) + { + case FailurePredictionSmart: + { + CLEAR_FLAG(DiskWmiFdoGuidList[SmartThresholdsGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + // + // Fall Through + // + } + case FailurePredictionIoctl: + { + CLEAR_FLAG(DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID); + + break; + } + + case FailurePredictionSense: + { + CLEAR_FLAG(DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID); + CLEAR_FLAG(DiskWmiFdoGuidList[ScsiInfoExceptionsGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + SET_FLAG (DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + break; + } + + + default: + { + SET_FLAG (DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + SET_FLAG (DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + SET_FLAG (DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID); + SET_FLAG (DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID); + break; + } + } + + // + // Use devnode for FDOs + *RegFlags = WMIREG_FLAG_INSTANCE_PDO; + + return STATUS_SUCCESS; +} + +NTSTATUS +DiskFdoQueryWmiRegInfoEx( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName, + OUT PUNICODE_STRING MofName + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to retrieve the list of + guids or data blocks that the driver wants to register with WMI. This + routine may not pend or block. Driver should NOT call + ClassWmiCompleteRequest. + +Arguments: + + DeviceObject is the device whose data block is being queried + + *RegFlags returns with a set of flags that describe the guids being + registered for this device. If the device wants enable and disable + collection callbacks before receiving queries for the registered + guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the + returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case + the instance name is determined from the PDO associated with the + device object. Note that the PDO must have an associated devnode. If + WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique + name for the device. + + InstanceName returns with the instance name for the guids if + WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The + caller will call ExFreePool with the buffer returned. + + MofName returns initialized with the mof resource name for the + binary mof resource attached to the driver's image file. If the + driver does not have a mof resource then it should leave this + parameter untouched. + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + + status = DiskFdoQueryWmiRegInfo(DeviceObject, + RegFlags, + InstanceName); + + // + // Leave MofName alone since disk doesn't have one + // + return(status); +} + + +NTSTATUS +DiskFdoQueryWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferAvail, + OUT PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + BufferAvail on has the maximum size available to write the data + block. + + Buffer on return is filled with the returned data block + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + ULONG sizeNeeded; + + PAGED_CODE(); + + DebugPrint((3, "Disk: DiskQueryWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n" + " BufferAvail %lx Buffer %lx\n", + DeviceObject, Irp, + GuidIndex, BufferAvail, Buffer)); + + switch (GuidIndex) + { + case DiskGeometryGuid: + { + sizeNeeded = sizeof(DISK_GEOMETRY); + if (BufferAvail >= sizeNeeded) + { + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) + { + // + // Issue ReadCapacity to update device extension + // with information for current media. + status = DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject); + + // + // Note whether the drive is ready. + diskData->ReadyStatus = status; + + if (!NT_SUCCESS(status)) + { + break; + } + } + + // + // Copy drive geometry information from device extension. + RtlMoveMemory(Buffer, + &(fdoExtension->DiskGeometry), + sizeof(DISK_GEOMETRY)); + + status = STATUS_SUCCESS; + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + + case SmartStatusGuid: + { + PSTORAGE_FAILURE_PREDICT_STATUS diskSmartStatus; + + ASSERT(diskData->FailurePredictionCapability != FailurePredictionNone); + + + sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_STATUS); + if (BufferAvail >= sizeNeeded) + { + STORAGE_PREDICT_FAILURE checkFailure; + + diskSmartStatus = (PSTORAGE_FAILURE_PREDICT_STATUS)Buffer; + + status = DiskSendFailurePredictIoctl(fdoExtension, + &checkFailure); + + if (NT_SUCCESS(status)) + { + if (diskData->FailurePredictionCapability == + FailurePredictionSense) + { + diskSmartStatus->Reason = *((PULONG)checkFailure.VendorSpecific); + } else { + diskSmartStatus->Reason = 0; // unknown + } + + diskSmartStatus->PredictFailure = (checkFailure.PredictFailure != 0); + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + + case SmartDataGuid: + { + PSTORAGE_FAILURE_PREDICT_DATA diskSmartData; + + ASSERT((diskData->FailurePredictionCapability == + FailurePredictionSmart) || + (diskData->FailurePredictionCapability == + FailurePredictionIoctl)); + + sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_DATA); + if (BufferAvail >= sizeNeeded) + { + PSTORAGE_PREDICT_FAILURE checkFailure = (PSTORAGE_PREDICT_FAILURE)Buffer; + + diskSmartData = (PSTORAGE_FAILURE_PREDICT_DATA)Buffer; + + status = DiskSendFailurePredictIoctl(fdoExtension, + checkFailure); + + if (NT_SUCCESS(status)) + { + diskSmartData->Length = 512; + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + case SmartThresholdsGuid: + { + PSTORAGE_FAILURE_PREDICT_THRESHOLDS diskSmartThresholds; + + ASSERT((diskData->FailurePredictionCapability == + FailurePredictionSmart)); + + sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_THRESHOLDS); + if (BufferAvail >= sizeNeeded) + { + diskSmartThresholds = (PSTORAGE_FAILURE_PREDICT_THRESHOLDS)Buffer; + status = DiskReadFailurePredictThresholds(fdoExtension, + diskSmartThresholds); + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + case SmartPerformFunction: + { + sizeNeeded = 0; + status = STATUS_SUCCESS; + break; + } + + case ScsiInfoExceptionsGuid: + { + PSTORAGE_SCSI_INFO_EXCEPTIONS infoExceptions; + MODE_INFO_EXCEPTIONS modeInfo; + + ASSERT((diskData->FailurePredictionCapability == + FailurePredictionSense)); + + sizeNeeded = sizeof(STORAGE_SCSI_INFO_EXCEPTIONS); + if (BufferAvail >= sizeNeeded) + { + infoExceptions = (PSTORAGE_SCSI_INFO_EXCEPTIONS)Buffer; + status = DiskGetInfoExceptionInformation(fdoExtension, + &modeInfo); + + if (NT_SUCCESS(status)) + { + infoExceptions->PageSavable = modeInfo.PSBit; + infoExceptions->Flags = modeInfo.Flags; + infoExceptions->MRIE = modeInfo.ReportMethod; + infoExceptions->Padding = 0; + REVERSE_BYTES(&infoExceptions->IntervalTimer, + &modeInfo.IntervalTimer); + REVERSE_BYTES(&infoExceptions->ReportCount, + &modeInfo.ReportCount) + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + default: + { + sizeNeeded = 0; + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + DebugPrint((3, "Disk: DiskQueryWmiDataBlock Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + sizeNeeded, + IO_NO_INCREMENT); + + return status; +} + +NTSTATUS +DiskFdoSetWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + BufferSize has the size of the data block passed + + Buffer has the new values for the data block + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + + PAGED_CODE(); + + DebugPrint((3, "Disk: DiskSetWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n" + " BufferSize %#x Buffer %p\n", + DeviceObject, Irp, + GuidIndex, BufferSize, Buffer)); + + if (GuidIndex == ScsiInfoExceptionsGuid) + { + PSTORAGE_SCSI_INFO_EXCEPTIONS infoExceptions; + MODE_INFO_EXCEPTIONS modeInfo; + + if (BufferSize >= sizeof(STORAGE_SCSI_INFO_EXCEPTIONS)) + { + infoExceptions = (PSTORAGE_SCSI_INFO_EXCEPTIONS)Buffer; + + modeInfo.PageCode = MODE_PAGE_FAULT_REPORTING; + modeInfo.PageLength = sizeof(MODE_INFO_EXCEPTIONS) - 2; + + modeInfo.PSBit = 0; + modeInfo.Flags = infoExceptions->Flags; + + modeInfo.ReportMethod = infoExceptions->MRIE; + + REVERSE_BYTES(&modeInfo.IntervalTimer[0], + &infoExceptions->IntervalTimer); + + REVERSE_BYTES(&modeInfo.ReportCount[0], + &infoExceptions->ReportCount); + + if (modeInfo.Perf == 1) + { + diskData->AllowFPPerfHit = FALSE; + } else { + diskData->AllowFPPerfHit = TRUE; + } + + status = DiskSetInfoExceptionInformation(fdoExtension, + &modeInfo); + } else { + status = STATUS_INVALID_PARAMETER; + } + + } else if (GuidIndex <= SmartEventGuid) + { + status = STATUS_WMI_READ_ONLY; + } else { + status = STATUS_WMI_GUID_NOT_FOUND; + } + + DebugPrint((3, "Disk: DiskSetWmiDataBlock Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + + return status; +} + +NTSTATUS +DiskFdoSetWmiDataItem( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG DataItemId, + IN ULONG BufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + DataItemId has the id of the data item being set + + BufferSize has the size of the data item passed + + Buffer has the new values for the data item + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + DebugPrint((3, "Disk: DiskSetWmiDataItem, Device %p, Irp %p, GuiIndex %d, DataId %d\n" + " BufferSize %#x Buffer %p\n", + DeviceObject, Irp, + GuidIndex, DataItemId, BufferSize, Buffer)); + + if (GuidIndex <= SmartEventGuid) + { + status = STATUS_WMI_READ_ONLY; + } else { + status = STATUS_WMI_GUID_NOT_FOUND; + } + + DebugPrint((3, "Disk: DiskSetWmiDataItem Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + + return status; +} + + +NTSTATUS +DiskFdoExecuteWmiMethod( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG MethodId, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to execute a method. When the + driver has finished filling the data block it must call + ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + MethodId has the id of the method being called + + InBufferSize has the size of the data block passed in as the input to + the method. + + OutBufferSize on entry has the maximum size available to write the + returned data block. + + Buffer is filled with the returned data block + + +Return Value: + + status + +--*/ +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + ULONG sizeNeeded; + NTSTATUS status; + + PAGED_CODE(); + + DebugPrint((3, "Disk: DiskExecuteWmiMethod, DeviceObject %p, Irp %p, Guid Id %d, MethodId %d\n" + " InBufferSize %#x, OutBufferSize %#x, Buffer %p\n", + DeviceObject, Irp, + GuidIndex, MethodId, InBufferSize, OutBufferSize, Buffer)); + + switch(GuidIndex) + { + case SmartPerformFunction: + { + + ASSERT((diskData->FailurePredictionCapability == + FailurePredictionSmart) || + (diskData->FailurePredictionCapability == + FailurePredictionIoctl) || + (diskData->FailurePredictionCapability == + FailurePredictionSense)); + + + switch(MethodId) + { + // + // void AllowPerformanceHit([in] boolean Allow) + // + case AllowDisallowPerformanceHit: + { + BOOLEAN allowPerfHit; + + sizeNeeded = 0; + if (InBufferSize >= sizeof(BOOLEAN)) + { + status = STATUS_SUCCESS; + + allowPerfHit = *((PBOOLEAN)Buffer); + if (diskData->AllowFPPerfHit != allowPerfHit) + { + diskData->AllowFPPerfHit = allowPerfHit; + if (diskData->FailurePredictionCapability == + FailurePredictionSense) + { + MODE_INFO_EXCEPTIONS modeInfo; + + status = DiskGetInfoExceptionInformation(fdoExtension, + &modeInfo); + if (NT_SUCCESS(status)) + { + modeInfo.Perf = allowPerfHit ? 0 : 1; + status = DiskSetInfoExceptionInformation(fdoExtension, + &modeInfo); + } + } + } + + DebugPrint((3, "DiskFdoWmiExecuteMethod: AllowPerformanceHit %x for device %p --> %lx\n", + allowPerfHit, + fdoExtension->DeviceObject, + status)); + } else { + status = STATUS_INVALID_PARAMETER; + } + break; + } + + // + // void EnableDisableHardwareFailurePrediction([in] boolean Enable) + // + case EnableDisableHardwareFailurePrediction: + { + BOOLEAN enable; + + sizeNeeded = 0; + if (InBufferSize >= sizeof(BOOLEAN)) + { + status = STATUS_SUCCESS; + enable = *((PBOOLEAN)Buffer); + if (! enable) + { + // + // If we are disabling we need to also disable + // polling + // + DiskEnableDisableFailurePredictPolling( + fdoExtension, + enable, + 0); + } + + status = DiskEnableDisableFailurePrediction( + fdoExtension, + enable); + + DebugPrint((3, "DiskFdoWmiExecuteMethod: EnableDisableHardwareFailurePrediction: %x for device %p --> %lx\n", + enable, + fdoExtension->DeviceObject, + status)); + } else { + status = STATUS_INVALID_PARAMETER; + } + break; + } + + // + // void EnableDisableFailurePredictionPolling( + // [in] uint32 Period, + // [in] boolean Enable) + // + case EnableDisableFailurePredictionPolling: + { + BOOLEAN enable; + ULONG period; + + sizeNeeded = 0; + if (InBufferSize >= (sizeof(ULONG) + sizeof(BOOLEAN))) + { + period = *((PULONG)Buffer); + Buffer += sizeof(ULONG); + enable = *((PBOOLEAN)Buffer); + + status = DiskEnableDisableFailurePredictPolling( + fdoExtension, + enable, + period); + + DebugPrint((3, "DiskFdoWmiExecuteMethod: EnableDisableFailurePredictionPolling: %x %x for device %p --> %lx\n", + enable, + period, + fdoExtension->DeviceObject, + status)); + } else { + status = STATUS_INVALID_PARAMETER; + } + break; + } + + // + // void GetFailurePredictionCapability([out] uint32 Capability) + // + case GetFailurePredictionCapability: + { + sizeNeeded = sizeof(ULONG); + if (OutBufferSize >= sizeNeeded) + { + status = STATUS_SUCCESS; + *((PFAILURE_PREDICTION_METHOD)Buffer) = diskData->FailurePredictionCapability; + DebugPrint((3, "DiskFdoWmiExecuteMethod: GetFailurePredictionCapability: %x for device %p --> %lx\n", + *((PFAILURE_PREDICTION_METHOD)Buffer), + fdoExtension->DeviceObject, + status)); + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + + // + // void EnableOfflineDiags([out] boolean Success); + // + case EnableOfflineDiags: + { + sizeNeeded = sizeof(BOOLEAN); + if (OutBufferSize >= sizeNeeded) + { + if (diskData->FailurePredictionCapability == + FailurePredictionSmart) + { + // + // Initiate or resume offline diagnostics. + // This may cause a loss of performance + // to the disk, but mayincrease the amount + // of disk checking. + // + status = DiskExecuteSmartDiagnostics(fdoExtension, + 0); + + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + + *((PBOOLEAN)Buffer) = NT_SUCCESS(status); + + DebugPrint((3, "DiskFdoWmiExecuteMethod: EnableOfflineDiags for device %p --> %lx\n", + fdoExtension->DeviceObject, + status)); + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + + // + // void ReadLogSectors([in] uint8 LogAddress, + // [in] uint8 SectorCount, + // [out] uint32 Length, + // [out, WmiSizeIs("Length")] uint8 LogSectors[] + // ); + // + case ReadLogSectors: + { + sizeNeeded = 0; + if (diskData->FailurePredictionCapability == + FailurePredictionSmart) + { + if (InBufferSize >= sizeof(READ_LOG_SECTORS_IN)) + { + PREAD_LOG_SECTORS_IN inParams; + PREAD_LOG_SECTORS_OUT outParams; + ULONG readSize; + + inParams = (PREAD_LOG_SECTORS_IN)Buffer; + readSize = inParams->SectorCount * SMART_LOG_SECTOR_SIZE; + sizeNeeded = FIELD_OFFSET(READ_LOG_SECTORS_OUT, + LogSectors) + readSize; + + if (OutBufferSize >= sizeNeeded) + { + outParams = (PREAD_LOG_SECTORS_OUT)Buffer; + status = DiskReadSmartLog(fdoExtension, + inParams->SectorCount, + inParams->LogAddress, + outParams->LogSectors); + + if (NT_SUCCESS(status)) + { + outParams->Length = readSize; + } else { + // + // SMART command failure is + // indicated by successful + // execution, but no data returned + // + outParams->Length = 0; + status = STATUS_SUCCESS; + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + + } else { + status = STATUS_INVALID_PARAMETER; + } + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + // void WriteLogSectors([in] uint8 LogAddress, + // [in] uint8 SectorCount, + // [in] uint32 Length, + // [in, WmiSizeIs("Length")] uint8 LogSectors[], + // [out] boolean Success + // ); + case WriteLogSectors: + { + sizeNeeded = 0; + if (diskData->FailurePredictionCapability == + FailurePredictionSmart) + { + if (InBufferSize >= FIELD_OFFSET(WRITE_LOG_SECTORS_IN, + LogSectors)) + { + PWRITE_LOG_SECTORS_IN inParams; + PWRITE_LOG_SECTORS_OUT outParams; + ULONG writeSize; + + inParams = (PWRITE_LOG_SECTORS_IN)Buffer; + writeSize = inParams->SectorCount * SMART_LOG_SECTOR_SIZE; + if (InBufferSize >= (FIELD_OFFSET(WRITE_LOG_SECTORS_IN, + LogSectors) + + writeSize)) + { + sizeNeeded = sizeof(WRITE_LOG_SECTORS_OUT); + + if (OutBufferSize >= sizeNeeded) + { + outParams = (PWRITE_LOG_SECTORS_OUT)Buffer; + status = DiskWriteSmartLog(fdoExtension, + inParams->SectorCount, + inParams->LogAddress, + inParams->LogSectors); + + if (NT_SUCCESS(status)) + { + outParams->Success = TRUE; + } else { + outParams->Success = FALSE; + status = STATUS_SUCCESS; + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + } else { + status = STATUS_INVALID_PARAMETER; + } + } else { + status = STATUS_INVALID_PARAMETER; + } + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + // void ExecuteSelfTest([in] uint8 Subcommand, + // [out, + // Values{"0", "1", "2"}, + // ValueMap{"Successful Completion", + // "Captive Mode Required", + // "Unsuccessful Completion"} + // ] + // uint32 ReturnCode); + case ExecuteSelfTest: + { + sizeNeeded = 0; + if (diskData->FailurePredictionCapability == + FailurePredictionSmart) + { + if (InBufferSize >= sizeof(EXECUTE_SELF_TEST_IN)) + { + sizeNeeded = sizeof(EXECUTE_SELF_TEST_OUT); + if (OutBufferSize >= sizeNeeded) + { + PEXECUTE_SELF_TEST_IN inParam; + PEXECUTE_SELF_TEST_OUT outParam; + + inParam = (PEXECUTE_SELF_TEST_IN)Buffer; + outParam = (PEXECUTE_SELF_TEST_OUT)Buffer; + + if (DiskIsValidSmartSelfTest(inParam->Subcommand)) + { + status = DiskExecuteSmartDiagnostics(fdoExtension, + inParam->Subcommand); + if (NT_SUCCESS(status)) + { + // + // Return self test executed + // without a problem + // + outParam->ReturnCode = 0; + } else { + // + // Return Self test execution + // failed status + // + outParam->ReturnCode = 2; + status = STATUS_SUCCESS; + } + } else { + // + // If self test subcommand requires + // captive mode then return that + // status + // + outParam->ReturnCode = 1; + status = STATUS_SUCCESS; + } + + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + + } else { + status = STATUS_INVALID_PARAMETER; + } + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + + break; + } + + default : + { + sizeNeeded = 0; + status = STATUS_WMI_ITEMID_NOT_FOUND; + break; + } + } + + break; + } + + case DiskGeometryGuid: + case SmartStatusGuid: + case SmartDataGuid: + { + + sizeNeeded = 0; + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + default: + { + sizeNeeded = 0; + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + + DebugPrint((3, "Disk: DiskExecuteMethod Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + sizeNeeded, + IO_NO_INCREMENT); + + return status; +} + + +#if 0 +// +// Enable this to add WMI support for PDOs +NTSTATUS +DiskPdoQueryWmiRegInfo( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to retrieve the list of + guids or data blocks that the driver wants to register with WMI. This + routine may not pend or block. Driver should NOT call + ClassWmiCompleteRequest. + +Arguments: + + DeviceObject is the device whose data block is being queried + + *RegFlags returns with a set of flags that describe the guids being + registered for this device. If the device wants enable and disable + collection callbacks before receiving queries for the registered + guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the + returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case + the instance name is determined from the PDO associated with the + device object. Note that the PDO must have an associated devnode. If + WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique + name for the device. + + InstanceName returns with the instance name for the guids if + WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The + caller will call ExFreePool with the buffer returned. + + +Return Value: + + status + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION parentFunctionalExtension; + ANSI_STRING ansiString; + CHAR name[256]; + NTSTATUS status; + + // + // We need to pick a name for PDOs since they do not have a devnode + parentFunctionalExtension = commonExtension->PartitionZeroExtension; + sprintf(name, + "Disk(%d)_Partition(%d)_Start(%#I64x)_Length(%#I64x)", + parentFunctionalExtension->DeviceNumber, + commonExtension->PartitionNumber, + commonExtension->StartingOffset.QuadPart, + commonExtension->PartitionLength.QuadPart); + RtlInitAnsiString(&ansiString, + name); + + status = RtlAnsiStringToUnicodeString(InstanceName, + &ansiString, + TRUE); + + return status; +} + +NTSTATUS +DiskPdoQueryWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferAvail, + OUT PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + BufferAvail on has the maximum size available to write the data + block. + + Buffer on return is filled with the returned data block + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData); + ULONG sizeNeeded; + + DebugPrint((3, "Disk: DiskQueryWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n" + " BufferAvail %#x Buffer %p\n", + DeviceObject, Irp, + GuidIndex, BufferAvail, Buffer)); + + switch (GuidIndex) + { + case 0: + { + sizeNeeded = 4 * sizeof(ULONG); + if (BufferAvail >= sizeNeeded) + { + RtlCopyMemory(Buffer, DiskDummyData, sizeNeeded); + status = STATUS_SUCCESS; + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + + default: + { + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + + DebugPrint((3, "Disk: DiskQueryWmiDataBlock Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + sizeNeeded, + IO_NO_INCREMENT); + + return status; +} + +NTSTATUS +DiskPdoSetWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + BufferSize has the size of the data block passed + + Buffer has the new values for the data block + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + ULONG sizeNeeded; + + DebugPrint((3, "Disk: DiskSetWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n" + " BufferSize %#x Buffer %p\n", + DeviceObject, Irp, + GuidIndex, BufferSize, Buffer)); + + switch(GuidIndex) + { + case 0: + { + sizeNeeded = 4 * sizeof(ULONG); + if (BufferSize == sizeNeeded) + { + RtlCopyMemory(DiskDummyData, Buffer, sizeNeeded); + status = STATUS_SUCCESS; + } else { + status = STATUS_INFO_LENGTH_MISMATCH; + } + break; + } + + default: + { + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + + DebugPrint((3, "Disk: DiskSetWmiDataBlock Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + + return status; +} + +NTSTATUS +DiskPdoSetWmiDataItem( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG DataItemId, + IN ULONG BufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + DataItemId has the id of the data item being set + + BufferSize has the size of the data item passed + + Buffer has the new values for the data item + + +Return Value: + + status + +--*/ +{ + NTSTATUS status; + + DebugPrint((3, "Disk: DiskSetWmiDataItem, Device %p, Irp %p, GuiIndex %d, DataId %d\n" + " BufferSize %#x Buffer %p\n", + DeviceObject, Irp, + GuidIndex, DataItemId, BufferSize, Buffer)); + + switch(GuidIndex) + { + case 0: + { + if ((BufferSize == sizeof(ULONG)) && + (DataItemId <= 3)) + { + DiskDummyData[DataItemId] = *((PULONG)Buffer); + status = STATUS_SUCCESS; + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + default: + { + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + + + DebugPrint((3, "Disk: DiskSetWmiDataItem Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + + return status; +} + + +NTSTATUS +DiskPdoExecuteWmiMethod( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG MethodId, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to execute a method. When the + driver has finished filling the data block it must call + ClassWmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject is the device whose data block is being queried + + Irp is the Irp that makes this request + + GuidIndex is the index into the list of guids provided when the + device registered + + MethodId has the id of the method being called + + InBufferSize has the size of the data block passed in as the input to + the method. + + OutBufferSize on entry has the maximum size available to write the + returned data block. + + Buffer is filled with the returned data block + + +Return Value: + + status + +--*/ +{ + ULONG sizeNeeded = 4 * sizeof(ULONG); + NTSTATUS status; + ULONG tempData[4]; + + DebugPrint((3, "Disk: DiskExecuteWmiMethod, DeviceObject %p, Irp %p, Guid Id %d, MethodId %d\n" + " InBufferSize %#x, OutBufferSize %#x, Buffer %p\n", + DeviceObject, Irp, + GuidIndex, MethodId, InBufferSize, OutBufferSize, Buffer)); + + switch(GuidIndex) + { + case 0: + { + if (MethodId == 1) + { + if (OutBufferSize >= sizeNeeded) + { + + if (InBufferSize == sizeNeeded) + { + RtlCopyMemory(tempData, Buffer, sizeNeeded); + RtlCopyMemory(Buffer, DiskDummyData, sizeNeeded); + RtlCopyMemory(DiskDummyData, tempData, sizeNeeded); + + status = STATUS_SUCCESS; + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + } else { + status = STATUS_BUFFER_TOO_SMALL; + } + } else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + break; + } + + default: + { + status = STATUS_WMI_GUID_NOT_FOUND; + } + } + + DebugPrint((3, "Disk: DiskExecuteMethod Device %p, Irp %p returns %lx\n", + DeviceObject, Irp, status)); + + status = ClassWmiCompleteRequest(DeviceObject, + Irp, + status, + 0, + IO_NO_INCREMENT); + + return status; +} +#endif + + + diff --git a/reactos/drivers/storage/class/disk_new/enum.c b/reactos/drivers/storage/class/disk_new/enum.c new file mode 100644 index 00000000000..56febd7853d --- /dev/null +++ b/reactos/drivers/storage/class/disk_new/enum.c @@ -0,0 +1,1249 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + pnp.c + +Abstract: + + SCSI disk class driver + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "disk.h" + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, DiskConvertExtendedToLayout) +#pragma alloc_text(PAGE, DiskConvertPartitionToExtended) +#pragma alloc_text(PAGE, DiskConvertLayoutToExtended) +#pragma alloc_text(PAGE, DiskCreatePdo) +#pragma alloc_text(PAGE, DiskEnumerateDevice) +#pragma alloc_text(PAGE, DiskUpdateRemovablePartitions) +#pragma alloc_text(PAGE, DiskUpdatePartitions) +#pragma alloc_text(PAGE, DiskCreatePdo) + +#endif + +PDRIVE_LAYOUT_INFORMATION +DiskConvertExtendedToLayout( + IN CONST PDRIVE_LAYOUT_INFORMATION_EX LayoutEx + ) +{ + ULONG i; + ULONG LayoutSize; + PDRIVE_LAYOUT_INFORMATION Layout; + PPARTITION_INFORMATION Partition; + PPARTITION_INFORMATION_EX PartitionEx; + + PAGED_CODE (); + + ASSERT ( LayoutEx ); + + + // + // The only valid conversion is from an MBR extended layout structure to + // the old structure. + // + + if (LayoutEx->PartitionStyle != PARTITION_STYLE_MBR) { + ASSERT ( FALSE ); + return NULL; + } + + LayoutSize = FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]) + + LayoutEx->PartitionCount * sizeof (PARTITION_INFORMATION); + + Layout = ExAllocatePoolWithTag ( + NonPagedPool, + LayoutSize, + DISK_TAG_PART_LIST + ); + + if ( Layout == NULL ) { + return NULL; + } + + Layout->Signature = LayoutEx->Mbr.Signature; + Layout->PartitionCount = LayoutEx->PartitionCount; + + for (i = 0; i < LayoutEx->PartitionCount; i++) { + + Partition = &Layout->PartitionEntry[i]; + PartitionEx = &LayoutEx->PartitionEntry[i]; + + Partition->StartingOffset = PartitionEx->StartingOffset; + Partition->PartitionLength = PartitionEx->PartitionLength; + Partition->RewritePartition = PartitionEx->RewritePartition; + Partition->PartitionNumber = PartitionEx->PartitionNumber; + + Partition->PartitionType = PartitionEx->Mbr.PartitionType; + Partition->BootIndicator = PartitionEx->Mbr.BootIndicator; + Partition->RecognizedPartition = PartitionEx->Mbr.RecognizedPartition; + Partition->HiddenSectors = PartitionEx->Mbr.HiddenSectors; + } + + return Layout; +} + +VOID +DiskConvertPartitionToExtended( + IN PPARTITION_INFORMATION Partition, + OUT PPARTITION_INFORMATION_EX PartitionEx + ) + +/*++ + +Routine Description: + + Convert a PARTITION_INFORMATION structure to a PARTITION_INFORMATION_EX + structure. + +Arguments: + + Partition - A pointer to the PARTITION_INFORMATION structure to convert. + + PartitionEx - A pointer to a buffer where the converted + PARTITION_INFORMATION_EX structure is to be stored. + +Return Values: + + None. + +--*/ + +{ + PAGED_CODE (); + + ASSERT ( PartitionEx != NULL ); + ASSERT ( Partition != NULL ); + + PartitionEx->PartitionStyle = PARTITION_STYLE_MBR; + PartitionEx->StartingOffset = Partition->StartingOffset; + PartitionEx->PartitionLength = Partition->PartitionLength; + PartitionEx->RewritePartition = Partition->RewritePartition; + PartitionEx->PartitionNumber = Partition->PartitionNumber; + + PartitionEx->Mbr.PartitionType = Partition->PartitionType; + PartitionEx->Mbr.BootIndicator = Partition->BootIndicator; + PartitionEx->Mbr.RecognizedPartition = Partition->RecognizedPartition; + PartitionEx->Mbr.HiddenSectors = Partition->HiddenSectors; +} + + +PDRIVE_LAYOUT_INFORMATION_EX +DiskConvertLayoutToExtended( + IN CONST PDRIVE_LAYOUT_INFORMATION Layout + ) + +/*++ + +Routine Description: + + Convert a DRIVE_LAYOUT_INFORMATION structure into a + DRIVE_LAYOUT_INFORMATION_EX structure. + +Arguments: + + Layout - The source DRIVE_LAYOUT_INFORMATION structure. + +Return Values: + + The resultant DRIVE_LAYOUT_INFORMATION_EX structure. This buffer must + be freed by the callee using ExFreePool. + +--*/ + +{ + ULONG i; + ULONG size; + PDRIVE_LAYOUT_INFORMATION_EX layoutEx; + + PAGED_CODE (); + + ASSERT ( Layout != NULL ); + + + // + // Allocate enough space for a DRIVE_LAYOUT_INFORMATION_EX structure + // plus as many PARTITION_INFORMATION_EX structures as are in the + // source array. + // + + size = FIELD_OFFSET (DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]) + + Layout->PartitionCount * sizeof ( PARTITION_INFORMATION_EX ); + + layoutEx = ExAllocatePoolWithTag( + NonPagedPool, + size, + DISK_TAG_PART_LIST + ); + + if ( layoutEx == NULL ) { + return NULL; + } + + // + // Convert the disk information. + // + + layoutEx->PartitionStyle = PARTITION_STYLE_MBR; + layoutEx->PartitionCount = Layout->PartitionCount; + layoutEx->Mbr.Signature = Layout->Signature; + + for (i = 0; i < Layout->PartitionCount; i++) { + + // + // Convert each entry. + // + + DiskConvertPartitionToExtended ( + &Layout->PartitionEntry[i], + &layoutEx->PartitionEntry[i] + ); + } + + return layoutEx; +} + + + +NTSTATUS +DiskEnumerateDevice( + IN PDEVICE_OBJECT Fdo + ) + +/*++ + +Routine Description: + + This routine is called by the class driver to update the PDO list off + of this FDO. The disk driver also calls it internally to re-create + device objects. + + This routine will read the partition table and create new PDO objects as + necessary. PDO's that no longer exist will be pulled out of the PDO list + so that pnp will destroy them. + +Arguments: + + Fdo - a pointer to the FDO being re-enumerated + +Return Value: + + status + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL; + + PDISK_DATA diskData = (PDISK_DATA) commonExtension->DriverData; + + PDEVICE_OBJECT pdo = NULL; + + ULONG numberListElements = 0; + + PDRIVE_LAYOUT_INFORMATION_EX partitionList; + + NTSTATUS status; + + ASSERT(commonExtension->IsFdo); + + PAGED_CODE(); + + // + // Update our image of the size of the drive. This may be necessary if + // the drive size is extended or we just released a reservation to + // ensure the kernel doesn't reject the partition table. + // + + DiskReadDriveCapacity(Fdo); + + // + // Lock out anyone else trying to repartition the disk. + // + + DiskAcquirePartitioningLock(fdoExtension); + + // + // Create objects for all the partitions on the device. + // + + status = DiskReadPartitionTableEx(fdoExtension, FALSE, &partitionList); + + // + // If the I/O read partition table failed and this is a removable device, + // then fix up the partition list to make it look like there is one + // zero length partition. + // + + if ((!NT_SUCCESS(status) || partitionList->PartitionCount == 0) && + Fdo->Characteristics & FILE_REMOVABLE_MEDIA) { + + SIZE_T partitionListSize; + + // + // Remember whether the drive is ready. + // + + diskData->ReadyStatus = status; + + // + // Allocate and zero a partition list. + // + + partitionListSize = + FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]); + + partitionList = ExAllocatePoolWithTag(NonPagedPool, + partitionListSize, + DISK_TAG_PART_LIST); + + if (partitionList != NULL) { + + RtlZeroMemory( partitionList, partitionListSize ); + + // + // Set the partition count to one and the status to success + // so one device object will be created. Set the partition type + // to a bogus value. + // + + partitionList->PartitionStyle = PARTITION_STYLE_MBR; + partitionList->PartitionCount = 1; + + status = STATUS_SUCCESS; + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(status)) { + + diskData->UpdatePartitionRoutine(Fdo, partitionList); + + // + // Record disk signature. + // + + if (partitionList->PartitionStyle == PARTITION_STYLE_MBR) { + + diskData->PartitionStyle = PARTITION_STYLE_MBR; + diskData->Mbr.Signature = partitionList->Mbr.Signature; + + } else { + + diskData->PartitionStyle = PARTITION_STYLE_GPT; + diskData->Efi.DiskId = partitionList->Gpt.DiskId; + } + } + + DiskReleasePartitioningLock(fdoExtension); + + return(STATUS_SUCCESS); + +} // end DiskEnumerateDevice() + + +VOID +DiskUpdateRemovablePartitions( + IN PDEVICE_OBJECT Fdo, + IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList + ) + +/*++ + +Routine Description: + + This routine is called by the class DLL to update the PDO list off of this + FDO. The disk driver also calls it internally to re-create device objects. + + This routine will read the partition table and update the size of the + single partition device object which always exists for removable devices. + +Arguments: + + Fdo - a pointer to the FDO being reenumerated. + +Return Value: + + status + +--*/ + +{ + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL; + + ULONG partitionCount; + + ULONG partitionNumber; + ULONG partitionOrdinal = 0; + ULONG newPartitionNumber; + + PDISK_DATA pdoData; + NTSTATUS status; + + PPARTITION_INFORMATION_EX partitionEntry; + PARTITION_STYLE partitionStyle; + + PAGED_CODE(); + + ASSERT(Fdo->Characteristics & FILE_REMOVABLE_MEDIA); + + partitionStyle = PartitionList->PartitionStyle; + partitionCount = PartitionList->PartitionCount; + + for(partitionNumber = 0; + partitionNumber < partitionCount; + partitionNumber++) { + + partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); + + partitionEntry->PartitionNumber = 0; + } + + // + // Get exclusive access to the child list while repartitioning. + // + + ClassAcquireChildLock(fdoExtension); + + // + // Removable media should never have more than one PDO. + // + + pdoExtension = fdoExtension->CommonExtension.ChildList; + + if(pdoExtension == NULL) { + + PARTITION_INFORMATION_EX tmpPartitionEntry; + PDEVICE_OBJECT pdo; + + // + // There is no PDO currently. Create one and pre-initialize it with + // a zero length. + // + + RtlZeroMemory(&tmpPartitionEntry, sizeof(tmpPartitionEntry)); + + tmpPartitionEntry.PartitionNumber = 1; + + DebugPrint((1, "DiskUpdateRemovablePartitions: Creating RM partition\n")); + + status = DiskCreatePdo(Fdo, + 0, + &tmpPartitionEntry, + partitionStyle, + &pdo); + + if(!NT_SUCCESS(status)) { + + DebugPrint((1, "DiskUpdateRemovablePartitions: error %lx creating " + "new PDO for RM partition\n", + status)); + + ClassReleaseChildLock(fdoExtension); + return; + } + + // + // mark the new device as enumerated + // + + pdoExtension = pdo->DeviceExtension; + pdoExtension->IsMissing = FALSE; + + } + + pdoData = pdoExtension->CommonExtension.DriverData; + + // + // Search the partition list for a valid entry. We're looking for a + // primary partition since we only support the one. + // + + for(partitionNumber = 0; + partitionNumber < partitionCount; + partitionNumber++) { + + partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); + + + // + // Is this partition interesting? + // + + if (partitionStyle == PARTITION_STYLE_MBR) { + + if(partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED || + IsContainerPartition(partitionEntry->Mbr.PartitionType)) { + + continue; + } + } + + partitionOrdinal++; + + // + // We have found the first and thus only partition allowed on + // this disk. Update the information in the PDO to match the new + // partition. + // + DebugPrint((1, "DiskUpdateRemovablePartitions: Matched %wZ to #%d, " + "ordinal %d\n", + &pdoExtension->CommonExtension.DeviceName, + partitionEntry->PartitionNumber, + partitionOrdinal)); + + + partitionEntry->PartitionNumber = 1; + + pdoData->PartitionStyle = partitionStyle; + pdoData->PartitionOrdinal = partitionOrdinal; + ASSERT(partitionEntry->PartitionLength.LowPart != 0x23456789); + + pdoExtension->CommonExtension.StartingOffset = + partitionEntry->StartingOffset; + + pdoExtension->CommonExtension.PartitionLength = + partitionEntry->PartitionLength; + + + if (partitionStyle == PARTITION_STYLE_MBR) { + + pdoData->Mbr.HiddenSectors = partitionEntry->Mbr.HiddenSectors; + pdoData->Mbr.BootIndicator = partitionEntry->Mbr.BootIndicator; + + + // + // If this partition is being re-written then update the type + // information as well + // + + if (partitionEntry->RewritePartition) { + pdoData->Mbr.PartitionType = partitionEntry->Mbr.PartitionType; + } + + } else { + + pdoData->Efi.PartitionType = partitionEntry->Gpt.PartitionType; + pdoData->Efi.PartitionId = partitionEntry->Gpt.PartitionId; + pdoData->Efi.Attributes = partitionEntry->Gpt.Attributes; + + RtlCopyMemory( + pdoData->Efi.PartitionName, + partitionEntry->Gpt.Name, + sizeof (pdoData->Efi.PartitionName) + ); + } + + // + // Mark this one as found + // + + pdoExtension->IsMissing = FALSE; + ClassReleaseChildLock(fdoExtension); + return; + } + + // + // No interesting partition was found. + // + + if (partitionStyle == PARTITION_STYLE_MBR) { + + pdoData->Mbr.HiddenSectors = 0; + pdoData->Mbr.PartitionType = PARTITION_ENTRY_UNUSED; + + } else { + + RtlZeroMemory (&pdoData->Efi, + sizeof (pdoData->Efi) + ); + } + + pdoExtension->CommonExtension.StartingOffset.QuadPart = 0; + pdoExtension->CommonExtension.PartitionLength.QuadPart = 0; + + ClassReleaseChildLock(fdoExtension); + return; +} + + +VOID +DiskUpdatePartitions( + IN PDEVICE_OBJECT Fdo, + IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList + ) + +/*++ + +Routine Description: + + This routine will synchronize the information held in the partition list + with the device objects hanging off this Fdo. Any new partition objects + will be created, any non-existant ones will be marked as un-enumerated. + + This will be done in several stages: + + * Clear state (partition number) from every entry in the partition + list + + * Set IsMissing flag on every child of this FDO + + * For each child of the FDO: + if a matching partition exists in the partition list, + update the partition number in the table, update the + ordinal in the object and mark the object as enumerated + + * For each un-enumerated device object + zero out the partition information to invalidate the device + delete the symbolic link if any + + * For each un-matched entry in the partition list: + create a new partition object + update the partition number in the list entry + create a new symbolic link if necessary + +Arguments: + + Fdo - a pointer to the functional device object this partition list is for + + PartitionList - a pointer to the partition list being updated + +Return Value: + + none + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PPHYSICAL_DEVICE_EXTENSION oldChildList = NULL; + PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL; + + ULONG partitionCount; + + ULONG partitionNumber; + ULONG partitionOrdinal; + ULONG newPartitionNumber; + + PPARTITION_INFORMATION_EX partitionEntry = NULL; + PDISK_DATA pdoData; + PARTITION_STYLE partitionStyle; + + NTSTATUS status; + + PAGED_CODE(); + + // + // Get exclusive access to the child list. + // + + ClassAcquireChildLock(fdoExtension); + + partitionStyle = PartitionList->PartitionStyle; + + partitionCount = PartitionList->PartitionCount; + + // + // Pull all the child device objects off the children list. We'll + // add them back later. + // + + oldChildList = fdoExtension->CommonExtension.ChildList; + fdoExtension->CommonExtension.ChildList = NULL; + + // + // Clear the partition numbers from the list entries + // + + for(partitionNumber = 0; + partitionNumber < partitionCount; + partitionNumber++) { + + partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); + partitionEntry->PartitionNumber = 0; + } + + // + // Now match each child partition to it's entry (if any) in the partition + // list. + // + + while(oldChildList != NULL) { + + pdoExtension = oldChildList; + pdoData = pdoExtension->CommonExtension.DriverData; + + // + // Check all partition entries for a match on offset and length + // + + partitionOrdinal = 0; + + for(partitionNumber = 0; + partitionNumber < partitionCount; + partitionNumber++) { + + partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); + + // + // Is this an interesting partition entry? + // + + if (partitionStyle == PARTITION_STYLE_MBR) { + + if((partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED) || + (IsContainerPartition(partitionEntry->Mbr.PartitionType))) { + + continue; + } + } + + partitionOrdinal++; + + if(partitionEntry->PartitionNumber) { + + // + // This partition has already been found - skip it + // + + continue; + } + + // + // Let's see if the partition information matches + // + + if(partitionEntry->StartingOffset.QuadPart != + pdoExtension->CommonExtension.StartingOffset.QuadPart) { + continue; + } + + if(partitionEntry->PartitionLength.QuadPart != + pdoExtension->CommonExtension.PartitionLength.QuadPart) { + continue; + } + + // + // Yep - it matches. Update the information in the entry + // + + partitionEntry->PartitionNumber = pdoExtension->CommonExtension.PartitionNumber; + + if (partitionStyle == PARTITION_STYLE_MBR) { + + pdoData->Mbr.HiddenSectors = partitionEntry->Mbr.HiddenSectors; + + } + + break; + } + + if(partitionNumber != partitionCount) { + + DebugPrint((1, "DiskUpdatePartitions: Matched %wZ to #%d, ordinal " + "%d\n", + &pdoExtension->CommonExtension.DeviceName, + partitionEntry->PartitionNumber, + partitionOrdinal)); + + ASSERT(partitionEntry->PartitionLength.LowPart != 0x23456789); + // ASSERT(pdoExtension->CommonExtension.PartitionLength.QuadPart != 0); + + pdoData->PartitionStyle = partitionStyle; + + // + // we found a match - update the information in the device object + // extension and driverdata + // + + pdoData->PartitionOrdinal = partitionOrdinal; + + // + // If this partition is being re-written then update the type + // information as well + // + + + if (partitionStyle == PARTITION_STYLE_MBR) { + + if(partitionEntry->RewritePartition) { + pdoData->Mbr.PartitionType = partitionEntry->Mbr.PartitionType; + } + + } else { + + DebugPrint((1, "DiskUpdatePartitions: EFI Partition %ws\n", + pdoData->Efi.PartitionName + )); + + pdoData->Efi.PartitionType = partitionEntry->Gpt.PartitionType; + pdoData->Efi.PartitionId = partitionEntry->Gpt.PartitionId; + pdoData->Efi.Attributes = partitionEntry->Gpt.Attributes; + + RtlCopyMemory( + pdoData->Efi.PartitionName, + partitionEntry->Gpt.Name, + sizeof (pdoData->Efi.PartitionName) + ); + } + + // + // Mark this one as found. + // + + pdoExtension->IsMissing = FALSE; + + // + // Pull it out of the old child list and add it into the + // real one. + // + + oldChildList = pdoExtension->CommonExtension.ChildList; + + pdoExtension->CommonExtension.ChildList = + fdoExtension->CommonExtension.ChildList; + + fdoExtension->CommonExtension.ChildList = pdoExtension; + + } else { + + PDEVICE_OBJECT nextPdo; + + DebugPrint ((1, "DiskUpdatePartitions: Deleting %wZ\n", + &pdoExtension->CommonExtension.DeviceName)); + + if (partitionStyle == PARTITION_STYLE_GPT) { + + DebugPrint ((1, "DiskUpdatePartitions: EFI Partition %ws\n", + pdoData->Efi.PartitionName + )); + } + // + // no matching entry in the partition list - throw this partition + // object away + // + + pdoExtension->CommonExtension.PartitionLength.QuadPart = 0; + + // + // grab a pointer to the next child before we mark this one as + // missing since missing devices could vanish at any time. + // + + oldChildList = pdoExtension->CommonExtension.ChildList; + pdoExtension->CommonExtension.ChildList = (PVOID) -1; + + // + // Now tell the class driver that this child is "missing" - this + // will cause it to be deleted. + // + + + ClassMarkChildMissing(pdoExtension, FALSE); + } + } + + // + // At this point the old child list had best be empty. + // + + ASSERT(oldChildList == NULL); + + // + // Iterate through the partition entries and create any partition + // objects that don't already exist + // + + partitionOrdinal = 0; + newPartitionNumber = 0; + + for(partitionNumber = 0; + partitionNumber < partitionCount; + partitionNumber++) { + + PDEVICE_OBJECT pdo; + + partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]); + + // + // Is this partition interesting + // + + if (partitionStyle == PARTITION_STYLE_MBR) { + + if((partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED) || + (IsContainerPartition(partitionEntry->Mbr.PartitionType))) { + + continue; + } + } + + // + // Increment the count of interesting partitions + // + + partitionOrdinal++; + newPartitionNumber++; + + // + // Has this already been matched + // + + if(partitionEntry->PartitionNumber == 0) { + + LONG i; + + // + // find the first safe partition number for this device + // + + for(i = 0; i < (LONG) partitionCount; i++) { + + + PPARTITION_INFORMATION_EX tmp = &(PartitionList->PartitionEntry[i]); + + if (partitionStyle == PARTITION_STYLE_MBR) { + if (tmp->Mbr.PartitionType == PARTITION_ENTRY_UNUSED || + IsContainerPartition(tmp->Mbr.PartitionType)) { + continue; + } + } + + if(tmp->PartitionNumber == newPartitionNumber) { + + // + // Found a matching partition number - increment the count + // and restart the scan. + // + + newPartitionNumber++; + i = -1; + continue; + } + } + + // + // Assign this partition a partition number + // + + partitionEntry->PartitionNumber = newPartitionNumber; + + DebugPrint((1, "DiskUpdatePartitions: Found new partition #%d, ord %d " + "starting at %#016I64x and running for %#016I64x\n", + partitionEntry->PartitionNumber, + partitionOrdinal, + partitionEntry->StartingOffset.QuadPart, + partitionEntry->PartitionLength.QuadPart)); + + ClassReleaseChildLock(fdoExtension); + + status = DiskCreatePdo(Fdo, + partitionOrdinal, + partitionEntry, + partitionStyle, + &pdo); + + ClassAcquireChildLock(fdoExtension); + + if(!NT_SUCCESS(status)) { + + DebugPrint((1, "DiskUpdatePartitions: error %lx creating " + "new PDO for partition ordinal %d, number %d\n", + status, + partitionOrdinal, + partitionEntry->PartitionNumber)); + + // + // don't increment the partition number - we'll try to reuse + // it for the next child. + // + + partitionEntry->PartitionNumber = 0; + newPartitionNumber--; + + continue; + } + + // + // mark the new device as enumerated + // + + pdoExtension = pdo->DeviceExtension; + pdoExtension->IsMissing = FALSE; + + // + // This number's taken already - try to scanning the partition + // table more than once for a new number. + // + + } + } + + // + // ISSUE - 2000/02/09 - math: Review. + // Is PartitionStyle the only field that needs updating? + // + + { + PCOMMON_DEVICE_EXTENSION commonExtension; + PDISK_DATA diskData; + + commonExtension = Fdo->DeviceExtension; + diskData = (PDISK_DATA)(commonExtension->DriverData); + + diskData->PartitionStyle = partitionStyle; + } + + ClassReleaseChildLock(fdoExtension); + return; +} + + +NTSTATUS +DiskCreatePdo( + IN PDEVICE_OBJECT Fdo, + IN ULONG PartitionOrdinal, + IN PPARTITION_INFORMATION_EX PartitionEntry, + IN PARTITION_STYLE PartitionStyle, + OUT PDEVICE_OBJECT *Pdo + ) + +/*++ + +Routine Description: + + This routine will create and initialize a new partition device object + (PDO) and insert it into the FDO partition list. + +Arguments: + + Fdo - a pointer to the functional device object this PDO will be a child + of + + PartitionOrdinal - the partition ordinal for this PDO + + PartitionEntry - the partition information for this device object + + PartitionStyle - what style of partition table entry PartitionEntry is; + currently either MBR or EFI + + Pdo - a location to store the pdo pointer upon successful completion + +Return Value: + + status + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PDEVICE_OBJECT pdo = NULL; + PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL; + + PUCHAR deviceName = NULL; + + PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData; + + ULONG numberListElements; + + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // Create partition object and set up partition parameters. + // + + status = DiskGenerateDeviceName(FALSE, + fdoExtension->DeviceNumber, + PartitionEntry->PartitionNumber, + &PartitionEntry->StartingOffset, + &PartitionEntry->PartitionLength, + &deviceName); + + if(!NT_SUCCESS(status)) { + + DebugPrint((1, "DiskCreatePdo - Can't generate name %lx\n", status)); + return status; + } + + DebugPrint((2, "DiskCreatePdo: Create device object %s\n", deviceName)); + + status = ClassCreateDeviceObject(Fdo->DriverObject, + deviceName, + Fdo, + FALSE, + &pdo); + + if (!NT_SUCCESS(status)) { + + DebugPrint((1, "DiskEnumerateDevice: Can't create device object for %s\n", deviceName)); + + return status; + } + + // + // Set up device extension fields. + // + + pdoExtension = pdo->DeviceExtension; + + // + // Set up device object fields. + // + + SET_FLAG(pdo->Flags, DO_DIRECT_IO); + + pdo->StackSize = (CCHAR) + pdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1; + + // + // Get pointer to new disk data. + // + + diskData = (PDISK_DATA) pdoExtension->CommonExtension.DriverData; + + // + // Set the alignment requirements for the device based on the + // host adapter requirements + // + + if (Fdo->AlignmentRequirement > pdo->AlignmentRequirement) { + pdo->AlignmentRequirement = Fdo->AlignmentRequirement; + } + + if (fdoExtension->SrbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) { + numberListElements = 30; + } else { + numberListElements = 8; + } + + // + // Build the lookaside list for srb's for this partition based on + // whether the adapter and disk can do tagged queueing. Don't bother to + // check the status - this can't fail when called for a PDO. + // + + ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION) pdoExtension, + numberListElements); + + // + // Set the sense-data pointer in the device extension. + // + + diskData->PartitionOrdinal = PartitionOrdinal; + pdoExtension->CommonExtension.PartitionNumber = PartitionEntry->PartitionNumber; + + // + // Initialize relevant data. + // + + if (PartitionStyle == PARTITION_STYLE_MBR) { + + diskData->Mbr.PartitionType = PartitionEntry->Mbr.PartitionType; + diskData->Mbr.BootIndicator = PartitionEntry->Mbr.BootIndicator; + diskData->Mbr.HiddenSectors = PartitionEntry->Mbr.HiddenSectors; + + } else { + + diskData->Efi.PartitionType = PartitionEntry->Gpt.PartitionType; + diskData->Efi.PartitionId = PartitionEntry->Gpt.PartitionType; + diskData->Efi.Attributes = PartitionEntry->Gpt.Attributes; + RtlCopyMemory (diskData->Efi.PartitionName, + PartitionEntry->Gpt.Name, + sizeof (diskData->Efi.PartitionName) + ); + } + + DebugPrint((2, "DiskEnumerateDevice: Partition type is %x\n", + diskData->Mbr.PartitionType)); + + pdoExtension->CommonExtension.StartingOffset = + PartitionEntry->StartingOffset; + + pdoExtension->CommonExtension.PartitionLength = + PartitionEntry->PartitionLength; + + + DebugPrint((1, "DiskCreatePdo: hidden sectors value for pdo %#p set to %#x\n", + pdo, + diskData->Mbr.HiddenSectors)); + + // + // Check for removable media support. + // + + if (fdoExtension->DeviceDescriptor->RemovableMedia) { + SET_FLAG(pdo->Characteristics, FILE_REMOVABLE_MEDIA); + } + + pdoExtension->CommonExtension.DeviceObject = pdo; + + CLEAR_FLAG(pdo->Flags, DO_DEVICE_INITIALIZING); + + *Pdo = pdo; + + return status; +} + + + + + +VOID +DiskAcquirePartitioningLock( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData; + + PAGED_CODE(); + + ASSERT_FDO(FdoExtension->DeviceObject); + + KeWaitForSingleObject(&(diskData->PartitioningEvent), + UserRequest, + UserMode, + FALSE, + NULL); + return; +} + + +VOID +DiskReleasePartitioningLock( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +{ + PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData; + + PAGED_CODE(); + + ASSERT_FDO(FdoExtension->DeviceObject); + + KeSetEvent(&(diskData->PartitioningEvent), IO_NO_INCREMENT, FALSE); + return; +} + diff --git a/reactos/drivers/storage/class/disk_new/geometry.c b/reactos/drivers/storage/class/disk_new/geometry.c new file mode 100644 index 00000000000..9495e36eedf --- /dev/null +++ b/reactos/drivers/storage/class/disk_new/geometry.c @@ -0,0 +1,1470 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + geometry.c + +Abstract: + + SCSI disk class driver - this module contains all the code for generating + disk geometries. + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + + +#include "disk.h" +#include "ntddstor.h" + +#if defined (_X86_) + +DISK_GEOMETRY_SOURCE +DiskUpdateGeometry( + IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension + ); + +NTSTATUS +DiskUpdateRemovableGeometry ( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ); + +VOID +DiskScanBusDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE BusKey + ); + +NTSTATUS +DiskSaveBusDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE TargetKey, + IN ULONG DiskNumber + ); + +NTSTATUS +DiskSaveGeometryDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE HardwareKey + ); + +NTSTATUS +DiskGetPortGeometry( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PDISK_GEOMETRY Geometry + ); + +typedef struct _DISK_DETECT_INFO { + BOOLEAN Initialized; + ULONG Style; + ULONG Signature; + ULONG MbrCheckSum; + PDEVICE_OBJECT Device; + CM_INT13_DRIVE_PARAMETER DriveParameters; +} DISK_DETECT_INFO, *PDISK_DETECT_INFO; + +// +// Information about the disk geometries collected and saved into the registry +// by NTDETECT.COM or the system firmware. +// + +PDISK_DETECT_INFO DetectInfoList = NULL; +ULONG DetectInfoCount = 0; +ULONG DetectInfoUsedCount = 0; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DiskSaveDetectInfo) +#pragma alloc_text(INIT, DiskScanBusDetectInfo) +#pragma alloc_text(INIT, DiskSaveBusDetectInfo) +#pragma alloc_text(INIT, DiskSaveGeometryDetectInfo) + +#pragma alloc_text(PAGE, DiskUpdateGeometry) +#pragma alloc_text(PAGE, DiskUpdateRemovableGeometry) +#pragma alloc_text(PAGE, DiskGetPortGeometry) +#pragma alloc_text(PAGE, DiskGetDetectInfo) +#pragma alloc_text(PAGE, DiskReadSignature) +#endif + + + +NTSTATUS +DiskSaveDetectInfo( + PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine saves away the firmware information about the disks which has + been saved in the registry. It generates a list (DetectInfoList) which + contains the disk geometries, signatures & checksums of all drives which + were examined by NtDetect. This list is later used to assign geometries + to disks as they are initialized. + +Arguments: + + DriverObject - the driver being initialized. This is used to get to the + hardware database. + +Return Value: + + status. + +--*/ + +{ + OBJECT_ATTRIBUTES objectAttributes; + HANDLE hardwareKey; + + UNICODE_STRING unicodeString; + HANDLE busKey; + + NTSTATUS status; + + PAGED_CODE(); + + InitializeObjectAttributes( + &objectAttributes, + DriverObject->HardwareDatabase, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + // + // Create the hardware base key. + // + + status = ZwOpenKey(&hardwareKey, KEY_READ, &objectAttributes); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveDetectInfo: Cannot open hardware data. " + "Name: %wZ\n", + DriverObject->HardwareDatabase)); + return status; + } + + status = DiskSaveGeometryDetectInfo(DriverObject, hardwareKey); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveDetectInfo: Can't query configuration data " + "(%#08lx)\n", + status)); + ZwClose(hardwareKey); + return status; + } + + // + // Open EISA bus key. + // + + RtlInitUnicodeString(&unicodeString, L"EisaAdapter"); + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + hardwareKey, + NULL); + + status = ZwOpenKey(&busKey, + KEY_READ, + &objectAttributes); + + if(NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveDetectInfo: Opened EisaAdapter key\n")); + DiskScanBusDetectInfo(DriverObject, busKey); + ZwClose(busKey); + } + + // + // Open MultiFunction bus key. + // + + RtlInitUnicodeString(&unicodeString, L"MultifunctionAdapter"); + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + hardwareKey, + NULL); + + status = ZwOpenKey(&busKey, + KEY_READ, + &objectAttributes); + + if(NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveDetectInfo: Opened MultifunctionAdapter key\n")); + DiskScanBusDetectInfo(DriverObject, busKey); + ZwClose(busKey); + } + + ZwClose(hardwareKey); + + return STATUS_SUCCESS; +} + + +VOID +DiskCleanupDetectInfo( + IN PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine will cleanup the data structure built by DiskSaveDetectInfo. + +Arguments: + + DriverObject - a pointer to the kernel object for this driver. + +Return Value: + + none + +--*/ + +{ + if(DetectInfoList != NULL) { + ExFreePool(DetectInfoList); + DetectInfoList = NULL; + } + return; +} + + +NTSTATUS +DiskSaveGeometryDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE HardwareKey + ) +{ + UNICODE_STRING unicodeString; + PKEY_VALUE_FULL_INFORMATION keyData; + ULONG length; + + PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor; + PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor; + + PCM_INT13_DRIVE_PARAMETER driveParameters; + ULONG numberOfDrives; + + ULONG i; + + NTSTATUS status; + + PAGED_CODE(); + + // + // Get disk BIOS geometry information. + // + + RtlInitUnicodeString(&unicodeString, L"Configuration Data"); + + keyData = ExAllocatePoolWithTag(PagedPool, + VALUE_BUFFER_SIZE, + DISK_TAG_UPDATE_GEOM); + + if(keyData == NULL) { + DebugPrint((1, "DiskSaveGeometryDetectInfo: Can't allocate config " + "data buffer\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + status = ZwQueryValueKey(HardwareKey, + &unicodeString, + KeyValueFullInformation, + keyData, + VALUE_BUFFER_SIZE, + &length); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveGeometryDetectInfo: Can't query configuration " + "data (%#08lx)\n", + status)); + ExFreePool(keyData); + return status; + } + + // + // Extract the resource list out of the key data. + // + + fullDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR) + (((PUCHAR) keyData) + keyData->DataOffset); + partialDescriptor = + fullDescriptor->PartialResourceList.PartialDescriptors; + length = partialDescriptor->u.DeviceSpecificData.DataSize; + + if((keyData->DataLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR)) || + (fullDescriptor->PartialResourceList.Count == 0) || + (partialDescriptor->Type != CmResourceTypeDeviceSpecific) || + (length < sizeof(ULONG))) { + + DebugPrint((1, "DiskSaveGeometryDetectInfo: BIOS header data too small " + "or invalid\n")); + ExFreePool(keyData); + return STATUS_INVALID_PARAMETER; + } + + // + // Point to the BIOS data. THe BIOS data is located after the first + // partial Resource list which should be device specific data. + // + + { + PUCHAR buffer = (PUCHAR) keyData; + buffer += keyData->DataOffset; + buffer += sizeof(CM_FULL_RESOURCE_DESCRIPTOR); + driveParameters = (PCM_INT13_DRIVE_PARAMETER) buffer; + } + + numberOfDrives = length / sizeof(CM_INT13_DRIVE_PARAMETER); + + // + // Allocate our detect info list now that we know how many entries there + // are going to be. No other routine allocates detect info and this is + // done out of DriverEntry so we don't need to synchronize it's creation. + // + + length = sizeof(DISK_DETECT_INFO) * numberOfDrives; + DetectInfoList = ExAllocatePoolWithTag(PagedPool, + length, + DISK_TAG_UPDATE_GEOM); + + if(DetectInfoList == NULL) { + DebugPrint((1, "DiskSaveGeometryDetectInfo: Couldn't allocate %x bytes " + "for DetectInfoList\n", + length)); + + ExFreePool(keyData); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DetectInfoCount = numberOfDrives; + + RtlZeroMemory(DetectInfoList, length); + + // + // Copy the information out of the key data and into the list we've + // allocated. + // + + for(i = 0; i < numberOfDrives; i++) { + DetectInfoList[i].DriveParameters = driveParameters[i]; + } + + ExFreePool(keyData); + return STATUS_SUCCESS; +} + + +VOID +DiskScanBusDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE BusKey + ) +/*++ + +Routine Description: + + The routine queries the registry to determine which disks are visible to + the BIOS. If a disk is visable to the BIOS then the geometry information + is updated with the disk's signature and MBR checksum. + +Arguments: + + DriverObject - the object for this driver. + BusKey - handle to the bus key to be enumerated. + +Return Value: + + status + +--*/ +{ + ULONG busNumber; + + NTSTATUS status; + + for(busNumber = 0; ; busNumber++) { + + WCHAR buffer[32]; + UNICODE_STRING unicodeString; + + OBJECT_ATTRIBUTES objectAttributes; + + HANDLE spareKey; + HANDLE adapterKey; + + ULONG adapterNumber; + + DebugPrint((1, "DiskScanBusDetectInfo: Scanning bus %d\n", busNumber)); + + // + // Open controller name key. + // + + swprintf(buffer, L"%d", busNumber); + RtlInitUnicodeString(&unicodeString, buffer); + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + BusKey, + NULL); + + status = ZwOpenKey(&spareKey, KEY_READ, &objectAttributes); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx opening bus " + "key %#x\n", + status, busNumber)); + break; + } + + // + // Open up a controller ordinal key. + // + + RtlInitUnicodeString(&unicodeString, L"DiskController"); + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + spareKey, + NULL); + + status = ZwOpenKey(&adapterKey, KEY_READ, &objectAttributes); + ZwClose(spareKey); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx opening " + "DiskController key\n", + status)); + continue; + } + + for(adapterNumber = 0; ; adapterNumber++) { + + HANDLE diskKey; + ULONG diskNumber; + + // + // Open disk key. + // + + DebugPrint((1, "DiskScanBusDetectInfo: Scanning disk key " + "%d\\DiskController\\%d\\DiskPeripheral\n", + busNumber, adapterNumber)); + + swprintf(buffer, L"%d\\DiskPeripheral", adapterNumber); + RtlInitUnicodeString(&unicodeString, buffer); + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + adapterKey, + NULL); + + status = ZwOpenKey(&diskKey, KEY_READ, &objectAttributes); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx opening " + "disk key\n", + status)); + break; + } + + for(diskNumber = 0; ; diskNumber++) { + + HANDLE targetKey; + + DebugPrint((1, "DiskScanBusDetectInfo: Scanning target key " + "%d\\DiskController\\%d\\DiskPeripheral\\%d\n", + busNumber, adapterNumber, diskNumber)); + + swprintf(buffer, L"%d", diskNumber); + RtlInitUnicodeString(&unicodeString, buffer); + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + diskKey, + NULL); + + status = ZwOpenKey(&targetKey, KEY_READ, &objectAttributes); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx " + "opening target key\n", + status)); + break; + } + + status = DiskSaveBusDetectInfo(DriverObject, + targetKey, + diskNumber); + + ZwClose(targetKey); + } + + ZwClose(diskKey); + } + ZwClose(adapterKey); + } + return; +} + + +NTSTATUS +DiskSaveBusDetectInfo( + IN PDRIVER_OBJECT DriverObject, + IN HANDLE TargetKey, + IN ULONG DiskNumber + ) +/*++ + +Routine Description: + + This routine will transfer the firmware/ntdetect reported information + in the specified target key into the appropriate entry in the + DetectInfoList. + +Arguments: + + DriverObject - the object for this driver. + + TargetKey - the key for the disk being saved. + + DiskNumber - the ordinal of the entry in the DiskPeripheral tree for this + entry + +Return Value: + + status + +--*/ +{ + PDISK_DETECT_INFO diskInfo; + + UNICODE_STRING unicodeString; + + PKEY_VALUE_FULL_INFORMATION keyData; + ULONG length; + + NTSTATUS status; + + PAGED_CODE(); + + diskInfo = &(DetectInfoList[DiskNumber]); + + if(diskInfo->Initialized) { + + ASSERT(FALSE); + DebugPrint((1, "DiskSaveBusDetectInfo: disk entry %#x already has a " + "signature of %#08lx and mbr checksum of %#08lx\n", + DiskNumber, + diskInfo->Signature, + diskInfo->MbrCheckSum)); + return STATUS_UNSUCCESSFUL; + } + + RtlInitUnicodeString(&unicodeString, L"Identifier"); + + keyData = ExAllocatePoolWithTag(PagedPool, + VALUE_BUFFER_SIZE, + DISK_TAG_UPDATE_GEOM); + + if(keyData == NULL) { + DebugPrint((1, "DiskSaveBusDetectInfo: Couldn't allocate space for " + "registry data\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Get disk peripheral identifier. + // + + status = ZwQueryValueKey(TargetKey, + &unicodeString, + KeyValueFullInformation, + keyData, + VALUE_BUFFER_SIZE, + &length); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveBusDetectInfo: Error %#08lx getting " + "Identifier\n", + status)); + ExFreePool(keyData); + return status; + + } else if (keyData->DataLength < 9*sizeof(WCHAR)) { + + // + // the data is too short to use (we subtract 9 chars in normal path) + // + DebugPrint((1, "DiskSaveBusDetectInfo: Saved data was invalid, " + "not enough data in registry!\n")); + ExFreePool(keyData); + return STATUS_UNSUCCESSFUL; + + } else { + + UNICODE_STRING identifier; + ULONG value; + + // + // Complete unicode string. + // + + identifier.Buffer = (PWSTR) ((PUCHAR)keyData + keyData->DataOffset); + identifier.Length = (USHORT) keyData->DataLength; + identifier.MaximumLength = (USHORT) keyData->DataLength; + + // + // Get the first value out of the identifier - this will be the MBR + // checksum. + // + + status = RtlUnicodeStringToInteger(&identifier, 16, &value); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveBusDetectInfo: Error %#08lx converting " + "identifier %wZ into MBR xsum\n", + status, + &identifier)); + ExFreePool(keyData); + return status; + } + + diskInfo->MbrCheckSum = value; + + // + // Shift the string over to get the disk signature + // + + identifier.Buffer += 9; + identifier.Length -= 9 * sizeof(WCHAR); + identifier.MaximumLength -= 9 * sizeof(WCHAR); + + status = RtlUnicodeStringToInteger(&identifier, 16, &value); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskSaveBusDetectInfo: Error %#08lx converting " + "identifier %wZ into disk signature\n", + status, + &identifier)); + ExFreePool(keyData); + value = 0; + } + + diskInfo->Signature = value; + } + + // + // Here is where we would save away the extended int13 data. + // + + // + // Mark this entry as initialized so we can make sure not to do it again. + // + + diskInfo->Initialized = TRUE; + + + return STATUS_SUCCESS; +} + + +DISK_GEOMETRY_SOURCE +DiskUpdateGeometry( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) +/*++ + +Routine Description: + + This routine checks the DetectInfoList saved away during disk driver init + to see if any geometry information was reported for this drive. If the + geometry data exists (determined by matching non-zero signatures or + non-zero MBR checksums) then it will be saved in the RealGeometry member + of the disk data block. + + ClassReadDriveCapacity MUST be called after calling this routine to update + the cylinder count based on the size of the disk and the presence of any + disk management software. + +Arguments: + + DeviceExtension - Supplies a pointer to the device information for disk. + +Return Value: + + Inidicates whether the "RealGeometry" in the data block is now valid. + +--*/ + +{ + PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData; + + ULONG i; + PDISK_DETECT_INFO diskInfo; + + BOOLEAN found = FALSE; + + NTSTATUS status; + + PAGED_CODE(); + + + ASSERT(FdoExtension->CommonExtension.IsFdo); + ASSERT((FdoExtension->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) == 0); + + // + // If we've already set a non-default geometry for this drive then there's + // no need to try and update again. + // + + if(diskData->GeometrySource != DiskGeometryUnknown) { + return diskData->GeometrySource; + } + + // + // Scan through the saved detect info to see if we can find a match + // for this device. + // + + for(i = 0; i < DetectInfoCount; i++) { + + ASSERT(DetectInfoList != NULL); + + diskInfo = &(DetectInfoList[i]); + + if((diskData->Mbr.Signature != 0) && + (diskData->Mbr.Signature == diskInfo->Signature)) { + DebugPrint((1, "DiskUpdateGeometry: found match for signature " + "%#08lx\n", + diskData->Mbr.Signature)); + found = TRUE; + break; + } else if((diskData->Mbr.Signature == 0) && + (diskData->Mbr.MbrCheckSum != 0) && + (diskData->Mbr.MbrCheckSum == diskInfo->MbrCheckSum)) { + DebugPrint((1, "DiskUpdateGeometry: found match for xsum %#08lx\n", + diskData->Mbr.MbrCheckSum)); + found = TRUE; + break; + } + } + + if(found) { + + ULONG cylinders; + ULONG sectorsPerTrack; + ULONG tracksPerCylinder; + + ULONG sectors; + ULONG length; + + // + // Point to the array of drive parameters. + // + + cylinders = diskInfo->DriveParameters.MaxCylinders + 1; + sectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack; + tracksPerCylinder = diskInfo->DriveParameters.MaxHeads + 1; + + // + // Since the BIOS may not report the full drive, recalculate the drive + // size based on the volume size and the BIOS values for tracks per + // cylinder and sectors per track.. + // + + length = tracksPerCylinder * sectorsPerTrack; + + if (length == 0) { + + // + // The BIOS information is bogus. + // + + DebugPrint((1, "DiskUpdateGeometry: H (%d) or S(%d) is zero\n", + tracksPerCylinder, sectorsPerTrack)); + return FALSE; + } + + // + // since we are copying the structure RealGeometry here, we should + // really initialize all the fields, especially since a zero'd + // BytesPerSector field would cause a trap in xHalReadPartitionTable() + // + + diskData->RealGeometry = FdoExtension->DiskGeometry; + + // + // Save the geometry information away in the disk data block and + // set the bit indicating that we found a valid one. + // + + diskData->RealGeometry.SectorsPerTrack = sectorsPerTrack; + diskData->RealGeometry.TracksPerCylinder = tracksPerCylinder; + diskData->RealGeometry.Cylinders.QuadPart = (LONGLONG)cylinders; + + DebugPrint((1, "DiskUpdateGeometry: BIOS spt %#x, #heads %#x, " + "#cylinders %#x\n", + sectorsPerTrack, tracksPerCylinder, cylinders)); + + diskData->GeometrySource = DiskGeometryFromBios; + diskInfo->Device = FdoExtension->DeviceObject; + + } else { + + DebugPrint((1, "DiskUpdateGeometry: no match found for signature %#08lx\n", diskData->Mbr.Signature)); + } + + if(diskData->GeometrySource == DiskGeometryUnknown) { + + // + // We couldn't find a geometry from the BIOS. Check with the port + // driver and see if it can provide one. + // + + status = DiskGetPortGeometry(FdoExtension, &(diskData->RealGeometry)); + + if(NT_SUCCESS(status)) { + + // + // Check the geometry to make sure it's valid. + // + + if((diskData->RealGeometry.TracksPerCylinder * + diskData->RealGeometry.SectorsPerTrack) != 0) { + + diskData->GeometrySource = DiskGeometryFromPort; + DebugPrint((1, "DiskUpdateGeometry: using Port geometry for disk %#p\n", FdoExtension)); + + if (diskData->RealGeometry.BytesPerSector == 0) { + + DebugPrint((0, "DiskDriverReinit: Port driver failed to " + "set BytesPerSector in the RealGeometry\n")); + diskData->RealGeometry.BytesPerSector = + FdoExtension->DiskGeometry.BytesPerSector; + if (diskData->RealGeometry.BytesPerSector == 0) { + ASSERT(!"BytesPerSector is still zero!"); + } + + } + } + } + } + + // + // If we came up with a "real" geometry for this drive then set it in the + // device extension. + // + + if(diskData->GeometrySource != DiskGeometryUnknown) { + FdoExtension->DiskGeometry = diskData->RealGeometry; + + // + // Increment the count of used geometry entries. + // + + InterlockedIncrement(&DetectInfoUsedCount); + } + + return diskData->GeometrySource; +} + + +NTSTATUS +DiskUpdateRemovableGeometry ( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension + ) + +/*++ + +Routine Description: + + This routine updates the geometry of the disk. It will query the port + driver to see if it can provide any geometry info. If not it will use + the current head & sector count. + + Based on these values & the capacity of the drive as reported by + ClassReadDriveCapacity it will determine a new cylinder count for the + device. + +Arguments: + + Fdo - Supplies the functional device object whos size needs to be updated. + +Return Value: + + Returns the status of the opertion. + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension); + PDISK_DATA diskData = commonExtension->DriverData; + PDISK_GEOMETRY geometry = &(diskData->RealGeometry); + + NTSTATUS status; + + PAGED_CODE(); + + ASSERT_FDO(commonExtension->DeviceObject); + if (FdoExtension->DeviceDescriptor) { + ASSERT(FdoExtension->DeviceDescriptor->RemovableMedia); + } + ASSERT(TEST_FLAG(FdoExtension->DeviceObject->Characteristics, + FILE_REMOVABLE_MEDIA)); + + // + // Attempt to determine the disk geometry. First we'll check with the + // port driver to see what it suggests for a value. + // + + status = DiskGetPortGeometry(FdoExtension, geometry); + + if(NT_SUCCESS(status) && + ((geometry->TracksPerCylinder * geometry->SectorsPerTrack) != 0)) { + + FdoExtension->DiskGeometry = (*geometry); + } + + return status; +} + + +NTSTATUS +DiskGetPortGeometry( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PDISK_GEOMETRY Geometry + ) +/*++ + +Routine Description: + + This routine will query the port driver for disk geometry. Some port + drivers (in particular IDEPORT) may be able to provide geometry for the + device. + +Arguments: + + FdoExtension - the device object for the disk. + + Geometry - a structure to save the geometry information into (if any is + available) + +Return Value: + + STATUS_SUCCESS if geometry information can be provided or + error status indicating why it can't. + +--*/ +{ + PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension); + PIRP irp; + PIO_STACK_LOCATION irpStack; + KEVENT event; + + NTSTATUS status; + + PAGED_CODE(); + + // + // Build an irp to send IOCTL_DISK_GET_DRIVE_GEOMETRY to the lower driver. + // + + irp = IoAllocateIrp(commonExtension->LowerDeviceObject->StackSize, FALSE); + + if(irp == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; + + irpStack->Parameters.DeviceIoControl.IoControlCode = + IOCTL_DISK_GET_DRIVE_GEOMETRY; + irpStack->Parameters.DeviceIoControl.OutputBufferLength = + sizeof(DISK_GEOMETRY); + + irp->AssociatedIrp.SystemBuffer = Geometry; + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + IoSetCompletionRoutine(irp, + (PIO_COMPLETION_ROUTINE)ClassSignalCompletion, + &event, + TRUE, + TRUE, + TRUE); + + status = IoCallDriver(commonExtension->LowerDeviceObject, irp); + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + + ASSERT((status == STATUS_PENDING) || (status == irp->IoStatus.Status)); + status = irp->IoStatus.Status; + + IoFreeIrp(irp); + + return status; +} + + + +NTSTATUS +DiskReadDriveCapacity( + IN PDEVICE_OBJECT Fdo + ) +/*++ + +Routine Description: + + This routine is used by disk.sys as a wrapper for the classpnp API + ClassReadDriveCapacity. It will perform some additional operations to + attempt to determine drive geometry before it calls the classpnp version + of the routine. + + For fixed disks this involves calling DiskUpdateGeometry which will check + various sources (the BIOS, the port driver) for geometry information. + +Arguments: + + Fdo - a pointer to the device object to be checked. + +Return Value: + + status of ClassReadDriveCapacity. + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData; + DISK_GEOMETRY_SOURCE diskGeometrySource = DiskGeometryUnknown; + NTSTATUS status; + + ASSERT_FDO(Fdo); + + if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { + DiskUpdateRemovableGeometry(fdoExtension); + } else { + diskGeometrySource = DiskUpdateGeometry(fdoExtension); + } + + status = ClassReadDriveCapacity(Fdo); + + return status; +} + + + +VOID +DiskDriverReinitialization( + IN PDRIVER_OBJECT DriverObject, + IN PVOID Nothing, + IN ULONG Count + ) +/*++ + +Routine Description: + + This routine will scan through the current list of disks and attempt to + match them to any remaining geometry information. This will only be done + on the first call to the routine. + + Note: This routine assumes that the system will not be adding or removing + devices during this phase of the init process. This is very likely + a bad assumption but it greatly simplifies the code. + +Arguments: + + DriverObject - a pointer to the object for the disk driver. + + Nothing - unused + + Count - an indication of how many times this routine has been called. + +Return Value: + + none + +--*/ + +{ + PDEVICE_OBJECT deviceObject; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; + PDISK_DATA diskData; + + ULONG unmatchedDiskCount; + PDEVICE_OBJECT unmatchedDisk = NULL; + + ULONG i; + PDISK_DETECT_INFO diskInfo = NULL; + + if(Count != 1) { + DebugPrint((1, "DiskDriverReinitialization: ignoring call %d\n", + Count)); + return; + } + + // + // Check to see how many entries in the detect info list have been matched. + // If there's only one remaining we'll see if we can find a disk to go with + // it. + // + + if(DetectInfoCount == 0) { + DebugPrint((1, "DiskDriverReinitialization: no detect info saved\n")); + return; + } + + if((DetectInfoCount - DetectInfoUsedCount) != 1) { + DebugPrint((1, "DiskDriverReinitialization: %d of %d geometry entries " + "used - will not attempt match\n")); + return; + } + + // + // Scan through the list of disks and see if any of them are missing + // geometry information. If there is only one such disk we'll try to + // match it to the unmatched geometry. + // + + + // + // ISSUE-2000/5/24-henrygab - figure out if there's a way to keep + // removals from happening while doing this. + // + + for(deviceObject = DriverObject->DeviceObject, unmatchedDiskCount = 0; + deviceObject != NULL; + deviceObject = deviceObject->NextDevice) { + + // + // Make sure this is a disk and not a partition. + // + + fdoExtension = deviceObject->DeviceExtension; + if(fdoExtension->CommonExtension.IsFdo == FALSE) { + DebugPrint((1, "DiskDriverReinit: DO %#p is not an FDO\n", + deviceObject)); + continue; + } + + // + // If the geometry for this one is already known then skip it. + // + + diskData = fdoExtension->CommonExtension.DriverData; + if(diskData->GeometrySource != DiskGeometryUnknown) { + DebugPrint((1, "DiskDriverReinit: FDO %#p has a geometry\n", + deviceObject)); + continue; + } + + DebugPrint((1, "DiskDriverReinit: FDO %#p has no geometry\n", + deviceObject)); + + // + // Mark this one as using the default. It's past the time when disk + // might blunder across the geometry info. If we set the geometry + // from the bios we'll reset this field down below. + // + + diskData->GeometrySource = DiskGeometryFromDefault; + + // + // As long as we've only got one unmatched disk we're fine. + // + + unmatchedDiskCount++; + if(unmatchedDiskCount > 1) { + ASSERT(unmatchedDisk != NULL); + DebugPrint((1, "DiskDriverReinit: FDO %#p also has no geometry\n", + unmatchedDisk)); + unmatchedDisk = NULL; + break; + } + + unmatchedDisk = deviceObject; + } + + // + // If there's more or less than one ungeometried disk then we can't do + // anything about the geometry. + // + + if(unmatchedDiskCount != 1) { + DebugPrint((1, "DiskDriverReinit: Unable to match geometry\n")); + return; + + } + + fdoExtension = unmatchedDisk->DeviceExtension; + diskData = fdoExtension->CommonExtension.DriverData; + + DebugPrint((1, "DiskDriverReinit: Found possible match\n")); + + // + // Find the geometry which wasn't assigned. + // + + for(i = 0; i < DetectInfoCount; i++) { + if(DetectInfoList[i].Device == NULL) { + diskInfo = &(DetectInfoList[i]); + break; + } + } + + ASSERT(diskInfo != NULL); + + { + // + // Save the geometry information away in the disk data block and + // set the bit indicating that we found a valid one. + // + + ULONG cylinders; + ULONG sectorsPerTrack; + ULONG tracksPerCylinder; + + ULONG sectors; + ULONG length; + + // + // Point to the array of drive parameters. + // + + cylinders = diskInfo->DriveParameters.MaxCylinders + 1; + sectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack; + tracksPerCylinder = diskInfo->DriveParameters.MaxHeads + 1; + + // + // Since the BIOS may not report the full drive, recalculate the drive + // size based on the volume size and the BIOS values for tracks per + // cylinder and sectors per track.. + // + + length = tracksPerCylinder * sectorsPerTrack; + + if (length == 0) { + + // + // The BIOS information is bogus. + // + + DebugPrint((1, "DiskDriverReinit: H (%d) or S(%d) is zero\n", + tracksPerCylinder, sectorsPerTrack)); + return; + } + + // + // since we are copying the structure RealGeometry here, we should + // really initialize all the fields, especially since a zero'd + // BytesPerSector field would cause a trap in xHalReadPartitionTable() + // + + diskData->RealGeometry = fdoExtension->DiskGeometry; + + // + // Save the geometry information away in the disk data block and + // set the bit indicating that we found a valid one. + // + + diskData->RealGeometry.SectorsPerTrack = sectorsPerTrack; + diskData->RealGeometry.TracksPerCylinder = tracksPerCylinder; + diskData->RealGeometry.Cylinders.QuadPart = (LONGLONG)cylinders; + + DebugPrint((1, "DiskDriverReinit: BIOS spt %#x, #heads %#x, " + "#cylinders %#x\n", + sectorsPerTrack, tracksPerCylinder, cylinders)); + + diskData->GeometrySource = DiskGeometryGuessedFromBios; + diskInfo->Device = unmatchedDisk; + + // + // Now copy the geometry over to the fdo extension and call + // classpnp to redetermine the disk size and cylinder count. + // + + fdoExtension->DiskGeometry = diskData->RealGeometry; + + // + // BUGBUG - why not call DiskReadDriveCapacity()? + // + + ClassReadDriveCapacity(unmatchedDisk); + + if (diskData->RealGeometry.BytesPerSector == 0) { + + // + // if the BytesPerSector field is set to zero for a disk + // listed in the bios, then the system will bugcheck in + // xHalReadPartitionTable(). assert here since it is + // easier to determine what is happening this way. + // + + ASSERT(!"RealGeometry not set to non-zero bps\n"); + } + } + + return; +} + + +NTSTATUS +DiskGetDetectInfo( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + OUT PDISK_DETECTION_INFO DetectInfo + ) +/*++ + +Routine Description: + + Get the Int13 information from the BIOS DetectInfoList. + +Arguments: + + FdoExtension - Supplies a pointer to the FDO extension that we want to + obtain the detect information for. + + DetectInfo - A buffer where the detect information will be copied to. + +Return Value: + + NTSTATUS code. + +--*/ +{ + ULONG i; + BOOLEAN found; + PDISK_DETECT_INFO diskInfo = NULL; + PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData; + + PAGED_CODE (); + + ASSERT(FdoExtension->CommonExtension.IsFdo); + + // + // Fail for non-fixed drives. + // + + if (TEST_FLAG (FdoExtension->DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) { + return STATUS_NOT_SUPPORTED; + } + + // + // There is no GPT detection info, so fail this. + // + + if (diskData->PartitionStyle == PARTITION_STYLE_GPT) { + return STATUS_NOT_SUPPORTED; + } + + for(i = 0; i < DetectInfoCount; i++) { + + + ASSERT(DetectInfoList != NULL); + + diskInfo = &(DetectInfoList[i]); + + if((diskData->Mbr.Signature != 0) && + (diskData->Mbr.Signature == diskInfo->Signature)) { + DebugPrint((1, "DiskGetDetectInfo: found match for signature " + "%#08lx\n", + diskData->Mbr.Signature)); + found = TRUE; + break; + } else if((diskData->Mbr.Signature == 0) && + (diskData->Mbr.MbrCheckSum != 0) && + (diskData->Mbr.MbrCheckSum == diskInfo->MbrCheckSum)) { + DebugPrint((1, "DiskGetDetectInfo: found match for xsum %#08lx\n", + diskData->Mbr.MbrCheckSum)); + found = TRUE; + break; + } + } + + if ( found ) { + DetectInfo->DetectionType = DetectInt13; + DetectInfo->Int13.DriveSelect = diskInfo->DriveParameters.DriveSelect; + DetectInfo->Int13.MaxCylinders = diskInfo->DriveParameters.MaxCylinders; + DetectInfo->Int13.SectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack; + DetectInfo->Int13.MaxHeads = diskInfo->DriveParameters.MaxHeads; + DetectInfo->Int13.NumberDrives = diskInfo->DriveParameters.NumberDrives; + RtlZeroMemory (&DetectInfo->ExInt13, sizeof (DetectInfo->ExInt13)); + } + + return (found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); +} + + +NTSTATUS +DiskReadSignature( + IN PDEVICE_OBJECT Fdo + ) + +/*++ + +Routine Description: + + Read the disks signature from the drive. The signature can be either + a MBR signature or a GPT/EFI signature. + + The low-level signature reading is done by IoReadDiskSignature(). + +Arguments: + + Fdo - Pointer to the FDO of a disk to read the signature for. + +Return Value: + + NTSTATUS code. + +--*/ + + +{ + NTSTATUS Status; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData; + DISK_SIGNATURE Signature; + + PAGED_CODE (); + + Status = IoReadDiskSignature (Fdo, + fdoExtension->DiskGeometry.BytesPerSector, + &Signature); + + if (!NT_SUCCESS (Status)) { + return Status; + } + + if (Signature.PartitionStyle == PARTITION_STYLE_GPT) { + diskData->PartitionStyle = PARTITION_STYLE_GPT; + diskData->Efi.DiskId = Signature.Gpt.DiskId; + } else if (Signature.PartitionStyle == PARTITION_STYLE_MBR) { + diskData->PartitionStyle = PARTITION_STYLE_MBR; + diskData->Mbr.Signature = Signature.Mbr.Signature; + diskData->Mbr.MbrCheckSum = Signature.Mbr.CheckSum; + } else { + ASSERT (FALSE); + Status = STATUS_UNSUCCESSFUL; + } + + return Status; +} + +#endif // defined(_X86_) + + diff --git a/reactos/drivers/storage/class/disk_new/part.c b/reactos/drivers/storage/class/disk_new/part.c new file mode 100644 index 00000000000..66e69435493 --- /dev/null +++ b/reactos/drivers/storage/class/disk_new/part.c @@ -0,0 +1,327 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + disk.c + +Abstract: + + SCSI disk class driver + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "disk.h" + +#define PtCache ClassDebugExternal1 + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, DiskReadPartitionTableEx) +#pragma alloc_text(PAGE, DiskWritePartitionTableEx) +#pragma alloc_text(PAGE, DiskSetPartitionInformationEx) +#endif + +ULONG DiskBreakOnPtInval = FALSE; + +// +// By default, 64-bit systems can see GPT disks and 32-bit systems +// cannot. This will likely change in the future. +// + +#if defined(_WIN64) +ULONG DiskDisableGpt = FALSE; +#else +ULONG DiskDisableGpt = TRUE; +#endif + +NTSTATUS +DiskReadPartitionTableEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN BypassCache, + OUT PDRIVE_LAYOUT_INFORMATION_EX* DriveLayout + ) +/*++ + +Routine Description: + + This routine will return the current layout information for the disk. + If the cached information is still valid then it will be returned, + otherwise the layout will be retreived from the kernel and cached for + future use. + + This routine must be called with the partitioning lock held. The + partition list which is returned is not guaranteed to remain valid + once the lock has been released. + +Arguments: + + Fdo - a pointer to the FDO for the disk. + + DriveLayout - a location to store a pointer to the drive layout information. + +Return Value: + + STATUS_SUCCESS if successful or an error status indicating what failed. + +--*/ + +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + NTSTATUS status; + PDRIVE_LAYOUT_INFORMATION_EX layoutEx; + + layoutEx = NULL; + + if(BypassCache) { + diskData->CachedPartitionTableValid = FALSE; + DebugPrint((PtCache, "DiskRPTEx: cache bypassed and invalidated for " + "FDO %#p\n", Fdo)); + } + + // + // If the cached partition table is present then return a copy of it. + // + + if(diskData->CachedPartitionTableValid == TRUE) { + + ULONG partitionNumber; + PDRIVE_LAYOUT_INFORMATION_EX layout = diskData->CachedPartitionTable; + + // + // Clear the partition numbers from the list entries + // + + for(partitionNumber = 0; + partitionNumber < layout->PartitionCount; + partitionNumber++) { + layout->PartitionEntry[partitionNumber].PartitionNumber = 0; + } + + *DriveLayout = diskData->CachedPartitionTable; + + DebugPrint((PtCache, "DiskRPTEx: cached PT returned (%#p) for " + "FDO %#p\n", + *DriveLayout, Fdo)); + + return STATUS_SUCCESS; + } + + ASSERTMSG("DiskReadPartitionTableEx is not using cached partition table", + (DiskBreakOnPtInval == FALSE)); + + // + // If there's a cached partition table still around then free it. + // + + if(diskData->CachedPartitionTable) { + DebugPrint((PtCache, "DiskRPTEx: cached PT (%#p) freed for FDO %#p\n", + diskData->CachedPartitionTable, Fdo)); + + ExFreePool(diskData->CachedPartitionTable); + diskData->CachedPartitionTable = NULL; + } + + // + // By default, X86 disables recognition of GPT disks. Instead we + // return the protective MBR partition. Use IoReadPartitionTable + // to get this. + // + + status = IoReadPartitionTableEx(Fdo->DeviceObject, &layoutEx); + + if (DiskDisableGpt) { + PDRIVE_LAYOUT_INFORMATION layout; + + if (NT_SUCCESS (status) && + layoutEx->PartitionStyle == PARTITION_STYLE_GPT) { + + // + // ISSUE - 2000/29/08 - math: Remove from final product. + // Leave this debug print in for a while until everybody + // has had a chance to convert their GPT disks to MBR. + // + + DbgPrint ("DISK: Disk %p recognized as a GPT disk on a system without GPT support.\n" + " Disk will appear as RAW.\n", + Fdo->DeviceObject); + + ExFreePool (layoutEx); + status = IoReadPartitionTable(Fdo->DeviceObject, + Fdo->DiskGeometry.BytesPerSector, + FALSE, + &layout); + if (NT_SUCCESS (status)) { + layoutEx = DiskConvertLayoutToExtended(layout); + ExFreePool (layout); + } + } + } + + diskData->CachedPartitionTable = layoutEx; + + // + // If the routine fails make sure we don't have a stale partition table + // pointer. Otherwise indicate that the table is now valid. + // + + if(!NT_SUCCESS(status)) { + diskData->CachedPartitionTable = NULL; + } else { + diskData->CachedPartitionTableValid = TRUE; + } + + *DriveLayout = diskData->CachedPartitionTable; + + DebugPrint((PtCache, "DiskRPTEx: returning PT %#p for FDO %#p with status " + "%#08lx. PT is %scached\n", + *DriveLayout, + Fdo, + status, + (diskData->CachedPartitionTableValid ? "" : "not "))); + + + return status; +} + + +NTSTATUS +DiskWritePartitionTableEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout + ) +/*++ + +Routine Description: + + This routine will invalidate the cached partition table. It will then + write the new drive layout to disk. + +Arguments: + + Fdo - the FDO for the disk getting the new partition table. + + DriveLayout - the new drive layout. + +Return Value: + + status + +--*/ +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + + // + // Invalidate the cached partition table. Do not free it as it may be + // the very drive layout that was passed in to us. + // + + diskData->CachedPartitionTableValid = FALSE; + + DebugPrint((PtCache, "DiskWPTEx: Invalidating PT cache for FDO %#p\n", + Fdo)); + + if (DiskDisableGpt) { + if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT) { + return STATUS_NOT_SUPPORTED; + } + } + + return IoWritePartitionTableEx(Fdo->DeviceObject, DriveLayout); +} + +NTSTATUS +DiskSetPartitionInformationEx( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN ULONG PartitionNumber, + IN struct _SET_PARTITION_INFORMATION_EX* PartitionInfo + ) +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + + diskData->CachedPartitionTableValid = FALSE; + DebugPrint((PtCache, "DiskSPIEx: Invalidating PT cache for FDO %#p\n", + Fdo)); + + if (DiskDisableGpt) { + if (PartitionInfo->PartitionStyle == PARTITION_STYLE_GPT) { + return STATUS_NOT_SUPPORTED; + } + } + + return IoSetPartitionInformationEx(Fdo->DeviceObject, + PartitionNumber, + PartitionInfo); +} + +NTSTATUS +DiskSetPartitionInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN ULONG SectorSize, + IN ULONG PartitionNumber, + IN ULONG PartitionType + ) +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + + diskData->CachedPartitionTableValid = FALSE; + DebugPrint((PtCache, "DiskSPI: Invalidating PT cache for FDO %#p\n", + Fdo)); + + return IoSetPartitionInformation(Fdo->DeviceObject, + SectorSize, + PartitionNumber, + PartitionType); +} + +BOOLEAN +DiskInvalidatePartitionTable( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN PartitionLockHeld + ) +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + BOOLEAN wasValid; + + wasValid = (BOOLEAN) (diskData->CachedPartitionTableValid ? TRUE : FALSE); + diskData->CachedPartitionTableValid = FALSE; + + DebugPrint((PtCache, "DiskIPT: Invalidating PT cache for FDO %#p\n", + Fdo)); + + if((PartitionLockHeld) && (diskData->CachedPartitionTable != NULL)) { + DebugPrint((PtCache, "DiskIPT: Freeing PT cache (%#p) for FDO %#p\n", + diskData->CachedPartitionTable, Fdo)); + ExFreePool(diskData->CachedPartitionTable); + diskData->CachedPartitionTable = NULL; + } + + return wasValid; +} + +NTSTATUS +DiskVerifyPartitionTable( + IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, + IN BOOLEAN FixErrors + ) +{ + PDISK_DATA diskData = Fdo->CommonExtension.DriverData; + + if(FixErrors) { + diskData->CachedPartitionTableValid = FALSE; + DebugPrint((PtCache, "DiskWPTEx: Invalidating PT cache for FDO %#p\n", + Fdo)); + } + + return IoVerifyPartitionTable(Fdo->DeviceObject, FixErrors); +} + diff --git a/reactos/drivers/storage/class/disk_new/pnp.c b/reactos/drivers/storage/class/disk_new/pnp.c new file mode 100644 index 00000000000..fe1ef90a9cb --- /dev/null +++ b/reactos/drivers/storage/class/disk_new/pnp.c @@ -0,0 +1,1403 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1991 - 1999 + +Module Name: + + pnp.c + +Abstract: + + SCSI disk class driver + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "disk.h" + +extern NTSYSAPI ULONG InitSafeBootMode; + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, DiskAddDevice) +#pragma alloc_text(PAGE, DiskInitFdo) +#pragma alloc_text(PAGE, DiskInitPdo) +#pragma alloc_text(PAGE, DiskStartFdo) +#pragma alloc_text(PAGE, DiskStartPdo) +#pragma alloc_text(PAGE, DiskQueryId) +#pragma alloc_text(PAGE, DiskGenerateDeviceName) +#pragma alloc_text(PAGE, DiskCreateSymbolicLinks) +#pragma alloc_text(PAGE, DiskDeleteSymbolicLinks) +#pragma alloc_text(PAGE, DiskRemoveDevice) + +#endif + + +NTSTATUS +DiskAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject + ) + +/*++ + +Routine Description: + + This routine gets a port drivers capabilities, obtains the + inquiry data, searches the SCSI bus for the port driver and creates + the device objects for the disks found. + +Arguments: + + DriverObject - Pointer to driver object created by system. + + Pdo - Device object use to send requests to port driver. + +Return Value: + + True is returned if one disk was found and successfully created. + +--*/ + +{ + ULONG rootPartitionMountable = FALSE; + + PCONFIGURATION_INFORMATION configurationInformation; + ULONG diskCount; + + NTSTATUS status; + + PAGED_CODE(); + + // + // See if we should be allowing file systems to mount on partition zero. + // + + TRY { + HANDLE deviceKey; + + UNICODE_STRING diskKeyName; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE diskKey; + + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + + status = IoOpenDeviceRegistryKey(PhysicalDeviceObject, + PLUGPLAY_REGKEY_DEVICE, + KEY_READ, + &deviceKey); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskAddDevice: Error %#08lx opening device key " + "for pdo %#08lx\n", + status, PhysicalDeviceObject)); + LEAVE; + } + + RtlInitUnicodeString(&diskKeyName, L"Disk"); + InitializeObjectAttributes(&objectAttributes, + &diskKeyName, + OBJ_CASE_INSENSITIVE, + deviceKey, + NULL); + + status = ZwOpenKey(&diskKey, KEY_READ, &objectAttributes); + ZwClose(deviceKey); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskAddDevice: Error %#08lx opening disk key " + "for pdo %#08lx device key %#x\n", + status, PhysicalDeviceObject, deviceKey)); + LEAVE; + } + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + queryTable[0].Name = L"RootPartitionMountable"; + queryTable[0].EntryContext = &(rootPartitionMountable); + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + diskKey, + queryTable, + NULL, + NULL); + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskAddDevice: Error %#08lx reading value from " + "disk key %#x for pdo %#08lx\n", + status, diskKey, PhysicalDeviceObject)); + } + + ZwClose(diskKey); + + } FINALLY { + + // + // Do nothing. + // + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskAddDevice: Will %sallow file system to mount on " + "partition zero of disk %#08lx\n", + (rootPartitionMountable ? "" : "not "), + PhysicalDeviceObject)); + } + } + + // + // Create device objects for disk + // + + diskCount = 0; + + status = DiskCreateFdo( + DriverObject, + PhysicalDeviceObject, + &diskCount, + (BOOLEAN) !rootPartitionMountable + ); + + // + // Get the number of disks already initialized. + // + + configurationInformation = IoGetConfigurationInformation(); + + if (NT_SUCCESS(status)) { + + // + // Increment system disk device count. + // + + configurationInformation->DiskCount++; + + } + + return status; + +} // end DiskAddDevice() + + + +NTSTATUS +DiskInitFdo( + IN PDEVICE_OBJECT Fdo + ) + +/*++ + +Routine Description: + + This routine is called to do one-time initialization of new device objects + + +Arguments: + + Fdo - a pointer to the functional device object for this device + +Return Value: + + status + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + + PDISK_DATA diskData = (PDISK_DATA) fdoExtension->CommonExtension.DriverData; + + ULONG srbFlags = 0; + + ULONG timeOut = 0; + + ULONG bytesPerSector; + UCHAR sectorShift; + + BOOLEAN dmActive = FALSE; + PULONG dmSkew; + ULONG dmByteSkew; + + NTSTATUS status; + + PAGED_CODE(); + + // + // Build the lookaside list for srb's for the physical disk. Should only + // need a couple. If this fails then we don't have an emergency SRB so + // fail the call to initialize. + // + + ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION) fdoExtension, + PARTITION0_LIST_SIZE); + + // + // Because all requests share a common sense buffer, it is possible + // for the buffer to be overwritten if the port driver completes + // multiple failed requests that require a request sense before the + // class driver's completion routine can consume the data in the buffer. + // To prevent this, we allow the port driver to allocate a unique sense + // buffer each time it needs one. We are responsible for freeing this + // buffer. This also allows the adapter to be configured to support + // additional sense data beyond the minimum 18 bytes. + // + + fdoExtension->SrbFlags = SRB_FLAGS_PORT_DRIVER_ALLOCSENSE; + + // + // Initialize the srb flags. + // + + if (fdoExtension->DeviceDescriptor->CommandQueueing && + fdoExtension->AdapterDescriptor->CommandQueueing) { + + fdoExtension->SrbFlags = SRB_FLAGS_QUEUE_ACTION_ENABLE; + + } + + if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) { + SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT); + } + + // + // Look for controllers that require special flags. + // + + ClassScanForSpecial(fdoExtension, DiskBadControllers, DiskSetSpecialHacks); + + // + // Look into the registry to see if this device + // requires special attention - [ like a hack ] + // + + DiskScanRegistryForSpecial(fdoExtension); + + srbFlags = fdoExtension->SrbFlags; + + // + // Clear buffer for drive geometry. + // + + RtlZeroMemory(&(fdoExtension->DiskGeometry), + sizeof(DISK_GEOMETRY)); + + // + // Allocate request sense buffer. + // + + fdoExtension->SenseData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, + SENSE_BUFFER_SIZE, + DISK_TAG_START); + + if (fdoExtension->SenseData == NULL) { + + // + // The buffer can not be allocated. + // + + DebugPrint((1, "DiskInitFdo: Can not allocate request sense buffer\n")); + + status = STATUS_INSUFFICIENT_RESOURCES; + return status; + } + + // + // Physical device object will describe the entire + // device, starting at byte offset 0. + // + + fdoExtension->CommonExtension.StartingOffset.QuadPart = (LONGLONG)(0); + + // + // Set timeout value in seconds. + // + + timeOut = ClassQueryTimeOutRegistryValue(Fdo); + if (timeOut) { + fdoExtension->TimeOutValue = timeOut; + } else { + fdoExtension->TimeOutValue = SCSI_DISK_TIMEOUT; + } + + // + // If this is a removable drive, build an entry in devicemap\scsi + // indicating it's physicaldriveN name, set up the appropriate + // update partitions routine and set the flags correctly. + // note: only do this after the timeout value is set, above. + // + + if (fdoExtension->DeviceDescriptor->RemovableMedia) { + ClassUpdateInformationInRegistry( Fdo, + "PhysicalDrive", + fdoExtension->DeviceNumber, + NULL, + 0); + // + // Enable media change notification for removable disks + // + ClassInitializeMediaChangeDetection(fdoExtension, + "Disk"); + + SET_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA); + diskData->UpdatePartitionRoutine = DiskUpdateRemovablePartitions; + + } else { + + SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + diskData->UpdatePartitionRoutine = DiskUpdatePartitions; + + } + + // + // Read the drive capacity. Don't use the disk version of the routine here + // since we don't know the disk signature yet - the disk version will + // attempt to determine the BIOS reported geometry. + // + + status = ClassReadDriveCapacity(Fdo); + + // + // If the read capcity failed then just return, unless this is a + // removable disk where a device object partition needs to be created. + // + + if (!NT_SUCCESS(status) && + !(Fdo->Characteristics & FILE_REMOVABLE_MEDIA)) { + + DebugPrint((1, + "DiskInitFdo: Can't read capacity for device %p\n", + Fdo)); + + if (fdoExtension->DeviceDescriptor->RemovableMedia) { + fdoExtension->DiskGeometry.MediaType = RemovableMedia; + Fdo->Flags &= ~DO_VERIFY_VOLUME; + } else { + fdoExtension->DiskGeometry.MediaType = FixedMedia; + } + + status = STATUS_SUCCESS; + } + + // + // Set up sector size fields. + // + // Stack variables will be used to update + // the partition device extensions. + // + // The device extension field SectorShift is + // used to calculate sectors in I/O transfers. + // + // The DiskGeometry structure is used to service + // IOCTls used by the format utility. + // + + bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector; + + // + // Make sure sector size is not zero. + // + + if (bytesPerSector == 0) { + + // + // Default sector size for disk is 512. + // + + bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector = 512; + } + + sectorShift = fdoExtension->SectorShift; + + // + // Determine is DM Driver is loaded on an IDE drive that is + // under control of Atapi - this could be either a crashdump or + // an Atapi device is sharing the controller with an IDE disk. + // + + HalExamineMBR(fdoExtension->CommonExtension.DeviceObject, + fdoExtension->DiskGeometry.BytesPerSector, + (ULONG)0x54, + (PVOID*)&dmSkew); + + if (dmSkew) { + + // + // Update the device extension, so that the call to IoReadPartitionTable + // will get the correct information. Any I/O to this disk will have + // to be skewed by *dmSkew sectors aka DMByteSkew. + // + + fdoExtension->DMSkew = *dmSkew; + fdoExtension->DMActive = TRUE; + fdoExtension->DMByteSkew = fdoExtension->DMSkew * bytesPerSector; + + // + // Save away the infomation that we need, since this deviceExtension will soon be + // blown away. + // + + dmActive = TRUE; + dmByteSkew = fdoExtension->DMByteSkew; + + } + +#if defined(_X86_) + // + // Try to read the signature off the disk and determine the correct drive + // geometry based on that. This requires rereading the disk size to get + // the cylinder count updated correctly. + // + + if(fdoExtension->DeviceDescriptor->RemovableMedia == FALSE) { + DiskReadSignature(Fdo); + DiskReadDriveCapacity(Fdo); + } +#endif + + // + // Register interfaces for this device + // + { + UNICODE_STRING interfaceName; + + RtlInitUnicodeString(&interfaceName, NULL); + + status = IoRegisterDeviceInterface(fdoExtension->LowerPdo, + (LPGUID) &DiskClassGuid, + NULL, + &interfaceName); + + if(NT_SUCCESS(status)) { + + diskData->DiskInterfaceString = interfaceName; + status = IoSetDeviceInterfaceState(&interfaceName, TRUE); + + } else { + interfaceName.Buffer = NULL; + } + + if(!NT_SUCCESS(status)) { + + DebugPrint((1, "DiskInitFdo: Unable to register or set disk DCA " + "for fdo %p [%lx]\n", Fdo, status)); + + RtlFreeUnicodeString(&interfaceName); + RtlInitUnicodeString(&(diskData->DiskInterfaceString), NULL); + } + } + + DiskCreateSymbolicLinks(Fdo); + + // + // Determine the type of disk and enable failure preiction in the hardware + // and enable failure prediction polling. + // + + if (InitSafeBootMode == 0) + { + DiskDetectFailurePrediction(fdoExtension, + &diskData->FailurePredictionCapability); + + if (diskData->FailurePredictionCapability != FailurePredictionNone) + { + // + // Cool, we've got some sort of failure prediction, enable it + // at the hardware and then enable polling for it + // + + // + // By default we allow performance to be degradeded if failure + // prediction is enabled. + // + // TODO: Make a registry entry ? + // + + diskData->AllowFPPerfHit = TRUE; + + // + // Enable polling only after Atapi and SBP2 add support for the new + // SRB flag that indicates that the request should not reset the + // drive spin down idle timer. + // + + status = DiskEnableDisableFailurePredictPolling(fdoExtension, + TRUE, + DISK_DEFAULT_FAILURE_POLLING_PERIOD); + + DebugPrint((3, "DiskInitFdo: Failure Prediction Poll enabled as " + "%d for device %p\n", + diskData->FailurePredictionCapability, + Fdo)); + } + } else { + + // + // In safe boot mode we do not enable failure prediction, as perhaps + // it is the reason why normal boot does not work + // + + diskData->FailurePredictionCapability = FailurePredictionNone; + + } + + // + // Initialize the verify mutex + // + + KeInitializeMutex(&diskData->VerifyMutex, MAX_SECTORS_PER_VERIFY); + + return(STATUS_SUCCESS); + +} // end DiskInitFdo() + + +NTSTATUS +DiskInitPdo( + IN PDEVICE_OBJECT Pdo + ) + +/*++ + +Routine Description: + + This routine will create the well known names for a PDO and register + it's device interfaces. + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION pdoExtension = Pdo->DeviceExtension; + PDISK_DATA diskData = pdoExtension->DriverData; + + UNICODE_STRING interfaceName; + + NTSTATUS status; + + PAGED_CODE(); + + DiskCreateSymbolicLinks(Pdo); + + // + // Register interfaces for this device + // + + RtlInitUnicodeString(&interfaceName, NULL); + + status = IoRegisterDeviceInterface(Pdo, + (LPGUID) &PartitionClassGuid, + NULL, + &interfaceName); + + if(NT_SUCCESS(status)) { + + diskData->PartitionInterfaceString = interfaceName; + status = IoSetDeviceInterfaceState(&interfaceName, TRUE); + + } else { + interfaceName.Buffer = NULL; + } + + if(!NT_SUCCESS(status)) { + DebugPrint((1, "DiskInitPdo: Unable to register partition DCA for " + "pdo %p [%lx]\n", Pdo, status)); + + RtlFreeUnicodeString(&interfaceName); + RtlInitUnicodeString(&(diskData->PartitionInterfaceString), NULL); + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +DiskStartPdo( + IN PDEVICE_OBJECT Pdo + ) + +/*++ + +Routine Description: + + This routine will create the well known names for a PDO and register + it's device interfaces. + +--*/ + +{ + PAGED_CODE(); + + return STATUS_SUCCESS; +} + +NTSTATUS +DiskStopDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ) + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdo = DeviceObject->DeviceExtension; + + if(fdo->CommonExtension.IsFdo) { + DiskAcquirePartitioningLock(fdo); + DiskInvalidatePartitionTable(fdo, TRUE); + DiskReleasePartitioningLock(fdo); + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +DiskQueryId( + IN PDEVICE_OBJECT Pdo, + IN BUS_QUERY_ID_TYPE IdType, + IN PUNICODE_STRING UnicodeIdString + ) + +/*++ + +Routine Description: + + This routine generates the PNP id's for the disk's "children". If the + specified ID isn't one that the routine can generate it must return + STATUS_NOT_IMPLEMENTED so classpnp will know not to do anything with the + PNP request's status. + + This routine allocates the buffer for the UnicodeIdString. It is the + caller's responsibility to free the buffer when it's done. + +Arguments: + + Pdo - a pointer to the PDO we are to generate an ID for + + IdType - the type of ID to be generated + + UnicodeIdString - a string to put the results into. + +Return Value: + + STATUS_SUCCCESS if successful + + STATUS_NOT_IMPLEMENTED if the IdType is not one supported by this routine + + error status otherwise. + +--*/ + +{ + ANSI_STRING ansiIdString; + + NTSTATUS status; + + PAGED_CODE(); + ASSERT_PDO(Pdo); + + if(IdType == BusQueryDeviceID) { + + if((Pdo->Characteristics & FILE_REMOVABLE_MEDIA) == 0) { + RtlInitAnsiString(&ansiIdString, "STORAGE\\Partition"); + return RtlAnsiStringToUnicodeString(UnicodeIdString, &ansiIdString, TRUE); + } + + RtlInitAnsiString(&ansiIdString, + "STORAGE\\RemovableMedia"); + + return RtlAnsiStringToUnicodeString(UnicodeIdString, &ansiIdString, TRUE); + } + + if(IdType == BusQueryInstanceID) { + + PPHYSICAL_DEVICE_EXTENSION pdoExtension = Pdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = Pdo->DeviceExtension; + PDISK_DATA diskData = commonExtension->PartitionZeroExtension->CommonExtension.DriverData; + + UCHAR string[64]; + + if((Pdo->Characteristics & FILE_REMOVABLE_MEDIA) == 0) { + + if (diskData->PartitionStyle == PARTITION_STYLE_MBR) { + sprintf(string, "S%08lx_O%I64lx_L%I64lx", + diskData->Mbr.Signature, + commonExtension->StartingOffset, + commonExtension->PartitionLength); + } else { + sprintf(string, + "S%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02xS_O%I64lx_L%I64lx", + diskData->Efi.DiskId.Data1, + diskData->Efi.DiskId.Data2, + diskData->Efi.DiskId.Data3, + diskData->Efi.DiskId.Data4[0], + diskData->Efi.DiskId.Data4[1], + diskData->Efi.DiskId.Data4[2], + diskData->Efi.DiskId.Data4[3], + diskData->Efi.DiskId.Data4[4], + diskData->Efi.DiskId.Data4[5], + diskData->Efi.DiskId.Data4[6], + diskData->Efi.DiskId.Data4[7], + commonExtension->StartingOffset, + commonExtension->PartitionLength); + } + } else { + sprintf(string, "RM"); + } + + RtlInitAnsiString(&ansiIdString, string); + + return RtlAnsiStringToUnicodeString(UnicodeIdString, &ansiIdString, TRUE); + } + + if((IdType == BusQueryHardwareIDs) || (IdType == BusQueryCompatibleIDs)) { + + RtlInitAnsiString(&ansiIdString, "STORAGE\\Volume"); + + UnicodeIdString->MaximumLength = (USHORT) RtlAnsiStringToUnicodeSize(&ansiIdString) + sizeof(UNICODE_NULL); + + UnicodeIdString->Buffer = ExAllocatePoolWithTag(PagedPool, + UnicodeIdString->MaximumLength, + DISK_TAG_PNP_ID); + + if(UnicodeIdString->Buffer == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(UnicodeIdString->Buffer, UnicodeIdString->MaximumLength); + + return RtlAnsiStringToUnicodeString(UnicodeIdString, + &ansiIdString, + FALSE); + } + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS +DiskGenerateDeviceName( + IN BOOLEAN IsFdo, + IN ULONG DeviceNumber, + IN OPTIONAL ULONG PartitionNumber, + IN OPTIONAL PLARGE_INTEGER StartingOffset, + IN OPTIONAL PLARGE_INTEGER PartitionLength, + OUT PUCHAR *RawName + ) + +/*++ + +Routine Description: + + This routine will allocate a unicode string buffer and then fill it in + with a generated name for the specified device object. + + It is the responsibility of the user to allocate a UNICODE_STRING structure + to pass in and to free UnicodeName->Buffer when done with it. + +Arguments: + + DeviceObject - a pointer to the device object + + UnicodeName - a unicode string to put the name buffer into + +Return Value: + + status + +--*/ + +//#define PDO_NAME_FORMAT "\\Device\\Harddisk%d\\DP(%d)%d" +#define PDO_NAME_FORMAT "\\Device\\Harddisk%d\\DP(%d)%#I64x-%#I64x+%lx" +#define FDO_NAME_FORMAT "\\Device\\Harddisk%d\\DR%d" + +//#define PDO_NAME_FORMAT (PDO_BASE_NAME "+%#I64x+%#I64x+%#lx") + +{ + UCHAR rawName[64]; + static ULONG diskDeviceSequenceNumber = 0; + + PAGED_CODE(); + + if(!IsFdo) { + + ASSERT(ARGUMENT_PRESENT((PVOID)(ULONG_PTR) PartitionNumber)); + ASSERT(ARGUMENT_PRESENT(PartitionLength)); + ASSERT(ARGUMENT_PRESENT(StartingOffset)); + + sprintf(rawName, PDO_NAME_FORMAT, DeviceNumber, PartitionNumber, + StartingOffset->QuadPart, + PartitionLength->QuadPart, + diskDeviceSequenceNumber++); + } else { + + ASSERT(!ARGUMENT_PRESENT((PVOID)(ULONG_PTR) PartitionNumber)); + ASSERT(!ARGUMENT_PRESENT(PartitionLength)); + ASSERT(!ARGUMENT_PRESENT(StartingOffset)); + + sprintf(rawName, FDO_NAME_FORMAT, DeviceNumber, + diskDeviceSequenceNumber++); + + } + + *RawName = ExAllocatePoolWithTag(PagedPool, + strlen(rawName) + 1, + DISK_TAG_NAME); + + if(*RawName == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + strcpy(*RawName, rawName); + + DebugPrint((2, "DiskGenerateDeviceName: generated \"%s\"\n", rawName)); + + return STATUS_SUCCESS; +} + + +VOID +DiskCreateSymbolicLinks( + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine will generate a symbolic link for the specified device object + using the well known form \\Device\HarddiskX\PartitionY, where X and Y are + filled in using the partition information in the device object's extension. + + This routine will not try to delete any previous symbolic link for the + same generated name - the caller must make sure the symbolic link has + been broken before calling this routine. + +Arguments: + + DeviceObject - the device object to make a well known name for + +Return Value: + + STATUS + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + + PDISK_DATA diskData = commonExtension->DriverData; + + WCHAR wideSourceName[64]; + UNICODE_STRING unicodeSourceName; + + NTSTATUS status; + + PAGED_CODE(); + + // + // Build the destination for the link first using the device name + // stored in the device object + // + + ASSERT(commonExtension->DeviceName.Buffer); + + if(!diskData->LinkStatus.WellKnownNameCreated) { + // + // Put together the source name using the partition and device number + // in the device extension and disk data segment + // + + swprintf(wideSourceName, L"\\Device\\Harddisk%d\\Partition%d", + commonExtension->PartitionZeroExtension->DeviceNumber, + (commonExtension->IsFdo ? + 0 : + commonExtension->PartitionNumber)); + + RtlInitUnicodeString(&unicodeSourceName, wideSourceName); + + DebugPrint((1, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n", + &unicodeSourceName, + &commonExtension->DeviceName)); + + status = IoCreateSymbolicLink(&unicodeSourceName, + &commonExtension->DeviceName); + + #if DBG + + if((status == STATUS_OBJECT_NAME_EXISTS) || + (status == STATUS_OBJECT_NAME_COLLISION)) { + + DebugPrint((1, "DiskCreateSymbolicLink: name %wZ already exists\n", + &unicodeSourceName)); + } + #endif + + if(NT_SUCCESS(status)){ + diskData->LinkStatus.WellKnownNameCreated = TRUE; + } + } + + if((!diskData->LinkStatus.PhysicalDriveLinkCreated) && + (commonExtension->IsFdo)) { + + // + // Create a physical drive N link using the device number we saved + // away during AddDevice. + // + + swprintf(wideSourceName, + L"\\DosDevices\\PhysicalDrive%d", + commonExtension->PartitionZeroExtension->DeviceNumber); + + RtlInitUnicodeString(&unicodeSourceName, wideSourceName); + + DebugPrint((1, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n", + &unicodeSourceName, + &(commonExtension->DeviceName))); + + status = IoCreateSymbolicLink(&unicodeSourceName, + &(commonExtension->DeviceName)); + +#if DBG + + if((status == STATUS_OBJECT_NAME_EXISTS) || + (status == STATUS_OBJECT_NAME_COLLISION)) { + + DebugPrint((1, "DiskCreateSymbolicLink: name %wZ already exists\n", + &unicodeSourceName)); + } +#endif + + if(NT_SUCCESS(status)) { + diskData->LinkStatus.PhysicalDriveLinkCreated = TRUE; + } + } else if(commonExtension->IsFdo == FALSE) { + diskData->LinkStatus.PhysicalDriveLinkCreated = FALSE; + } + + return; +} + + +VOID +DiskDeleteSymbolicLinks( + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine will delete the well known name (symlink) for the specified + device. It generates the link name using information stored in the + device extension + +Arguments: + + DeviceObject - the device object we are unlinking + +Return Value: + + status + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = commonExtension->DriverData; + + WCHAR wideLinkName[64]; + UNICODE_STRING unicodeLinkName; + + PAGED_CODE(); + + if(diskData->LinkStatus.WellKnownNameCreated) { + + swprintf(wideLinkName, + L"\\Device\\Harddisk%d\\Partition%d", + commonExtension->PartitionZeroExtension->DeviceNumber, + (commonExtension->IsFdo ? 0 : + commonExtension->PartitionNumber)); + + RtlInitUnicodeString(&unicodeLinkName, wideLinkName); + + IoDeleteSymbolicLink(&unicodeLinkName); + + diskData->LinkStatus.WellKnownNameCreated = FALSE; + } + + if(diskData->LinkStatus.PhysicalDriveLinkCreated) { + + ASSERT_FDO(DeviceObject); + + swprintf(wideLinkName, + L"\\DosDevices\\PhysicalDrive%d", + commonExtension->PartitionZeroExtension->DeviceNumber); + + RtlInitUnicodeString(&unicodeLinkName, wideLinkName); + + IoDeleteSymbolicLink(&unicodeLinkName); + + diskData->LinkStatus.PhysicalDriveLinkCreated = FALSE; + } + + return; +} + + +NTSTATUS +DiskRemoveDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ) + +/*++ + +Routine Description: + + This routine will release any resources the device may have allocated for + this device object and return. + +Arguments: + + DeviceObject - the device object being removed + +Return Value: + + status + +--*/ + +{ + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PDISK_DATA diskData = commonExtension->DriverData; + + PAGED_CODE(); + + // + // Handle query and cancel + // + + if((Type == IRP_MN_QUERY_REMOVE_DEVICE) || + (Type == IRP_MN_CANCEL_REMOVE_DEVICE)) { + return STATUS_SUCCESS; + } + + if(commonExtension->IsFdo) { + + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = + DeviceObject->DeviceExtension; + + // + // Purge the cached partition table (if any). + // + + DiskAcquirePartitioningLock(fdoExtension); + DiskInvalidatePartitionTable(fdoExtension, TRUE); + DiskReleasePartitioningLock(fdoExtension); + + // + // Delete our object directory. + // + + if(fdoExtension->AdapterDescriptor) { + ExFreePool(fdoExtension->AdapterDescriptor); + fdoExtension->AdapterDescriptor = NULL; + } + + if(fdoExtension->DeviceDescriptor) { + ExFreePool(fdoExtension->DeviceDescriptor); + fdoExtension->DeviceDescriptor = NULL; + } + + if(fdoExtension->SenseData) { + ExFreePool(fdoExtension->SenseData); + fdoExtension->SenseData = NULL; + } + + if(fdoExtension->DeviceDirectory != NULL) { + ZwMakeTemporaryObject(fdoExtension->DeviceDirectory); + ZwClose(fdoExtension->DeviceDirectory); + fdoExtension->DeviceDirectory = NULL; + } + + if(Type == IRP_MN_REMOVE_DEVICE) { + IoGetConfigurationInformation()->DiskCount--; + } + + } else { + + PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension; + + } + + DiskDeleteSymbolicLinks(DeviceObject); + + // + // Release the mounted device interface if we've set it. + // + + if(diskData->PartitionInterfaceString.Buffer != NULL) { + IoSetDeviceInterfaceState(&(diskData->PartitionInterfaceString), FALSE); + RtlFreeUnicodeString(&(diskData->PartitionInterfaceString)); + RtlInitUnicodeString(&(diskData->PartitionInterfaceString), NULL); + } + if(diskData->DiskInterfaceString.Buffer != NULL) { + IoSetDeviceInterfaceState(&(diskData->DiskInterfaceString), FALSE); + RtlFreeUnicodeString(&(diskData->DiskInterfaceString)); + RtlInitUnicodeString(&(diskData->DiskInterfaceString), NULL); + } + + ClassDeleteSrbLookasideList(commonExtension); + return STATUS_SUCCESS; +} + + +NTSTATUS +DiskStartFdo( + IN PDEVICE_OBJECT Fdo + ) + +/*++ + +Routine Description: + + This routine will query the underlying device for any information necessary + to complete initialization of the device. This will include physical + disk geometry, mode sense information and such. + + This routine does not perform partition enumeration - that is left to the + re-enumeration routine + + If this routine fails it will return an error value. It does not clean up + any resources - that is left for the Stop/Remove routine. + +Arguments: + + Fdo - a pointer to the functional device object for this device + +Return Value: + + status + +--*/ + +{ + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; + PCOMMON_DEVICE_EXTENSION commonExtension = &(fdoExtension->CommonExtension); + PDISK_DATA diskData = commonExtension->DriverData; + STORAGE_HOTPLUG_INFO hotplugInfo; + ULONG writeCacheOverride = DiskWriteCacheDefault; + DISK_CACHE_INFORMATION cacheInfo; + NTSTATUS status; + + PAGED_CODE(); + + // + // Get the hotplug information, so we can turn off write cache if needed + // + // NOTE: Capabilities info is not good enough to determine hotplugedness + // as we cannot determine device relations information and other + // dependencies. Get the hotplug info instead + // + + { + PIRP irp; + KEVENT event; + IO_STATUS_BLOCK statusBlock; + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + RtlZeroMemory(&hotplugInfo, sizeof(STORAGE_HOTPLUG_INFO)); + + irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_HOTPLUG_INFO, + Fdo, + NULL, + 0L, + &hotplugInfo, + sizeof(STORAGE_HOTPLUG_INFO), + FALSE, + &event, + &statusBlock); + + if (irp != NULL) { + + // send to self -- classpnp handles this + status = IoCallDriver(Fdo, irp); + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + NULL); + status = statusBlock.Status; + } + } + } + + // + // Clear the DEV_WRITE_CACHE flag now and set + // it below only if we read that from the disk + // + + CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE); + + if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE)) + { + // + // This flag overrides the user's setting, because faulty firmware + // may cause the filesystem to refuse to format media on this device + // + DebugPrint((1, + "DiskStartFdo: Shutting off write cache for %p due to %s\n", + Fdo, + "Possible Firmware Issue")); + + writeCacheOverride = DiskWriteCacheDisable; + } + else + { + // + // Look into the registry to see if the user + // has chosen to override the default setting + // + ClassGetDeviceParameter(fdoExtension, + DiskDeviceParameterSubkey, + DiskDeviceUserWriteCacheSetting, + &writeCacheOverride); + + if (writeCacheOverride == DiskWriteCacheDefault) + { + // + // The user has not overridden the default settings + // + if (hotplugInfo.DeviceHotplug && !hotplugInfo.WriteCacheEnableOverride) + { + DebugPrint((1, + "DiskStartFdo: Shutting off write cache for %p due to %s\n", + Fdo, + "Hotplug Device")); + + writeCacheOverride = DiskWriteCacheDisable; + } + else if (hotplugInfo.MediaHotplug) + { + DebugPrint((1, + "DiskStartFdo: Shutting off write cache for %p due to %s\n", + Fdo, + "Hotplug (unlockable) Media")); + + writeCacheOverride = DiskWriteCacheDisable; + } + else + { + // + // We enable write cache if this device has no specific issues + // + writeCacheOverride = DiskWriteCacheEnable; + } + } + } + + // + // Query the disk to see if write cache is enabled + // and set the DEV_WRITE_CACHE flag appropriately + // + + RtlZeroMemory(&cacheInfo, sizeof(DISK_CACHE_INFORMATION)); + + status = DiskGetCacheInformation(fdoExtension, &cacheInfo); + + if (NT_SUCCESS(status)) + { + if (cacheInfo.WriteCacheEnabled == TRUE) + { + if (writeCacheOverride == DiskWriteCacheDisable) + { + // + // Write cache is currently enabled on this + // device, but we would like to turn it off + // + cacheInfo.WriteCacheEnabled = FALSE; + + status = DiskSetCacheInformation(fdoExtension, &cacheInfo); + } + else + { + // + // The write cache setting either matches + // our needs or we don't care + // + SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE); + } + } + else + { + if (writeCacheOverride == DiskWriteCacheEnable) + { + // + // Write cache is currently disabled on this + // device, but we would like to turn it on + // + cacheInfo.WriteCacheEnabled = TRUE; + + status = DiskSetCacheInformation(fdoExtension, &cacheInfo); + + SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE); + } + } + } + + // + // In the event that there's a cached partition table flush it now. + // + + DiskAcquirePartitioningLock(fdoExtension); + DiskInvalidatePartitionTable(fdoExtension, TRUE); + DiskReleasePartitioningLock(fdoExtension); + + // + // Get the SCSI address if it's available for use with SMART ioctls. + // + + { + PIRP irp; + KEVENT event; + IO_STATUS_BLOCK statusBlock; + + KeInitializeEvent(&event, SynchronizationEvent, FALSE); + + irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_ADDRESS, + commonExtension->LowerDeviceObject, + NULL, + 0L, + &(diskData->ScsiAddress), + sizeof(SCSI_ADDRESS), + FALSE, + &event, + &statusBlock); + + if(irp != NULL) { + + + status = IoCallDriver(commonExtension->LowerDeviceObject, irp); + + if(status == STATUS_PENDING) { + KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + NULL); + status = statusBlock.Status; + } + } + } + + return STATUS_SUCCESS; + +} // end DiskStartFdo() + diff --git a/reactos/drivers/storage/classpnp/CMakeLists.txt b/reactos/drivers/storage/classpnp/CMakeLists.txt index 1d5672b34b6..f0f9884e48c 100644 --- a/reactos/drivers/storage/classpnp/CMakeLists.txt +++ b/reactos/drivers/storage/classpnp/CMakeLists.txt @@ -17,7 +17,7 @@ if(ARCH MATCHES i386) endif() endif() -spec2def(classpnp.sys class.spec) +spec2def(classpnp.sys classpnp.spec) list(APPEND SOURCE autorun.c @@ -35,11 +35,12 @@ list(APPEND SOURCE utils.c xferpkt.c class.rc - ${CMAKE_CURRENT_BINARY_DIR}/class.def) + ${CMAKE_CURRENT_BINARY_DIR}/classpnp.def) add_library(classpnp SHARED ${SOURCE}) add_pch(classpnp classp.h) target_link_libraries(classpnp ${PSEH_LIB} libcntpr) set_module_type(classpnp kernelmodedriver) add_importlibs(classpnp hal ntoskrnl) +add_importlib_target(classpnp.spec) add_cd_file(TARGET classpnp DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/reactos/drivers/storage/classpnp/classpnp.rbuild b/reactos/drivers/storage/classpnp/classpnp.rbuild index 2eb4afddd2b..77ae9f2666d 100644 --- a/reactos/drivers/storage/classpnp/classpnp.rbuild +++ b/reactos/drivers/storage/classpnp/classpnp.rbuild @@ -2,7 +2,7 @@ - + ntoskrnl hal pseh diff --git a/reactos/drivers/storage/classpnp/class.spec b/reactos/drivers/storage/classpnp/classpnp.spec similarity index 100% rename from reactos/drivers/storage/classpnp/class.spec rename to reactos/drivers/storage/classpnp/classpnp.spec diff --git a/reactos/drivers/usb/CMakeLists.txt b/reactos/drivers/usb/CMakeLists.txt index a86fdfa7adb..9afa4fb8963 100644 --- a/reactos/drivers/usb/CMakeLists.txt +++ b/reactos/drivers/usb/CMakeLists.txt @@ -1,8 +1,6 @@ - -if(NOT ${ARCH} MATCHES amd64) - add_subdirectory(nt4compat) -endif() +add_subdirectory(usbccgp) add_subdirectory(usbd) -#add_subdirectory(usbehci) The USB branch has usbehci_new -#add_subdirectory(usbhub) Compiles, just skipped in trunk -#add_subdirectory(usbstor) Compiles, just skipped in trunk +add_subdirectory(usbehci) +add_subdirectory(usbhub) +add_subdirectory(usbohci) +add_subdirectory(usbstor) \ No newline at end of file diff --git a/reactos/drivers/usb/directory.rbuild b/reactos/drivers/usb/directory.rbuild index e81eb8b291a..8640fe372b0 100644 --- a/reactos/drivers/usb/directory.rbuild +++ b/reactos/drivers/usb/directory.rbuild @@ -1,16 +1,22 @@ - - + + - + diff --git a/reactos/drivers/usb/usbccgp/CMakeLists.txt b/reactos/drivers/usb/usbccgp/CMakeLists.txt new file mode 100644 index 00000000000..f3da240ef0c --- /dev/null +++ b/reactos/drivers/usb/usbccgp/CMakeLists.txt @@ -0,0 +1,16 @@ +remove_definitions(-D_WIN32_WINNT=0x502) +add_definitions(-D_WIN32_WINNT=0x600) + + +add_definitions(-DDEBUG_MODE) + +include_directories(${REACTOS_SOURCE_DIR}/ntoskrnl/include) + +add_library(usbccgp SHARED descriptor.c fdo.c function.c misc.c pdo.c usbccgp.c usbccgp.rc) + +target_link_libraries(usbccgp ${PSEH_LIB}) + +set_module_type(usbccgp kernelmodedriver) +add_importlibs(usbccgp ntoskrnl hal usbd) + +add_cd_file(TARGET usbccgp DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/reactos/drivers/usb/usbccgp/descriptor.c b/reactos/drivers/usb/usbccgp/descriptor.c new file mode 100644 index 00000000000..e8e74f8ba5f --- /dev/null +++ b/reactos/drivers/usb/usbccgp/descriptor.c @@ -0,0 +1,493 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbccgp/descriptor.c + * PURPOSE: USB device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + * Cameron Gutman + */ + +#include "usbccgp.h" + +NTSTATUS +NTAPI +USBCCGP_GetDescriptor( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR DescriptorType, + IN ULONG DescriptorLength, + IN UCHAR DescriptorIndex, + IN LANGID LanguageId, + OUT PVOID *OutDescriptor) +{ + PURB Urb; + NTSTATUS Status; + PVOID Descriptor; + + // + // sanity checks + // + ASSERT(DeviceObject); + ASSERT(OutDescriptor); + ASSERT(DescriptorLength); + + // + // first allocate descriptor buffer + // + Descriptor = AllocateItem(NonPagedPool, DescriptorLength); + if (!Descriptor) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // allocate urb + // + Urb = (PURB) AllocateItem(NonPagedPool, sizeof(URB)); + if (!Urb) + { + // + // no memory + // + FreeItem(Descriptor); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize urb + // + UsbBuildGetDescriptorRequest(Urb, + sizeof(Urb->UrbControlDescriptorRequest), + DescriptorType, + DescriptorIndex, + LanguageId, + Descriptor, + NULL, + DescriptorLength, + NULL); + + // + // submit urb + // + Status = USBCCGP_SyncUrbRequest(DeviceObject, Urb); + + // + // free urb + // + FreeItem(Urb); + + if (NT_SUCCESS(Status)) + { + // + // store result + // + *OutDescriptor = Descriptor; + } + + // + // done + // + return Status; +} + + +NTSTATUS +USBCCGP_GetDescriptors( + IN PDEVICE_OBJECT DeviceObject) +{ + NTSTATUS Status; + PFDO_DEVICE_EXTENSION DeviceExtension; + USHORT DescriptorLength; + + // + // get device extension + // + DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // first get device descriptor + // + Status = USBCCGP_GetDescriptor(DeviceExtension->NextDeviceObject, USB_DEVICE_DESCRIPTOR_TYPE, sizeof(USB_DEVICE_DESCRIPTOR), 0, 0, (PVOID*)&DeviceExtension->DeviceDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to get device descriptor + // + DeviceExtension->DeviceDescriptor = NULL; + return Status; + } + + // + // now get basic configuration descriptor + // + Status = USBCCGP_GetDescriptor(DeviceExtension->NextDeviceObject, USB_CONFIGURATION_DESCRIPTOR_TYPE, sizeof(USB_CONFIGURATION_DESCRIPTOR), 0, 0, (PVOID*)&DeviceExtension->ConfigurationDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to get configuration descriptor + // + FreeItem(DeviceExtension->DeviceDescriptor); + DeviceExtension->DeviceDescriptor = NULL; + return Status; + } + + // + // backup length + // + DescriptorLength = DeviceExtension->ConfigurationDescriptor->wTotalLength; + + // + // release basic descriptor + // + FreeItem(DeviceExtension->ConfigurationDescriptor); + DeviceExtension->ConfigurationDescriptor = NULL; + + // + // allocate full descriptor + // + Status = USBCCGP_GetDescriptor(DeviceExtension->NextDeviceObject, USB_CONFIGURATION_DESCRIPTOR_TYPE, DescriptorLength, 0, 0, (PVOID*)&DeviceExtension->ConfigurationDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to get configuration descriptor + // + FreeItem(DeviceExtension->DeviceDescriptor); + DeviceExtension->DeviceDescriptor = NULL; + return Status; + } + return Status; +} + +ULONG +CountInterfaceDescriptors( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) +{ + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + PVOID CurrentPosition; + ULONG Count = 0; + + // + // enumerate all interfaces + // + CurrentPosition = ConfigurationDescriptor; + do + { + // + // find next descriptor + // + InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor, CurrentPosition, -1, -1, -1, -1, -1); + if (!InterfaceDescriptor) + break; + + // + // advance to next descriptor + // + CurrentPosition = (PVOID)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength); + + // + // increment descriptor count + // + Count++; + + }while(TRUE); + + // + // done + // + return Count; +} + +NTSTATUS +AllocateInterfaceDescriptorsArray( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + OUT PUSB_INTERFACE_DESCRIPTOR **OutArray) +{ + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + PVOID CurrentPosition; + ULONG Count = 0; + PUSB_INTERFACE_DESCRIPTOR *Array; + + Count = CountInterfaceDescriptors(ConfigurationDescriptor); + ASSERT(Count); + + // + // allocate array + // + Array = AllocateItem(NonPagedPool, sizeof(PUSB_INTERFACE_DESCRIPTOR) * Count); + if (!Array) + return STATUS_INSUFFICIENT_RESOURCES; + + // + // enumerate all interfaces + // + CurrentPosition = ConfigurationDescriptor; + Count = 0; + do + { + // + // find next descriptor + // + InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor, CurrentPosition, -1, -1, -1, -1, -1); + if (!InterfaceDescriptor) + break; + + // + // store descriptor + // + Array[Count] = InterfaceDescriptor; + Count++; + + // + // advance to next descriptor + // + CurrentPosition = (PVOID)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength); + + }while(TRUE); + + // + // store result + // + *OutArray = Array; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +USBCCGP_ScanConfigurationDescriptor( + IN OUT PFDO_DEVICE_EXTENSION FDODeviceExtension, + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) +{ + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + ULONG InterfaceIndex = 0; + PVOID CurrentPosition; + ULONG DescriptorCount; + + // + // sanity checks + // + ASSERT(ConfigurationDescriptor); + ASSERT(ConfigurationDescriptor->bNumInterfaces); + + // + // count all interface descriptors + // + DescriptorCount = ConfigurationDescriptor->bNumInterfaces; + + // + // allocate array holding the interface descriptors + // + FDODeviceExtension->InterfaceList = AllocateItem(NonPagedPool, sizeof(USBD_INTERFACE_LIST_ENTRY) * (DescriptorCount + 1)); + if (!FDODeviceExtension->InterfaceList) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + CurrentPosition = ConfigurationDescriptor; + do + { + // + // parse configuration descriptor + // + InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor, ConfigurationDescriptor, InterfaceIndex, -1, -1, -1, -1); + ASSERT(InterfaceDescriptor); + if (InterfaceDescriptor) + { + // + // store in interface list + // + FDODeviceExtension->InterfaceList[FDODeviceExtension->InterfaceListCount].InterfaceDescriptor = InterfaceDescriptor; + FDODeviceExtension->InterfaceListCount++; + CurrentPosition = (PVOID)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength); + } + + // + // move to next interface + // + InterfaceIndex++; + + }while(InterfaceIndex < DescriptorCount); + + // + // sanity check + // + ASSERT(FDODeviceExtension->InterfaceListCount); + + // + // done + // + return STATUS_SUCCESS; +} + +VOID +DumpConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) +{ + DPRINT1("Dumping ConfigurationDescriptor %x\n", ConfigurationDescriptor); + DPRINT1("bLength %x\n", ConfigurationDescriptor->bLength); + DPRINT1("bDescriptorType %x\n", ConfigurationDescriptor->bDescriptorType); + DPRINT1("wTotalLength %x\n", ConfigurationDescriptor->wTotalLength); + DPRINT1("bNumInterfaces %x\n", ConfigurationDescriptor->bNumInterfaces); + DPRINT1("bConfigurationValue %x\n", ConfigurationDescriptor->bConfigurationValue); + DPRINT1("iConfiguration %x\n", ConfigurationDescriptor->iConfiguration); + DPRINT1("bmAttributes %x\n", ConfigurationDescriptor->bmAttributes); + DPRINT1("MaxPower %x\n", ConfigurationDescriptor->MaxPower); +} + +NTSTATUS +USBCCGP_SelectInterface( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension, + IN ULONG InterfaceIndex) +{ + NTSTATUS Status; + PURB Urb; + + // + // allocate urb + // + Urb = AllocateItem(NonPagedPool, GET_SELECT_INTERFACE_REQUEST_SIZE(DeviceExtension->InterfaceList[InterfaceIndex].InterfaceDescriptor->bNumEndpoints)); + if (!Urb) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // now prepare interface urb + // + UsbBuildSelectInterfaceRequest(Urb, GET_SELECT_INTERFACE_REQUEST_SIZE(DeviceExtension->InterfaceList[InterfaceIndex].InterfaceDescriptor->bNumEndpoints), DeviceExtension->ConfigurationHandle, DeviceExtension->InterfaceList[InterfaceIndex].InterfaceDescriptor->bInterfaceNumber, DeviceExtension->InterfaceList[InterfaceIndex].InterfaceDescriptor->bAlternateSetting); + + // + // now select the interface + // + Status = USBCCGP_SyncUrbRequest(DeviceExtension->NextDeviceObject, Urb); + + // + // did it succeeed + // + if (NT_SUCCESS(Status)) + { + // + // update configuration info + // + ASSERT(Urb->UrbSelectInterface.Interface.Length == DeviceExtension->InterfaceList[InterfaceIndex].Interface->Length); + RtlCopyMemory(DeviceExtension->InterfaceList[InterfaceIndex].Interface, &Urb->UrbSelectInterface.Interface, Urb->UrbSelectInterface.Interface.Length); + } + + // + // free urb + // + FreeItem(Urb); + + // + // done + // + return Status; +} + +NTSTATUS +USBCCGP_SelectConfiguration( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension) +{ + PUSBD_INTERFACE_INFORMATION InterfaceInformation; + NTSTATUS Status; + PURB Urb; + ULONG Index; + + // + // now scan configuration descriptors + // + Status = USBCCGP_ScanConfigurationDescriptor(DeviceExtension, DeviceExtension->ConfigurationDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to scan + // + return Status; + } + + // + // now allocate the urb + // + Urb = USBD_CreateConfigurationRequestEx(DeviceExtension->ConfigurationDescriptor, DeviceExtension->InterfaceList); + if (!Urb) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // submit urb + // + Status = USBCCGP_SyncUrbRequest(DeviceExtension->NextDeviceObject, Urb); + if (!NT_SUCCESS(Status)) + { + // + // failed to set configuration + // + DPRINT1("USBCCGP_SyncUrbRequest failed to set interface %x\n", Status); + ExFreePool(Urb); + return Status; + } + + // + // get interface information + // + InterfaceInformation = &Urb->UrbSelectConfiguration.Interface; + for(Index = 0; Index < DeviceExtension->InterfaceListCount; Index++) + { + // + // allocate buffer to store interface information + // + DeviceExtension->InterfaceList[Index].Interface = AllocateItem(NonPagedPool, InterfaceInformation->Length); + if (!DeviceExtension->InterfaceList[Index].Interface) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy interface information + // + RtlCopyMemory(DeviceExtension->InterfaceList[Index].Interface, InterfaceInformation, InterfaceInformation->Length); + + // + // move to next interface + // + InterfaceInformation = (PUSBD_INTERFACE_INFORMATION)((ULONG_PTR)InterfaceInformation + InterfaceInformation->Length); + } + + + // + // store pipe handle + // + DeviceExtension->ConfigurationHandle = Urb->UrbSelectConfiguration.ConfigurationHandle; + + // + // free interface list & urb + // + ExFreePool(Urb); + + // + // done + // + return Status; +} + diff --git a/reactos/drivers/usb/usbccgp/fdo.c b/reactos/drivers/usb/usbccgp/fdo.c new file mode 100644 index 00000000000..74c89eb0845 --- /dev/null +++ b/reactos/drivers/usb/usbccgp/fdo.c @@ -0,0 +1,720 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbccgp/fdo.c + * PURPOSE: USB device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + * Cameron Gutman + */ + +#include "usbccgp.h" + +NTSTATUS +NTAPI +FDO_QueryCapabilitiesCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + // + // set event + // + KeSetEvent((PRKEVENT)Context, 0, FALSE); + + // + // completion is done in the HidClassFDO_QueryCapabilities routine + // + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +FDO_QueryCapabilities( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PDEVICE_CAPABILITIES Capabilities) +{ + PIRP Irp; + KEVENT Event; + NTSTATUS Status; + PIO_STACK_LOCATION IoStack; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // init event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // now allocte the irp + // + Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + if (!Irp) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get next stack location + // + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // init stack location + // + IoStack->MajorFunction = IRP_MJ_PNP; + IoStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES; + IoStack->Parameters.DeviceCapabilities.Capabilities = Capabilities; + + // + // set completion routine + // + IoSetCompletionRoutine(Irp, FDO_QueryCapabilitiesCompletionRoutine, (PVOID)&Event, TRUE, TRUE, TRUE); + + // + // init capabilities + // + RtlZeroMemory(Capabilities, sizeof(DEVICE_CAPABILITIES)); + Capabilities->Size = sizeof(DEVICE_CAPABILITIES); + Capabilities->Version = 1; // FIXME hardcoded constant + Capabilities->Address = MAXULONG; + Capabilities->UINumber = MAXULONG; + + // + // pnp irps have default completion code + // + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + + // + // call lower device + // + Status = IoCallDriver(FDODeviceExtension->NextDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + // + // wait for completion + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + + // + // get status + // + Status = Irp->IoStatus.Status; + + // + // complete request + // + IoFreeIrp(Irp); + + // + // done + // + return Status; +} + +NTSTATUS +FDO_DeviceRelations( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + ULONG DeviceCount = 0; + ULONG Index; + PDEVICE_RELATIONS DeviceRelations; + PIO_STACK_LOCATION IoStack; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // check if relation type is BusRelations + // + if (IoStack->Parameters.QueryDeviceRelations.Type != BusRelations) + { + // + // FDO always only handles bus relations + // + return USBCCGP_SyncForwardIrp(FDODeviceExtension->NextDeviceObject, Irp); + } + + // + // go through array and count device objects + // + for(Index = 0; Index < FDODeviceExtension->FunctionDescriptorCount; Index++) + { + if (FDODeviceExtension->ChildPDO[Index]) + { + // + // child pdo + // + DeviceCount++; + } + } + + // + // allocate device relations + // + DeviceRelations = (PDEVICE_RELATIONS)AllocateItem(PagedPool, sizeof(DEVICE_RELATIONS) + (DeviceCount > 1 ? (DeviceCount-1) * sizeof(PDEVICE_OBJECT) : 0)); + if (!DeviceRelations) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add device objects + // + for(Index = 0; Index < FDODeviceExtension->FunctionDescriptorCount; Index++) + { + if (FDODeviceExtension->ChildPDO[Index]) + { + // + // store child pdo + // + DeviceRelations->Objects[DeviceRelations->Count] = FDODeviceExtension->ChildPDO[Index]; + + // + // add reference + // + ObReferenceObject(FDODeviceExtension->ChildPDO[Index]); + + // + // increment count + // + DeviceRelations->Count++; + } + } + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + + // + // request completed successfully + // + return STATUS_SUCCESS; +} + +NTSTATUS +FDO_CreateChildPdo( + IN PDEVICE_OBJECT DeviceObject) +{ + NTSTATUS Status; + PDEVICE_OBJECT PDODeviceObject; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + ULONG Index; + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // lets create array for the child PDO + // + FDODeviceExtension->ChildPDO = AllocateItem(NonPagedPool, sizeof(PDEVICE_OBJECT) * FDODeviceExtension->FunctionDescriptorCount); + if (!FDODeviceExtension->ChildPDO) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // create pdo for each function + // + for(Index = 0; Index < FDODeviceExtension->FunctionDescriptorCount; Index++) + { + // + // create the PDO + // + Status = IoCreateDevice(FDODeviceExtension->DriverObject, sizeof(PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_USB, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &PDODeviceObject); + if (!NT_SUCCESS(Status)) + { + // + // failed to create device object + // + DPRINT1("IoCreateDevice failed with %x\n", Status); + return Status; + } + + // + // store in array + // + FDODeviceExtension->ChildPDO[Index] = PDODeviceObject; + + // + // get device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)PDODeviceObject->DeviceExtension; + RtlZeroMemory(PDODeviceExtension, sizeof(PDO_DEVICE_EXTENSION)); + + // + // init device extension + // + PDODeviceExtension->Common.IsFDO = FALSE; + PDODeviceExtension->FunctionDescriptor = &FDODeviceExtension->FunctionDescriptor[Index]; + PDODeviceExtension->NextDeviceObject = DeviceObject; + PDODeviceExtension->FunctionIndex = Index; + PDODeviceExtension->FDODeviceExtension = FDODeviceExtension; + PDODeviceExtension->InterfaceList = FDODeviceExtension->InterfaceList; + PDODeviceExtension->InterfaceListCount = FDODeviceExtension->InterfaceListCount; + PDODeviceExtension->ConfigurationHandle = FDODeviceExtension->ConfigurationHandle; + PDODeviceExtension->ConfigurationDescriptor = FDODeviceExtension->ConfigurationDescriptor; + RtlCopyMemory(&PDODeviceExtension->Capabilities, &FDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES)); + RtlCopyMemory(&PDODeviceExtension->DeviceDescriptor, &FDODeviceExtension->DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); + + // + // patch the stack size + // + PDODeviceObject->StackSize = DeviceObject->StackSize + 1; + + // + // set device flags + // + PDODeviceObject->Flags |= DO_DIRECT_IO | DO_MAP_IO_BUFFER; + + // + // device is initialized + // + PDODeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + } + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +FDO_StartDevice( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + NTSTATUS Status; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // first start lower device + // + Status = USBCCGP_SyncForwardIrp(FDODeviceExtension->NextDeviceObject, Irp); + + if (!NT_SUCCESS(Status)) + { + // + // failed to start lower device + // + DPRINT1("FDO_StartDevice lower device failed to start with %x\n", Status); + return Status; + } + + // get descriptors + Status = USBCCGP_GetDescriptors(DeviceObject); + if (!NT_SUCCESS(Status)) + { + // failed to start lower device + DPRINT1("FDO_StartDevice failed to get descriptors with %x\n", Status); + return Status; + } + + // get capabilities + Status = FDO_QueryCapabilities(DeviceObject, &FDODeviceExtension->Capabilities); + if (!NT_SUCCESS(Status)) + { + // failed to start lower device + DPRINT1("FDO_StartDevice failed to get capabilities with %x\n", Status); + return Status; + } + + // now select the configuration + Status = USBCCGP_SelectConfiguration(DeviceObject, FDODeviceExtension); + if (!NT_SUCCESS(Status)) + { + // failed to select interface + DPRINT1("FDO_StartDevice failed to get capabilities with %x\n", Status); + return Status; + } + + // query bus interface + USBCCGP_QueryInterface(FDODeviceExtension->NextDeviceObject, &FDODeviceExtension->BusInterface); + + // now enumerate the functions + Status = USBCCGP_EnumerateFunctions(DeviceObject); + if (!NT_SUCCESS(Status)) + { + // failed to enumerate functions + DPRINT1("Failed to enumerate functions with %x\n", Status); + return Status; + } + + // + // sanity checks + // + ASSERT(FDODeviceExtension->FunctionDescriptorCount); + ASSERT(FDODeviceExtension->FunctionDescriptor); + DumpFunctionDescriptor(FDODeviceExtension->FunctionDescriptor, FDODeviceExtension->FunctionDescriptorCount); + + // + // now create the pdo + // + Status = FDO_CreateChildPdo(DeviceObject); + if (!NT_SUCCESS(Status)) + { + // + // failed + // + DPRINT1("FDO_CreateChildPdo failed with %x\n", Status); + return Status; + } + + // + // inform pnp manager of new device objects + // + IoInvalidateDeviceRelations(FDODeviceExtension->PhysicalDeviceObject, BusRelations); + + // + // done + // + DPRINT("[USBCCGP] FDO initialized successfully\n"); + return Status; +} + +NTSTATUS +FDO_HandlePnp( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // get device extension + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + + // get stack location + IoStack = IoGetCurrentIrpStackLocation(Irp); + DPRINT("[USBCCGP] PnP Minor %x\n", IoStack->MinorFunction); + switch(IoStack->MinorFunction) + { + case IRP_MN_REMOVE_DEVICE: + { + /* Send the IRP down the stack */ + Status = USBCCGP_SyncForwardIrp(FDODeviceExtension->NextDeviceObject, Irp); + if (NT_SUCCESS(Status)) + { + // + // Detach from the device stack + // + IoDetachDevice(FDODeviceExtension->NextDeviceObject); + + // + // Delete the device object + // + IoDeleteDevice(DeviceObject); + } + + // + // request completed + // + break; + } + case IRP_MN_START_DEVICE: + { + // + // start the device + // + Status = FDO_StartDevice(DeviceObject, Irp); + break; + } + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + // + // handle device relations + // + Status = FDO_DeviceRelations(DeviceObject, Irp); + break; + } + case IRP_MN_QUERY_CAPABILITIES: + { + // + // copy capabilities + // + RtlCopyMemory(IoStack->Parameters.DeviceCapabilities.Capabilities, &FDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES)); + Status = USBCCGP_SyncForwardIrp(FDODeviceExtension->NextDeviceObject, Irp); + if (NT_SUCCESS(Status)) + { + // + // surprise removal ok + // + IoStack->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE; + } + break; + } + case IRP_MN_QUERY_REMOVE_DEVICE: + case IRP_MN_QUERY_STOP_DEVICE: + { + // + // sure + // + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(FDODeviceExtension->NextDeviceObject, Irp); + } + default: + { + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(FDODeviceExtension->NextDeviceObject, Irp); + } + + } + + // + // complete request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; +} + +NTSTATUS +FDO_HandleResetCyclePort( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + PLIST_ENTRY ListHead, Entry; + LIST_ENTRY TempList; + PUCHAR ResetActive; + PIRP ListIrp; + KIRQL OldLevel; + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // get stack location + IoStack = IoGetCurrentIrpStackLocation(Irp); + DPRINT("FDO_HandleResetCyclePort IOCTL %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode); + + if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_RESET_PORT) + { + // + // use reset port list + // + ListHead = &FDODeviceExtension->ResetPortListHead; + ResetActive = &FDODeviceExtension->ResetPortActive; + } + else + { + // + // use cycle port list + // + ListHead = &FDODeviceExtension->CyclePortListHead; + ResetActive = &FDODeviceExtension->CyclePortActive; + } + + // + // acquire lock + // + KeAcquireSpinLock(&FDODeviceExtension->Lock, &OldLevel); + + if (*ResetActive) + { + // + // insert into pending list + // + InsertTailList(ListHead, &Irp->Tail.Overlay.ListEntry); + + // + // mark irp pending + // + IoMarkIrpPending(Irp); + Status = STATUS_PENDING; + + // + // release lock + // + KeReleaseSpinLock(&FDODeviceExtension->Lock, OldLevel); + } + else + { + // + // mark reset active + // + *ResetActive = TRUE; + + // + // release lock + // + KeReleaseSpinLock(&FDODeviceExtension->Lock, OldLevel); + + // + // forward request synchronized + // + USBCCGP_SyncForwardIrp(FDODeviceExtension->NextDeviceObject, Irp); + + // + // reacquire lock + // + KeAcquireSpinLock(&FDODeviceExtension->Lock, &OldLevel); + + // + // mark reset as completed + // + *ResetActive = FALSE; + + // + // move all requests into temporary list + // + InitializeListHead(&TempList); + while(!IsListEmpty(ListHead)) + { + Entry = RemoveHeadList(ListHead); + InsertTailList(&TempList, Entry); + } + + // + // release lock + // + KeReleaseSpinLock(&FDODeviceExtension->Lock, OldLevel); + + // + // complete pending irps + // + while(!IsListEmpty(&TempList)) + { + Entry = RemoveHeadList(&TempList); + ListIrp = (PIRP)CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry); + + // + // complete request with status success + // + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + // + // status success + // + Status = STATUS_SUCCESS; + } + + return Status; +} + + + +NTSTATUS +FDO_HandleInternalDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // get stack location + IoStack = IoGetCurrentIrpStackLocation(Irp); + + if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_RESET_PORT || + IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_CYCLE_PORT) + { + // + // handle reset / cycle ports + // + Status = FDO_HandleResetCyclePort(DeviceObject, Irp); + DPRINT("FDO_HandleResetCyclePort Status %x\n", Status); + if (Status != STATUS_PENDING) + { + // + // complete request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + return Status; + } + + // + // forward and forget request + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(FDODeviceExtension->NextDeviceObject, Irp); +} + +NTSTATUS +FDO_Dispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + + /* get stack location */ + IoStack = IoGetCurrentIrpStackLocation(Irp); + + switch(IoStack->MajorFunction) + { + case IRP_MJ_PNP: + return FDO_HandlePnp(DeviceObject, Irp); + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + return FDO_HandleInternalDeviceControl(DeviceObject, Irp); + default: + DPRINT1("FDO_Dispatch Function %x not implemented\n", IoStack->MajorFunction); + ASSERT(FALSE); + Status = Irp->IoStatus.Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + +} + + diff --git a/reactos/drivers/usb/usbccgp/function.c b/reactos/drivers/usb/usbccgp/function.c new file mode 100644 index 00000000000..2840166b803 --- /dev/null +++ b/reactos/drivers/usb/usbccgp/function.c @@ -0,0 +1,909 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbccgp/descriptor.c + * PURPOSE: USB device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + * Cameron Gutman + */ + +#include "usbccgp.h" + +NTSTATUS +USBCCGP_QueryInterface( + IN PDEVICE_OBJECT DeviceObject, + OUT PUSBC_DEVICE_CONFIGURATION_INTERFACE_V1 BusInterface) +{ + KEVENT Event; + NTSTATUS Status; + PIRP Irp; + IO_STATUS_BLOCK IoStatus; + PIO_STACK_LOCATION Stack; + + // + // sanity checks + // + ASSERT(DeviceObject); + + // + // initialize event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // init interface + // + RtlZeroMemory(BusInterface, sizeof(USBC_DEVICE_CONFIGURATION_INTERFACE_V1)); + BusInterface->Version = USBC_DEVICE_CONFIGURATION_INTERFACE_VERSION_1; + BusInterface->Size = sizeof(USBC_DEVICE_CONFIGURATION_INTERFACE_V1); + + // + // create irp + // + Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, + DeviceObject, + NULL, + 0, + NULL, + &Event, + &IoStatus); + + // + // was irp built + // + if (Irp == NULL) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize request + // + Stack=IoGetNextIrpStackLocation(Irp); + Stack->MajorFunction = IRP_MJ_PNP; + Stack->MinorFunction = IRP_MN_QUERY_INTERFACE; + Stack->Parameters.QueryInterface.Size = sizeof(BUS_INTERFACE_STANDARD); + Stack->Parameters.QueryInterface.InterfaceType = (LPGUID)&USB_BUS_INTERFACE_USBC_CONFIGURATION_GUID; + Stack->Parameters.QueryInterface.Version = 2; + Stack->Parameters.QueryInterface.Interface = (PINTERFACE)&BusInterface; + Stack->Parameters.QueryInterface.InterfaceSpecificData = NULL; + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + + // + // call driver + // + Status= IoCallDriver(DeviceObject, Irp); + + // + // did operation complete + // + if (Status == STATUS_PENDING) + { + // + // wait for completion + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + // + // collect status + // + Status = IoStatus.Status; + } + + return Status; +} + +NTSTATUS +USBCCGP_CustomEnumWithInterface( + IN PDEVICE_OBJECT DeviceObject) +{ + PFDO_DEVICE_EXTENSION FDODeviceExtension; + ULONG FunctionDescriptorBufferLength = 0; + NTSTATUS Status; + PUSBC_FUNCTION_DESCRIPTOR FunctionDescriptorBuffer = NULL; + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + if (FDODeviceExtension->BusInterface.StartDeviceCallback == NULL) + { + // + // not supported + // + return STATUS_NOT_SUPPORTED; + } + + // + // invoke callback + // + Status = FDODeviceExtension->BusInterface.StartDeviceCallback(FDODeviceExtension->DeviceDescriptor, + FDODeviceExtension->ConfigurationDescriptor, + &FunctionDescriptorBuffer, + &FunctionDescriptorBufferLength, + DeviceObject, + FDODeviceExtension->PhysicalDeviceObject); + + DPRINT("USBCCGP_CustomEnumWithInterface Status %x\n", Status); + if (!NT_SUCCESS(Status)) + { + // + // failed + // + return Status; + } + + DPRINT("FunctionDescriptorBufferLength %lu\n", FunctionDescriptorBufferLength); + DPRINT("FunctionDescriptorBuffer %p\n", FunctionDescriptorBuffer); + + // + // assume length % function buffer size + // + ASSERT(FunctionDescriptorBufferLength); + ASSERT(FunctionDescriptorBufferLength % sizeof(USBC_FUNCTION_DESCRIPTOR) == 0); + + // + // store result + // + FDODeviceExtension->FunctionDescriptor = FunctionDescriptorBuffer; + FDODeviceExtension->FunctionDescriptorCount = FunctionDescriptorBufferLength / sizeof(USBC_FUNCTION_DESCRIPTOR); + + // + // success + // + return STATUS_SUCCESS; +} + +ULONG +USBCCGP_CountAssociationDescriptors( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) +{ + PUSB_INTERFACE_ASSOCIATION_DESCRIPTOR Descriptor; + PUCHAR Offset, End; + ULONG Count = 0; + + // + // init offsets + // + Offset = (PUCHAR)ConfigurationDescriptor + ConfigurationDescriptor->bLength; + End = (PUCHAR)ConfigurationDescriptor + ConfigurationDescriptor->wTotalLength; + + while(Offset < End) + { + // + // get association descriptor + // + Descriptor = (PUSB_INTERFACE_ASSOCIATION_DESCRIPTOR)Offset; + + if (Descriptor->bLength == sizeof(USB_INTERFACE_ASSOCIATION_DESCRIPTOR) && Descriptor->bDescriptorType == USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE) + { + // + // found descriptor + // + Count++; + } + + // + // move to next descriptor + // + Offset += Descriptor->bLength; + } + + // + // done + // + return Count; +} + +PUSB_INTERFACE_ASSOCIATION_DESCRIPTOR +USBCCGP_GetAssociationDescriptorAtIndex( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + IN ULONG Index) +{ + PUSB_INTERFACE_ASSOCIATION_DESCRIPTOR Descriptor; + PUCHAR Offset, End; + ULONG Count = 0; + + // + // init offsets + // + Offset = (PUCHAR)ConfigurationDescriptor + ConfigurationDescriptor->bLength; + End = (PUCHAR)ConfigurationDescriptor + ConfigurationDescriptor->wTotalLength; + + while(Offset < End) + { + // + // get association descriptor + // + Descriptor = (PUSB_INTERFACE_ASSOCIATION_DESCRIPTOR)Offset; + + if (Descriptor->bLength == sizeof(USB_INTERFACE_ASSOCIATION_DESCRIPTOR) && Descriptor->bDescriptorType == USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE) + { + if (Index == Count) + { + // + // found descriptor + // + return Descriptor; + } + + // + // not the searched one + // + Count++; + } + + // + // move to next descriptor + // + Offset += Descriptor->bLength; + } + + // + // failed to find descriptor at the specified index + // + return NULL; +} + +NTSTATUS +USBCCGP_InitInterfaceListOfFunctionDescriptor( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + IN PUSB_INTERFACE_ASSOCIATION_DESCRIPTOR AssociationDescriptor, + OUT PUSBC_FUNCTION_DESCRIPTOR FunctionDescriptor) +{ + PUSB_INTERFACE_DESCRIPTOR Descriptor; + PUCHAR Offset, End; + ULONG Count = 0; + + // + // init offsets + // + Offset = (PUCHAR)AssociationDescriptor + AssociationDescriptor->bLength; + End = (PUCHAR)ConfigurationDescriptor + ConfigurationDescriptor->wTotalLength; + + while(Offset < End) + { + // + // get association descriptor + // + Descriptor = (PUSB_INTERFACE_DESCRIPTOR)Offset; + + if (Descriptor->bLength == sizeof(USB_INTERFACE_DESCRIPTOR) && Descriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE) + { + // + // store interface descriptor + // + FunctionDescriptor->InterfaceDescriptorList[Count] = Descriptor; + Count++; + + if (Count == AssociationDescriptor->bInterfaceCount) + { + // + // got all interfaces + // + return STATUS_SUCCESS; + } + } + + if (Descriptor->bLength == sizeof(USB_INTERFACE_ASSOCIATION_DESCRIPTOR) && Descriptor->bDescriptorType == USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE) + { + // + // WTF? a association descriptor which overlaps the next association descriptor + // + DPRINT1("Invalid association descriptor\n"); + ASSERT(FALSE); + return STATUS_UNSUCCESSFUL; + } + + // + // move to next descriptor + // + Offset += Descriptor->bLength; + } + + // + // invalid association descriptor + // + DPRINT1("Invalid association descriptor\n"); + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS +USBCCGP_InitFunctionDescriptor( + IN PFDO_DEVICE_EXTENSION FDODeviceExtension, + IN ULONG FunctionNumber, + OUT PUSBC_FUNCTION_DESCRIPTOR FunctionDescriptor) +{ + PUSB_INTERFACE_ASSOCIATION_DESCRIPTOR Descriptor; + NTSTATUS Status; + LPWSTR DescriptionBuffer; + WCHAR Buffer[100]; + ULONG Index; + + // init function number + FunctionDescriptor->FunctionNumber = (UCHAR)FunctionNumber; + + // get association descriptor + Descriptor = USBCCGP_GetAssociationDescriptorAtIndex(FDODeviceExtension->ConfigurationDescriptor, FunctionNumber); + ASSERT(Descriptor); + + // store number interfaces + FunctionDescriptor->NumberOfInterfaces = Descriptor->bInterfaceCount; + + // allocate array for interface count + FunctionDescriptor->InterfaceDescriptorList = AllocateItem(NonPagedPool, sizeof(PUSB_INTERFACE_DESCRIPTOR) * Descriptor->bInterfaceCount); + if (FunctionDescriptor->InterfaceDescriptorList) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // init interface list + Status = USBCCGP_InitInterfaceListOfFunctionDescriptor(FDODeviceExtension->ConfigurationDescriptor, Descriptor, FunctionDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed + // + return Status; + } + + // + // now init interface description + // + if (Descriptor->iFunction) + { + // + // get interface description + // + Status = USBCCGP_GetDescriptor(FDODeviceExtension->NextDeviceObject, + USB_STRING_DESCRIPTOR_TYPE, + 100 * sizeof(WCHAR), + Descriptor->iFunction, + 0x0409, //FIXME + (PVOID*)&DescriptionBuffer); + if (!NT_SUCCESS(Status)) + { + // + // no description + // + RtlInitUnicodeString(&FunctionDescriptor->FunctionDescription, L""); + } + else + { + // + // init description + // + RtlInitUnicodeString(&FunctionDescriptor->FunctionDescription, DescriptionBuffer); + } + DPRINT1("FunctionDescription %wZ\n", &FunctionDescriptor->FunctionDescription); + } + + // + // now init hardware id + // + Index = swprintf(Buffer, L"USB\\VID_%04x&PID_%04x&Rev_%04x&MI_%02x", FDODeviceExtension->DeviceDescriptor->idVendor, + FDODeviceExtension->DeviceDescriptor->idProduct, + FDODeviceExtension->DeviceDescriptor->bcdDevice, + Descriptor->bFirstInterface) + 1; + Index = swprintf(&Buffer[Index], L"USB\\VID_%04x&PID_%04x&MI_%02x", FDODeviceExtension->DeviceDescriptor->idVendor, + FDODeviceExtension->DeviceDescriptor->idProduct, + Descriptor->bFirstInterface) + 1; + + // allocate result buffer + DescriptionBuffer = AllocateItem(NonPagedPool, (Index + 1) * sizeof(WCHAR)); + if (!DescriptionBuffer) + { + // + // failed to allocate memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // copy description + RtlCopyMemory(DescriptionBuffer, Buffer, Index * sizeof(WCHAR)); + FunctionDescriptor->HardwareId.Buffer = DescriptionBuffer; + FunctionDescriptor->HardwareId.Length = Index * sizeof(WCHAR); + FunctionDescriptor->HardwareId.MaximumLength = (Index + 1) * sizeof(WCHAR); + + + // + // now init the compatible id + // + Index = swprintf(Buffer, L"USB\\Class_%02x&SubClass_%02x&Prot_%02x", Descriptor->bFunctionClass, Descriptor->bFunctionSubClass, Descriptor->bFunctionProtocol) + 1; + Index = swprintf(&Buffer[Index], L"USB\\Class_%04x&SubClass_%04x", Descriptor->bFunctionClass, Descriptor->bFunctionSubClass) + 1; + Index = swprintf(&Buffer[Index], L"USB\\Class_%04x", Descriptor->bFunctionClass) + 1; + + // allocate result buffer + DescriptionBuffer = AllocateItem(NonPagedPool, (Index + 1) * sizeof(WCHAR)); + if (!DescriptionBuffer) + { + // + // failed to allocate memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // copy description + RtlCopyMemory(DescriptionBuffer, Buffer, Index * sizeof(WCHAR)); + FunctionDescriptor->CompatibleId.Buffer = DescriptionBuffer; + FunctionDescriptor->CompatibleId.Length = Index * sizeof(WCHAR); + FunctionDescriptor->CompatibleId.MaximumLength = (Index + 1) * sizeof(WCHAR); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USBCCGP_EnumWithAssociationDescriptor( + IN PDEVICE_OBJECT DeviceObject) +{ + ULONG DescriptorCount, Index; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + NTSTATUS Status = STATUS_SUCCESS; + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // count association descriptors + // + DescriptorCount = USBCCGP_CountAssociationDescriptors(FDODeviceExtension->ConfigurationDescriptor); + if (!DescriptorCount) + { + // + // no descriptors found + // + return STATUS_NOT_SUPPORTED; + } + + // + // allocate function descriptor array + // + FDODeviceExtension->FunctionDescriptor = AllocateItem(NonPagedPool, sizeof(USBC_FUNCTION_DESCRIPTOR) * DescriptorCount); + if (!FDODeviceExtension->FunctionDescriptorCount) + { + // + // no memory + // + DPRINT1("USBCCGP_EnumWithAssociationDescriptor failed to allocate function descriptor count %x\n", DescriptorCount); + return STATUS_INSUFFICIENT_RESOURCES; + } + + for(Index = 0; Index < DescriptorCount; Index++) + { + // + // init function descriptors + // + Status = USBCCGP_InitFunctionDescriptor(FDODeviceExtension, Index, &FDODeviceExtension->FunctionDescriptor[Index]); + if (!NT_SUCCESS(Status)) + { + // + // failed + // + return Status; + } + } + + // + // store function descriptor count + // + FDODeviceExtension->FunctionDescriptorCount = DescriptorCount; + + // + // done + // + return Status; +} + +NTSTATUS +USBCCG_InitIdsWithInterfaceDescriptor( + IN PFDO_DEVICE_EXTENSION FDODeviceExtension, + IN PUSB_INTERFACE_DESCRIPTOR Descriptor, + IN ULONG FunctionIndex, + OUT PUSBC_FUNCTION_DESCRIPTOR FunctionDescriptor) +{ + ULONG Index; + WCHAR Buffer[200]; + LPWSTR DescriptionBuffer; + NTSTATUS Status; + + // + // now init interface description + // + if (Descriptor->iInterface) + { + // + // get interface description + // + Status = USBCCGP_GetDescriptor(FDODeviceExtension->NextDeviceObject, + USB_STRING_DESCRIPTOR_TYPE, + 100 * sizeof(WCHAR), + Descriptor->iInterface, + 0x0409, //FIXME + (PVOID*)&DescriptionBuffer); + if (!NT_SUCCESS(Status)) + { + // + // no description + // + RtlInitUnicodeString(&FunctionDescriptor->FunctionDescription, L""); + } + else + { + // + // init description + // + RtlInitUnicodeString(&FunctionDescriptor->FunctionDescription, DescriptionBuffer); + } + DPRINT1("FunctionDescription %wZ\n", &FunctionDescriptor->FunctionDescription); + } + + + // + // now init hardware id + // + Index = swprintf(Buffer, L"USB\\VID_%04x&PID_%04x&Rev_%04x&MI_%02x", FDODeviceExtension->DeviceDescriptor->idVendor, + FDODeviceExtension->DeviceDescriptor->idProduct, + FDODeviceExtension->DeviceDescriptor->bcdDevice, + FunctionIndex) + 1; + Index += swprintf(&Buffer[Index], L"USB\\VID_%04x&PID_%04x&MI_%02x", FDODeviceExtension->DeviceDescriptor->idVendor, + FDODeviceExtension->DeviceDescriptor->idProduct, + FunctionIndex) + 1; + + // allocate result buffer + DescriptionBuffer = AllocateItem(NonPagedPool, (Index + 1) * sizeof(WCHAR)); + if (!DescriptionBuffer) + { + // + // failed to allocate memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // copy description + RtlCopyMemory(DescriptionBuffer, Buffer, Index * sizeof(WCHAR)); + FunctionDescriptor->HardwareId.Buffer = DescriptionBuffer; + FunctionDescriptor->HardwareId.Length = Index * sizeof(WCHAR); + FunctionDescriptor->HardwareId.MaximumLength = (Index + 1) * sizeof(WCHAR); + + // + // now init the compatible id + // + Index = swprintf(Buffer, L"USB\\Class_%02x&SubClass_%02x&Prot_%02x", Descriptor->bInterfaceClass, Descriptor->bInterfaceSubClass, Descriptor->bInterfaceProtocol) + 1; + Index += swprintf(&Buffer[Index], L"USB\\Class_%02x&SubClass_%02x", Descriptor->bInterfaceClass, Descriptor->bInterfaceSubClass) + 1; + Index += swprintf(&Buffer[Index], L"USB\\Class_%02x", Descriptor->bInterfaceClass) + 1; + + // allocate result buffer + DescriptionBuffer = AllocateItem(NonPagedPool, (Index + 1) * sizeof(WCHAR)); + if (!DescriptionBuffer) + { + // + // failed to allocate memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // copy description + RtlCopyMemory(DescriptionBuffer, Buffer, Index * sizeof(WCHAR)); + FunctionDescriptor->CompatibleId.Buffer = DescriptionBuffer; + FunctionDescriptor->CompatibleId.Length = Index * sizeof(WCHAR); + FunctionDescriptor->CompatibleId.MaximumLength = (Index + 1) * sizeof(WCHAR); + + // + // done + // + return STATUS_SUCCESS; +} + + +NTSTATUS +USBCCGP_LegacyEnum( + IN PDEVICE_OBJECT DeviceObject) +{ + ULONG Index; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + NTSTATUS Status = STATUS_SUCCESS; + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // sanity check + // + ASSERT(FDODeviceExtension->ConfigurationDescriptor->bNumInterfaces); + + // + // allocate function array + // + FDODeviceExtension->FunctionDescriptor = AllocateItem(NonPagedPool, sizeof(USBC_FUNCTION_DESCRIPTOR) * FDODeviceExtension->ConfigurationDescriptor->bNumInterfaces); + if (!FDODeviceExtension->FunctionDescriptor) + { + // + // no memory + // + DPRINT1("USBCCGP_EnumWithAssociationDescriptor failed to allocate function descriptor %lu\n", FDODeviceExtension->ConfigurationDescriptor->bNumInterfaces); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // init function descriptors + // + for(Index = 0; Index < FDODeviceExtension->ConfigurationDescriptor->bNumInterfaces; Index++) + { + // get interface descriptor + InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(FDODeviceExtension->ConfigurationDescriptor, FDODeviceExtension->ConfigurationDescriptor, Index, 0, -1, -1, -1); + if (InterfaceDescriptor == NULL) + { + // + // failed to find interface descriptor + // + DPRINT1("[USBCCGP] Failed to find interface descriptor index %lu\n", Index); + ASSERT(FALSE); + return STATUS_UNSUCCESSFUL; + } + + // + // init function descriptor + // + FDODeviceExtension->FunctionDescriptor[Index].FunctionNumber = Index; + FDODeviceExtension->FunctionDescriptor[Index].NumberOfInterfaces = 1; + FDODeviceExtension->FunctionDescriptor[Index].InterfaceDescriptorList = AllocateItem(NonPagedPool, sizeof(PUSB_INTERFACE_DESCRIPTOR) * 1); + if (!FDODeviceExtension->FunctionDescriptor[Index].InterfaceDescriptorList) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // store interface descriptor + // + FDODeviceExtension->FunctionDescriptor[Index].InterfaceDescriptorList[0] = InterfaceDescriptor; + + // + // now init the device ids + // + Status = USBCCG_InitIdsWithInterfaceDescriptor(FDODeviceExtension, InterfaceDescriptor, Index, &FDODeviceExtension->FunctionDescriptor[Index]); + if (!NT_SUCCESS(Status)) + { + // + // failed to init ids + // + DPRINT1("[USBCCGP] Failed to init ids with %x\n", Status); + return Status; + } + + // + // store function count + // + FDODeviceExtension->FunctionDescriptorCount++; + } + + // + // done + // + return Status; +} + +NTSTATUS +USBCCGP_EnumWithUnionFunctionDescriptors( + IN PDEVICE_OBJECT DeviceObject) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +USBCCGP_EnumWithAudioLegacy( + IN PDEVICE_OBJECT DeviceObject) +{ + ULONG Index; + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor, FirstDescriptor = NULL; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + NTSTATUS Status = STATUS_SUCCESS; + PVOID StartPosition; + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + + // + // first check if all interfaces belong to the same audio class + // + StartPosition = FDODeviceExtension->ConfigurationDescriptor; + for(Index = 0; Index < CountInterfaceDescriptors(FDODeviceExtension->ConfigurationDescriptor); Index++) + { + // + // get interface descriptor + // + InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(FDODeviceExtension->ConfigurationDescriptor, StartPosition, -1, -1, -1, -1, -1); + DPRINT1("Index %lu Descriptor %p\n", Index, InterfaceDescriptor); + ASSERT(InterfaceDescriptor); + + // + // move to next descriptor + // + StartPosition = (PVOID)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength); + + if (InterfaceDescriptor->bInterfaceClass != 0x1) + { + // + // collection contains non audio class + // + return STATUS_UNSUCCESSFUL; + } + + if (FirstDescriptor == NULL) + { + // + // store interface descriptor + // + FirstDescriptor = InterfaceDescriptor; + continue; + } + + if (FirstDescriptor->bInterfaceSubClass == InterfaceDescriptor->bInterfaceSubClass) + { + // + // interface subclass must be different from the first interface + // + return STATUS_UNSUCCESSFUL; + } + } + + // + // this is an composite audio device + // + DPRINT("[USBCCGP] Audio Composite Device detected\n"); + + // + // audio interfaces are all grouped into one single function + // + FDODeviceExtension->FunctionDescriptor = AllocateItem(NonPagedPool, sizeof(USBC_FUNCTION_DESCRIPTOR)); + if (!FDODeviceExtension->FunctionDescriptor) + { + // + // no memory + // + DPRINT1("USBCCGP_EnumWithAssociationDescriptor failed to allocate function descriptor count\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // init function number + // + FDODeviceExtension->FunctionDescriptor[0].FunctionNumber = 0; + + // + // store interfaces + // + Status = AllocateInterfaceDescriptorsArray(FDODeviceExtension->ConfigurationDescriptor, &FDODeviceExtension->FunctionDescriptor[0].InterfaceDescriptorList); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate descriptor array + // + DPRINT1("[USBCCGP] Failed to allocate descriptor array %x\n", Status); + return Status; + } + + // + // now init the device ids + // + Status = USBCCG_InitIdsWithInterfaceDescriptor(FDODeviceExtension, FirstDescriptor, 0, &FDODeviceExtension->FunctionDescriptor[0]); + if (!NT_SUCCESS(Status)) + { + // + // failed to init ids + // + DPRINT1("[USBCCGP] Failed to init ids with %x\n", Status); + return Status; + } + + // + // number of interfaces + // + FDODeviceExtension->FunctionDescriptor[0].NumberOfInterfaces = CountInterfaceDescriptors(FDODeviceExtension->ConfigurationDescriptor); + + // + // store function count + // + FDODeviceExtension->FunctionDescriptorCount = 1; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USBCCGP_EnumerateFunctions( + IN PDEVICE_OBJECT DeviceObject) +{ + NTSTATUS Status; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // first try with filter driver + // + Status = USBCCGP_CustomEnumWithInterface(DeviceObject); + if (NT_SUCCESS(Status)) + { + // + // succeeded + // + return Status; + } + + // + // enumerate functions with interface association descriptor + // + Status = USBCCGP_EnumWithAssociationDescriptor(DeviceObject); + if (NT_SUCCESS(Status)) + { + // + // succeeded + // + return Status; + } + +#if 0 + // + // try with union function descriptors + // + Status = USBCCGP_EnumWithUnionFunctionDescriptors(DeviceObject); + if (NT_SUCCESS(Status)) + { + // + // succeeded + // + return Status; + } +#endif + + // + // try with legacy audio methods + // + Status = USBCCGP_EnumWithAudioLegacy(DeviceObject); + if (NT_SUCCESS(Status)) + { + // + // succeeded + // + return Status; + } + + // + // try with legacy enumeration + // + return USBCCGP_LegacyEnum(DeviceObject); +} diff --git a/reactos/drivers/usb/usbccgp/misc.c b/reactos/drivers/usb/usbccgp/misc.c new file mode 100644 index 00000000000..1cb81f6f020 --- /dev/null +++ b/reactos/drivers/usb/usbccgp/misc.c @@ -0,0 +1,224 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbccgp/misc.c + * PURPOSE: USB device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + * Cameron Gutman + */ + +#include "usbccgp.h" + +// +// driver verifier +// +IO_COMPLETION_ROUTINE SyncForwardIrpCompletionRoutine; + +NTSTATUS +NTAPI +USBSTOR_SyncForwardIrpCompletionRoutine( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context) +{ + if (Irp->PendingReturned) + { + KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE); + } + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +NTAPI +USBCCGP_SyncForwardIrp( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + KEVENT Event; + NTSTATUS Status; + + // + // initialize event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // copy irp stack location + // + IoCopyCurrentIrpStackLocationToNext(Irp); + + // + // set completion routine + // + IoSetCompletionRoutine(Irp, USBSTOR_SyncForwardIrpCompletionRoutine, &Event, TRUE, TRUE, TRUE); + + + // + // call driver + // + Status = IoCallDriver(DeviceObject, Irp); + + // + // check if pending + // + if (Status == STATUS_PENDING) + { + // + // wait for the request to finish + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + // + // copy status code + // + Status = Irp->IoStatus.Status; + } + + // + // done + // + return Status; +} + +NTSTATUS +USBCCGP_SyncUrbRequest( + IN PDEVICE_OBJECT DeviceObject, + OUT PURB UrbRequest) +{ + PIRP Irp; + PIO_STACK_LOCATION IoStack; + KEVENT Event; + NTSTATUS Status; + + // + // allocate irp + // + Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + if (!Irp) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + + // + // get next stack location + // + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // initialize stack location + // + IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; + IoStack->Parameters.Others.Argument1 = (PVOID)UrbRequest; + IoStack->Parameters.DeviceIoControl.InputBufferLength = UrbRequest->UrbHeader.Length; + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // setup completion routine + // + IoSetCompletionRoutine(Irp, USBSTOR_SyncForwardIrpCompletionRoutine, &Event, TRUE, TRUE, TRUE); + + // + // call driver + // + Status = IoCallDriver(DeviceObject, Irp); + + // + // check if request is pending + // + if (Status == STATUS_PENDING) + { + // + // wait for completion + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + // + // update status + // + Status = Irp->IoStatus.Status; + } + + // + // free irp + // + IoFreeIrp(Irp); + + // + // done + // + return Status; +} + +PVOID +AllocateItem( + IN POOL_TYPE PoolType, + IN ULONG ItemSize) +{ + // + // allocate item + // + PVOID Item = ExAllocatePoolWithTag(PoolType, ItemSize, USBCCPG_TAG); + + if (Item) + { + // + // zero item + // + RtlZeroMemory(Item, ItemSize); + } + + // + // return element + // + return Item; +} + +VOID +FreeItem( + IN PVOID Item) +{ + // + // free item + // + ExFreePoolWithTag(Item, USBCCPG_TAG); +} + +VOID +DumpFunctionDescriptor( + IN PUSBC_FUNCTION_DESCRIPTOR FunctionDescriptor, + IN ULONG FunctionDescriptorCount) +{ + ULONG Index, SubIndex; + + + DPRINT1("FunctionCount %lu\n", FunctionDescriptorCount); + for(Index = 0; Index < FunctionDescriptorCount; Index++) + { + DPRINT1("Function %lu\n", Index); + DPRINT1("FunctionNumber %lu\n", FunctionDescriptor[Index].FunctionNumber); + DPRINT1("HardwareId %wZ\n", &FunctionDescriptor[Index].HardwareId); + DPRINT1("CompatibleId %wZ\n", &FunctionDescriptor[Index].CompatibleId); + DPRINT1("FunctionDescription %wZ\n", &FunctionDescriptor[Index].FunctionDescription); + DPRINT1("NumInterfaces %lu\n", FunctionDescriptor[Index].NumberOfInterfaces); + + for(SubIndex = 0; SubIndex < FunctionDescriptor[Index].NumberOfInterfaces; SubIndex++) + { + DPRINT1(" Interface %p\n", FunctionDescriptor[Index].InterfaceDescriptorList[SubIndex]); + DPRINT1(" Interface InterfaceNumber %x\n", FunctionDescriptor[Index].InterfaceDescriptorList[SubIndex]->bInterfaceNumber); + DPRINT1(" Interface Alternate %x\n", FunctionDescriptor[Index].InterfaceDescriptorList[SubIndex]->bAlternateSetting ); + } + } + +} \ No newline at end of file diff --git a/reactos/drivers/usb/usbccgp/pdo.c b/reactos/drivers/usb/usbccgp/pdo.c new file mode 100644 index 00000000000..e6c343b7f76 --- /dev/null +++ b/reactos/drivers/usb/usbccgp/pdo.c @@ -0,0 +1,1002 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbccgp/pdo.c + * PURPOSE: USB device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + * Cameron Gutman + */ + +#include "usbccgp.h" + +NTSTATUS +USBCCGP_PdoHandleQueryDeviceText( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + LPWSTR Buffer; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + LPWSTR GenericString = L"Composite USB Device"; + // + // get device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // is there a device description + // + if (PDODeviceExtension->FunctionDescriptor->FunctionDescription.Length) + { + // + // allocate buffer + // + Buffer = AllocateItem(NonPagedPool, PDODeviceExtension->FunctionDescriptor->FunctionDescription.Length + sizeof(WCHAR)); + if (!Buffer) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy buffer + // + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + RtlCopyMemory(Buffer, PDODeviceExtension->FunctionDescriptor->FunctionDescription.Buffer, PDODeviceExtension->FunctionDescriptor->FunctionDescription.Length); + return STATUS_SUCCESS; + } + + // + // FIXME use GenericCompositeUSBDeviceString + // + UNIMPLEMENTED + Buffer = AllocateItem(PagedPool, (wcslen(GenericString) + 1) * sizeof(WCHAR)); + if (!Buffer) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory(Buffer, GenericString, (wcslen(GenericString) + 1) * sizeof(WCHAR)); + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + + return STATUS_SUCCESS; +} + +NTSTATUS +USBCCGP_PdoHandleDeviceRelations( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PDEVICE_RELATIONS DeviceRelations; + PIO_STACK_LOCATION IoStack; + + DPRINT("USBCCGP_PdoHandleDeviceRelations\n"); + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // check if relation type is BusRelations + // + if (IoStack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation) + { + // + // PDO handles only target device relation + // + return Irp->IoStatus.Status; + } + + // + // allocate device relations + // + DeviceRelations = (PDEVICE_RELATIONS)AllocateItem(PagedPool, sizeof(DEVICE_RELATIONS)); + if (!DeviceRelations) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize device relations + // + DeviceRelations->Count = 1; + DeviceRelations->Objects[0] = DeviceObject; + ObReferenceObject(DeviceObject); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + + // + // completed successfully + // + return STATUS_SUCCESS; +} + +NTSTATUS +USBCCGP_PdoAppendInterfaceNumber( + IN LPWSTR DeviceId, + IN ULONG InterfaceNumber, + OUT LPWSTR *OutString) +{ + ULONG Length = 0, StringLength; + LPWSTR String; + + // + // count length of string + // + String = DeviceId; + while(*String) + { + StringLength = wcslen(String) + 1; + Length += StringLength; + Length += 6; //&MI_XX + String += StringLength; + } + + // + // now allocate the buffer + // + String = AllocateItem(NonPagedPool, (Length + 2) * sizeof(WCHAR)); + if (!String) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // store result + // + *OutString = String; + + while(*DeviceId) + { + StringLength = swprintf(String, L"%s&MI_%02x", DeviceId) + 1; + Length = wcslen(DeviceId) + 1; + DPRINT("String %p\n", String); + + // + // next string + // + String += StringLength; + DeviceId += Length; + } + + // + // success + // + return STATUS_SUCCESS; +} + + +NTSTATUS +USBCCGP_PdoHandleQueryId( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PUNICODE_STRING DeviceString = NULL; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + NTSTATUS Status; + LPWSTR Buffer; + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + + if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID) + { + // + // handle query device id + // + Status = USBCCGP_SyncForwardIrp(PDODeviceExtension->NextDeviceObject, Irp); + if (NT_SUCCESS(Status)) + { + // + // allocate buffer + // + Buffer = AllocateItem(NonPagedPool, (wcslen((LPWSTR)Irp->IoStatus.Information) + 7) * sizeof(WCHAR)); + if (Buffer) + { + // + // append interface number + // + ASSERT(Irp->IoStatus.Information); + swprintf(Buffer, L"%s&MI_%02x", (LPWSTR)Irp->IoStatus.Information, PDODeviceExtension->FunctionDescriptor->FunctionNumber); + DPRINT("BusQueryDeviceID %S\n", Buffer); + + ExFreePool((PVOID)Irp->IoStatus.Information); + Irp->IoStatus .Information = (ULONG_PTR)Buffer; + } + else + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + return Status; + } + else if (IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs) + { + // + // handle instance id + // + DeviceString = &PDODeviceExtension->FunctionDescriptor->HardwareId; + } + else if (IoStack->Parameters.QueryId.IdType == BusQueryInstanceID) + { + // + // handle instance id + // + Buffer = AllocateItem(NonPagedPool, 5 * sizeof(WCHAR)); + if (!Buffer) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // use function number + // + swprintf(Buffer, L"%04x", PDODeviceExtension->FunctionDescriptor->FunctionNumber); + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + return STATUS_SUCCESS; + } + else if (IoStack->Parameters.QueryId.IdType == BusQueryCompatibleIDs) + { + // + // handle instance id + // + DeviceString = &PDODeviceExtension->FunctionDescriptor->CompatibleId; + } + + // + // sanity check + // + ASSERT(DeviceString != NULL); + + // + // allocate buffer + // + Buffer = AllocateItem(NonPagedPool, DeviceString->Length + sizeof(WCHAR)); + if (!Buffer) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy buffer + // + RtlCopyMemory(Buffer, DeviceString->Buffer, DeviceString->Length); + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + + return STATUS_SUCCESS; +} + +NTSTATUS +PDO_HandlePnp( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + NTSTATUS Status; + ULONG Index, bFound; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + switch(IoStack->MinorFunction) + { + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + // + // handle device relations + // + Status = USBCCGP_PdoHandleDeviceRelations(DeviceObject, Irp); + break; + } + case IRP_MN_QUERY_DEVICE_TEXT: + { + // + // handle query device text + // + Status = USBCCGP_PdoHandleQueryDeviceText(DeviceObject, Irp); + break; + } + case IRP_MN_QUERY_ID: + { + // + // handle request + // + Status = USBCCGP_PdoHandleQueryId(DeviceObject, Irp); + break; + } + case IRP_MN_REMOVE_DEVICE: + { + // + // remove us from the fdo's pdo list + // + bFound = FALSE; + for(Index = 0; Index < PDODeviceExtension->FDODeviceExtension->FunctionDescriptorCount; Index++) + { + if (PDODeviceExtension->FDODeviceExtension->ChildPDO[Index] == DeviceObject) + { + // + // remove us + // + PDODeviceExtension->FDODeviceExtension->ChildPDO[Index] = NULL; + bFound = TRUE; + break; + } + } + + // + // Complete the IRP + // + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + if (bFound) + { + // + // Delete the device object + // + IoDeleteDevice(DeviceObject); + } + return STATUS_SUCCESS; + } + case IRP_MN_QUERY_CAPABILITIES: + { + // + // copy device capabilities + // + RtlCopyMemory(IoStack->Parameters.DeviceCapabilities.Capabilities, &PDODeviceExtension->Capabilities, sizeof(DEVICE_CAPABILITIES)); + + /* Complete the IRP */ + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + case IRP_MN_QUERY_REMOVE_DEVICE: + case IRP_MN_QUERY_STOP_DEVICE: + { + // + // sure + // + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_START_DEVICE: + { + // + // no-op for PDO + // + DPRINT("[USBCCGP] PDO IRP_MN_START\n"); + Status = STATUS_SUCCESS; + break; + } + default: + { + // + // do nothing + // + Status = Irp->IoStatus.Status; + break; + } + } + + // + // complete request + // + if (Status != STATUS_PENDING) + { + // + // store result + // + Irp->IoStatus.Status = Status; + + // + // complete request + // + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + // + // done processing + // + return Status; + +} + +NTSTATUS +USBCCGP_BuildConfigurationDescriptor( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + ULONG TotalSize, Index; + PURB Urb; + PVOID Buffer; + PUCHAR BufferPtr; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + DPRINT("USBCCGP_BuildConfigurationDescriptor\n"); + + // + // get device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // get configuration descriptor + // + ConfigurationDescriptor = PDODeviceExtension->ConfigurationDescriptor; + + // + // calculate size of configuration descriptor + // + TotalSize = sizeof(USB_CONFIGURATION_DESCRIPTOR); + + for(Index = 0; Index < PDODeviceExtension->FunctionDescriptor->NumberOfInterfaces; Index++) + { + // + // get current interface descriptor + // + InterfaceDescriptor = PDODeviceExtension->FunctionDescriptor->InterfaceDescriptorList[Index]; + + // + // add to size and move to next descriptor + // + TotalSize += InterfaceDescriptor->bLength; + InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength); + + do + { + if ((ULONG_PTR)InterfaceDescriptor >= ((ULONG_PTR)ConfigurationDescriptor + ConfigurationDescriptor->wTotalLength)) + { + // + // reached end of configuration descriptor + // + break; + } + + // + // association descriptors are removed + // + if (InterfaceDescriptor->bDescriptorType != USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE) + { + if (InterfaceDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE) + { + // + // reached next descriptor + // + break; + } + + // + // append size + // + TotalSize += InterfaceDescriptor->bLength; + } + + // + // move to next descriptor + // + InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength); + }while(TRUE); + } + + // + // now allocate temporary buffer for the configuration descriptor + // + Buffer = AllocateItem(NonPagedPool, TotalSize); + if (!Buffer) + { + // + // failed to allocate buffer + // + DPRINT1("[USBCCGP] Failed to allocate %lu Bytes\n", TotalSize); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // first copy the configuration descriptor + // + RtlCopyMemory(Buffer, ConfigurationDescriptor, sizeof(USB_CONFIGURATION_DESCRIPTOR)); + BufferPtr = (PUCHAR)((ULONG_PTR)Buffer + ConfigurationDescriptor->bLength); + + for(Index = 0; Index < PDODeviceExtension->FunctionDescriptor->NumberOfInterfaces; Index++) + { + // + // get current interface descriptor + // + InterfaceDescriptor = PDODeviceExtension->FunctionDescriptor->InterfaceDescriptorList[Index]; + + // + // copy descriptor and move to next descriptor + // + RtlCopyMemory(BufferPtr, InterfaceDescriptor, InterfaceDescriptor->bLength); + BufferPtr += InterfaceDescriptor->bLength; + InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength); + + do + { + if ((ULONG_PTR)InterfaceDescriptor >= ((ULONG_PTR)ConfigurationDescriptor + ConfigurationDescriptor->wTotalLength)) + { + // + // reached end of configuration descriptor + // + break; + } + + // + // association descriptors are removed + // + if (InterfaceDescriptor->bDescriptorType != USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE) + { + if (InterfaceDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE) + { + // + // reached next descriptor + // + break; + } + + // + // copy descriptor + // + RtlCopyMemory(BufferPtr, InterfaceDescriptor, InterfaceDescriptor->bLength); + BufferPtr += InterfaceDescriptor->bLength; + } + + // + // move to next descriptor + // + InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength); + }while(TRUE); + } + + // + // modify configuration descriptor + // + ConfigurationDescriptor = Buffer; + ConfigurationDescriptor->wTotalLength = TotalSize; + ConfigurationDescriptor->bNumInterfaces = PDODeviceExtension->FunctionDescriptor->NumberOfInterfaces; + + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + ASSERT(Urb); + + // + // copy descriptor + // + RtlCopyMemory(Urb->UrbControlDescriptorRequest.TransferBuffer, Buffer, min(TotalSize, Urb->UrbControlDescriptorRequest.TransferBufferLength)); + + // + // store final size + // + Urb->UrbControlDescriptorRequest.TransferBufferLength = TotalSize; + + // + // free buffer + // + FreeItem(Buffer); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USBCCGP_PDOSelectConfiguration( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PURB Urb, NewUrb; + PUSBD_INTERFACE_INFORMATION InterfaceInformation; + ULONG InterfaceInformationCount, Index, InterfaceIndex; + PUSBD_INTERFACE_LIST_ENTRY Entry; + ULONG NeedSelect, FoundInterface; + NTSTATUS Status; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + ASSERT(Urb); + + // + // is there already an configuration handle + // + if (Urb->UrbSelectConfiguration.ConfigurationHandle) + { + // + // nothing to do + // + return STATUS_SUCCESS; + } + + // + // count interface information + // + InterfaceInformationCount = 0; + InterfaceInformation = &Urb->UrbSelectConfiguration.Interface; + do + { + InterfaceInformationCount++; + InterfaceInformation = (PUSBD_INTERFACE_INFORMATION)((ULONG_PTR)InterfaceInformation + InterfaceInformation->Length); + }while((ULONG_PTR)InterfaceInformation < (ULONG_PTR)Urb + Urb->UrbSelectConfiguration.Hdr.Length); + + // + // check all interfaces + // + InterfaceInformation = &Urb->UrbSelectConfiguration.Interface; + Index = 0; + Entry = NULL; + DPRINT("Count %x\n", InterfaceInformationCount); + do + { + DPRINT1("[USBCCGP] SelectConfiguration Function %x InterfaceNumber %x Alternative %x\n", PDODeviceExtension->FunctionDescriptor->FunctionNumber, InterfaceInformation->InterfaceNumber, InterfaceInformation->AlternateSetting); + + // + // search for the interface in the local interface list + // + FoundInterface = FALSE; + for(InterfaceIndex = 0; InterfaceIndex < PDODeviceExtension->FunctionDescriptor->NumberOfInterfaces; InterfaceIndex++) + { + if (PDODeviceExtension->FunctionDescriptor->InterfaceDescriptorList[InterfaceIndex]->bInterfaceNumber == InterfaceInformation->InterfaceNumber) + { + // found interface entry + FoundInterface = TRUE; + break; + } + } + + if (!FoundInterface) + { + // + // invalid parameter + // + DPRINT1("InterfaceInformation InterfaceNumber %x Alternative %x NumberOfPipes %x not found\n", InterfaceInformation->InterfaceNumber, InterfaceInformation->AlternateSetting, InterfaceInformation->NumberOfPipes); + ASSERT(FALSE); + return STATUS_INVALID_PARAMETER; + } + + // + // now query the total interface list + // + Entry = NULL; + for(InterfaceIndex = 0; InterfaceIndex < PDODeviceExtension->InterfaceListCount; InterfaceIndex++) + { + if (PDODeviceExtension->InterfaceList[InterfaceIndex].Interface->InterfaceNumber == InterfaceInformation->InterfaceNumber) + { + // + // found entry + // + Entry = &PDODeviceExtension->InterfaceList[InterfaceIndex]; + } + } + + // + // sanity check + // + ASSERT(Entry); + if (!Entry) + { + // + // corruption detected + // + KeBugCheck(0); + } + + NeedSelect = FALSE; + if (Entry->InterfaceDescriptor->bAlternateSetting == InterfaceInformation->AlternateSetting) + { + + for(InterfaceIndex = 0; InterfaceIndex < InterfaceInformation->NumberOfPipes; InterfaceIndex++) + { + if (InterfaceInformation->Pipes[InterfaceIndex].MaximumTransferSize != Entry->Interface->Pipes[InterfaceIndex].MaximumTransferSize) + { + // + // changed interface + // + NeedSelect = TRUE; + } + } + } + else + { + // + // need select as the interface number differ + // + NeedSelect = TRUE; + } + + if (!NeedSelect) + { + // + // interface is already selected + // + RtlCopyMemory(InterfaceInformation, Entry->Interface, min(InterfaceInformation->Length, Entry->Interface->Length)); + } + else + { + // + // select interface + // + DPRINT1("Selecting InterfaceIndex %lu AlternateSetting %lu NumberOfPipes %lu\n", InterfaceInformation->InterfaceNumber, InterfaceInformation->AlternateSetting, InterfaceInformation->NumberOfPipes); + ASSERT(InterfaceInformation->Length == Entry->Interface->Length); + + // + // build urb + // + NewUrb = AllocateItem(NonPagedPool, GET_SELECT_INTERFACE_REQUEST_SIZE(InterfaceInformation->NumberOfPipes)); + if (!NewUrb) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // now prepare interface urb + // + UsbBuildSelectInterfaceRequest(NewUrb, GET_SELECT_INTERFACE_REQUEST_SIZE(InterfaceInformation->NumberOfPipes), PDODeviceExtension->ConfigurationHandle, InterfaceInformation->InterfaceNumber, InterfaceInformation->AlternateSetting); + + // + // now select the interface + // + Status = USBCCGP_SyncUrbRequest(PDODeviceExtension->NextDeviceObject, NewUrb); + DPRINT1("SelectInterface Status %x\n", Status); + + // + // did it succeeed + // + if (NT_SUCCESS(Status)) + { + // + // update configuration info + // + ASSERT(Entry->Interface->Length == NewUrb->UrbSelectInterface.Interface.Length); + ASSERT(InterfaceInformation->Length == NewUrb->UrbSelectInterface.Interface.Length); + RtlCopyMemory(Entry->Interface, &NewUrb->UrbSelectInterface.Interface, NewUrb->UrbSelectInterface.Interface.Length); + + // + // update provided interface information + // + RtlCopyMemory(InterfaceInformation, Entry->Interface, Entry->Interface->Length); + } + + // + // free urb + // + FreeItem(NewUrb); + } + + // + // move to next information + // + InterfaceInformation = (PUSBD_INTERFACE_INFORMATION)((ULONG_PTR)InterfaceInformation + InterfaceInformation->Length); + Index++; + }while(Index < InterfaceInformationCount); + + // + // store configuration handle + // + Urb->UrbSelectConfiguration.ConfigurationHandle = PDODeviceExtension->ConfigurationHandle; + + DPRINT1("[USBCCGP] SelectConfiguration Function %x Completed\n", PDODeviceExtension->FunctionDescriptor->FunctionNumber); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +PDO_HandleInternalDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + NTSTATUS Status; + PURB Urb; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_SUBMIT_URB) + { + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + ASSERT(Urb); + DPRINT("IOCTL_INTERNAL_USB_SUBMIT_URB Function %x\n", Urb->UrbHeader.Function); + + if (Urb->UrbHeader.Function == URB_FUNCTION_SELECT_CONFIGURATION) + { + // + // select configuration + // + Status = USBCCGP_PDOSelectConfiguration(DeviceObject, Irp); + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + else if (Urb->UrbHeader.Function == URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE) + { + if(Urb->UrbControlDescriptorRequest.DescriptorType == USB_DEVICE_DESCRIPTOR_TYPE) + { + // + // is the buffer big enough + // + if (Urb->UrbControlDescriptorRequest.TransferBufferLength < sizeof(USB_DEVICE_DESCRIPTOR)) + { + // + // invalid buffer size + // + DPRINT1("[USBCCGP] invalid device descriptor size %lu\n", Urb->UrbControlDescriptorRequest.TransferBufferLength); + Urb->UrbControlDescriptorRequest.TransferBufferLength = sizeof(USB_DEVICE_DESCRIPTOR); + Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INVALID_BUFFER_SIZE; + } + + // + // copy device descriptor + // + ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + RtlCopyMemory(Urb->UrbControlDescriptorRequest.TransferBuffer, &PDODeviceExtension->DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + else if (Urb->UrbControlDescriptorRequest.DescriptorType == USB_CONFIGURATION_DESCRIPTOR_TYPE) + { + // + // build configuration descriptor + // + Status = USBCCGP_BuildConfigurationDescriptor(DeviceObject, Irp); + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + } + else + { + IoSkipCurrentIrpStackLocation(Irp); + Status = IoCallDriver(PDODeviceExtension->NextDeviceObject, Irp); + return Status; + } + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_GET_PORT_STATUS) + { + IoSkipCurrentIrpStackLocation(Irp); + Status = IoCallDriver(PDODeviceExtension->NextDeviceObject, Irp); + return Status; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_RESET_PORT) + { + IoSkipCurrentIrpStackLocation(Irp); + Status = IoCallDriver(PDODeviceExtension->NextDeviceObject, Irp); + return Status; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_CYCLE_PORT) + { + IoSkipCurrentIrpStackLocation(Irp); + Status = IoCallDriver(PDODeviceExtension->NextDeviceObject, Irp); + return Status; + } + + + + DPRINT1("IOCTL %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode); + DPRINT1("InputBufferLength %lu\n", IoStack->Parameters.DeviceIoControl.InputBufferLength); + DPRINT1("OutputBufferLength %lu\n", IoStack->Parameters.DeviceIoControl.OutputBufferLength); + DPRINT1("Type3InputBuffer %p\n", IoStack->Parameters.DeviceIoControl.Type3InputBuffer); + + ASSERT(FALSE); + + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS +PDO_Dispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + + /* get stack location */ + IoStack = IoGetCurrentIrpStackLocation(Irp); + + switch(IoStack->MajorFunction) + { + case IRP_MJ_PNP: + return PDO_HandlePnp(DeviceObject, Irp); + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + return PDO_HandleInternalDeviceControl(DeviceObject, Irp); + default: + DPRINT1("PDO_Dispatch Function %x not implemented\n", IoStack->MajorFunction); + ASSERT(FALSE); + Status = Irp->IoStatus.Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; + } + +} diff --git a/reactos/drivers/usb/usbccgp/usbccgp.c b/reactos/drivers/usb/usbccgp/usbccgp.c new file mode 100644 index 00000000000..1757505b098 --- /dev/null +++ b/reactos/drivers/usb/usbccgp/usbccgp.c @@ -0,0 +1,155 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbccgp/usbccgp.c + * PURPOSE: USB device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + * Cameron Gutman + */ + +#include "usbccgp.h" + +// +// driver verifier +// +DRIVER_ADD_DEVICE USBCCGP_AddDevice; + +NTSTATUS +NTAPI +USBCCGP_AddDevice( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT PhysicalDeviceObject) +{ + NTSTATUS Status; + PDEVICE_OBJECT DeviceObject; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // lets create the device + Status = IoCreateDevice(DriverObject, sizeof(FDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_USB, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &DeviceObject); + if (!NT_SUCCESS(Status)) + { + // failed to create device + DPRINT1("USBCCGP_AddDevice failed to create device with %x\n", Status); + return Status; + } + + // get device extension + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // init device extension + RtlZeroMemory(FDODeviceExtension, sizeof(FDO_DEVICE_EXTENSION)); + FDODeviceExtension->Common.IsFDO = TRUE; + FDODeviceExtension->DriverObject = DriverObject; + FDODeviceExtension->PhysicalDeviceObject = PhysicalDeviceObject; + InitializeListHead(&FDODeviceExtension->ResetPortListHead); + InitializeListHead(&FDODeviceExtension->CyclePortListHead); + KeInitializeSpinLock(&FDODeviceExtension->Lock); + + FDODeviceExtension->NextDeviceObject = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject); + if (!FDODeviceExtension->NextDeviceObject) + { + // failed to attach + DPRINT1("USBCCGP_AddDevice failed to attach device\n"); + IoDeleteDevice(DeviceObject); + return STATUS_DEVICE_REMOVED; + } + + // set device flags + DeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; + + // device is initialized + DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + // device initialized + return Status; +} + +NTSTATUS +NTAPI +USBCCGP_CreateClose( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // get common device extension + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // is it a fdo + if (DeviceExtension->IsFDO) + { + // forward and forget + IoSkipCurrentIrpStackLocation(Irp); + + // get fdo + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // call lower driver + return IoCallDriver(FDODeviceExtension->NextDeviceObject, Irp); + } + else + { + // pdo not supported + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_SUPPORTED; + } +} + +NTSTATUS +NTAPI +USBCCGP_Dispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PIO_STACK_LOCATION IoStack; + + // get common device extension + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // get current stack location + IoStack = IoGetCurrentIrpStackLocation(Irp); + + if (IoStack->MajorFunction == IRP_MJ_CREATE || IoStack->MajorFunction == IRP_MJ_CLOSE) + { + // dispatch to default handler + return USBCCGP_CreateClose(DeviceObject, Irp); + } + + if (DeviceExtension->IsFDO) + { + // handle request for FDO + return FDO_Dispatch(DeviceObject, Irp); + } + else + { + // handle request for PDO + return PDO_Dispatch(DeviceObject, Irp); + } +} + +NTSTATUS +NTAPI +DriverEntry( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath) +{ + + // initialize driver object + DPRINT("[USBCCGP] DriverEntry\n"); + DriverObject->DriverExtension->AddDevice = USBCCGP_AddDevice; + DriverObject->MajorFunction[IRP_MJ_CREATE] = USBCCGP_Dispatch; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = USBCCGP_Dispatch; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = USBCCGP_Dispatch; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = USBCCGP_Dispatch; + DriverObject->MajorFunction[IRP_MJ_POWER] = USBCCGP_Dispatch; + DriverObject->MajorFunction[IRP_MJ_PNP] = USBCCGP_Dispatch; + + // FIMXE query GenericCompositeUSBDeviceString + + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/usb/usbccgp/usbccgp.h b/reactos/drivers/usb/usbccgp/usbccgp.h new file mode 100644 index 00000000000..2208dfe8b49 --- /dev/null +++ b/reactos/drivers/usb/usbccgp/usbccgp.h @@ -0,0 +1,148 @@ +#ifndef USBEHCI_H__ +#define USBEHCI_H__ + +#include +#define NDEBUG +#include +#include +#include +#include +#include +#include + +// +// FIXME: +// #include +// +#include +#include +#include + +typedef struct +{ + BOOLEAN IsFDO; // is device a FDO or PDO +}COMMON_DEVICE_EXTENSION, *PCOMMON_DEVICE_EXTENSION; + +typedef struct +{ + COMMON_DEVICE_EXTENSION Common; // shared with PDO + PDRIVER_OBJECT DriverObject; // driver object + PDEVICE_OBJECT PhysicalDeviceObject; // physical device object + PDEVICE_OBJECT NextDeviceObject; // lower device object + PUSB_DEVICE_DESCRIPTOR DeviceDescriptor; // usb device descriptor + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; // usb configuration descriptor + DEVICE_CAPABILITIES Capabilities; // device capabilities + PUSBD_INTERFACE_LIST_ENTRY InterfaceList; // interface list + ULONG InterfaceListCount; // interface list count + USBD_CONFIGURATION_HANDLE ConfigurationHandle; // configuration handle + USBC_DEVICE_CONFIGURATION_INTERFACE_V1 BusInterface; // bus custom enumeration interface + PUSBC_FUNCTION_DESCRIPTOR FunctionDescriptor; // usb function descriptor + ULONG FunctionDescriptorCount; // number of function descriptor + PDEVICE_OBJECT * ChildPDO; // child pdos + LIST_ENTRY ResetPortListHead; // reset port list head + LIST_ENTRY CyclePortListHead; // cycle port list head + UCHAR ResetPortActive; // reset port active + UCHAR CyclePortActive; // cycle port active + KSPIN_LOCK Lock; // reset / cycle port list lock +}FDO_DEVICE_EXTENSION, *PFDO_DEVICE_EXTENSION; + +#define USBCCPG_TAG 'cbsu' + +typedef struct +{ + COMMON_DEVICE_EXTENSION Common; // shared with FDO + PUSBC_FUNCTION_DESCRIPTOR FunctionDescriptor; // function descriptor + PDEVICE_OBJECT NextDeviceObject; // next device object + DEVICE_CAPABILITIES Capabilities; // device capabilities + ULONG FunctionIndex; // function index + USB_DEVICE_DESCRIPTOR DeviceDescriptor; // usb device descriptor + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; // usb configuration descriptor + USBD_CONFIGURATION_HANDLE ConfigurationHandle; // configuration handle + PUSBD_INTERFACE_LIST_ENTRY InterfaceList; // interface list + ULONG InterfaceListCount; // interface list count + PFDO_DEVICE_EXTENSION FDODeviceExtension; // pointer to fdo's pdo list +}PDO_DEVICE_EXTENSION, *PPDO_DEVICE_EXTENSION; + +/* descriptor.c */ + +NTSTATUS +USBCCGP_GetDescriptors( + IN PDEVICE_OBJECT DeviceObject); + +NTSTATUS +USBCCGP_SelectConfiguration( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension); + +NTSTATUS +NTAPI +USBCCGP_GetDescriptor( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR DescriptorType, + IN ULONG DescriptorLength, + IN UCHAR DescriptorIndex, + IN LANGID LanguageId, + OUT PVOID *OutDescriptor); + +ULONG +CountInterfaceDescriptors( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor); + +NTSTATUS +AllocateInterfaceDescriptorsArray( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + OUT PUSB_INTERFACE_DESCRIPTOR **OutArray); + +/* misc.c */ + +NTSTATUS +NTAPI +USBCCGP_SyncForwardIrp( + PDEVICE_OBJECT DeviceObject, + PIRP Irp); + +NTSTATUS +USBCCGP_SyncUrbRequest( + IN PDEVICE_OBJECT DeviceObject, + OUT PURB UrbRequest); + +PVOID +AllocateItem( + IN POOL_TYPE PoolType, + IN ULONG ItemSize); + +VOID +FreeItem( + IN PVOID Item); + +VOID +DumpFunctionDescriptor( + IN PUSBC_FUNCTION_DESCRIPTOR FunctionDescriptor, + IN ULONG FunctionDescriptorCount); + +/* fdo.c */ + +NTSTATUS +FDO_Dispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp); + +/* pdo.c */ + +NTSTATUS +PDO_Dispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp); + +/* function.c */ + +NTSTATUS +USBCCGP_QueryInterface( + IN PDEVICE_OBJECT DeviceObject, + OUT PUSBC_DEVICE_CONFIGURATION_INTERFACE_V1 BusInterface); + +NTSTATUS +USBCCGP_EnumerateFunctions( + IN PDEVICE_OBJECT DeviceObject); + +#endif diff --git a/reactos/drivers/usb/usbccgp/usbccgp.rbuild b/reactos/drivers/usb/usbccgp/usbccgp.rbuild new file mode 100644 index 00000000000..4b26820fbed --- /dev/null +++ b/reactos/drivers/usb/usbccgp/usbccgp.rbuild @@ -0,0 +1,19 @@ + + + + + 0x600 + + include + ntoskrnl + hal + usbd + pseh + descriptor.c + fdo.c + function.c + misc.c + pdo.c + usbccgp.c + usbccgp.rc + diff --git a/reactos/drivers/usb/usbccgp/usbccgp.rc b/reactos/drivers/usb/usbccgp/usbccgp.rc new file mode 100644 index 00000000000..df848aa5e4d --- /dev/null +++ b/reactos/drivers/usb/usbccgp/usbccgp.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "USBCCGP Driver\0" +#define REACTOS_STR_INTERNAL_NAME "usccpg\0" +#define REACTOS_STR_ORIGINAL_FILENAME "usbccpg.sys\0" +#include diff --git a/reactos/drivers/usb/usbd/CMakeLists.txt b/reactos/drivers/usb/usbd/CMakeLists.txt index 5f30aab7530..b04f9e61ed6 100644 --- a/reactos/drivers/usb/usbd/CMakeLists.txt +++ b/reactos/drivers/usb/usbd/CMakeLists.txt @@ -9,3 +9,5 @@ add_library(usbd SHARED set_module_type(usbd kernelmodedriver) add_importlibs(usbd ntoskrnl hal) add_importlib_target(usbd.spec) + +add_cd_file(TARGET usbd DESTINATION reactos/system32/drivers NO_CAB FOR all) \ No newline at end of file diff --git a/reactos/drivers/usb/usbd/usbd.c b/reactos/drivers/usb/usbd/usbd.c index 5732a343f0d..fa3735acc16 100644 --- a/reactos/drivers/usb/usbd/usbd.c +++ b/reactos/drivers/usb/usbd/usbd.c @@ -32,9 +32,10 @@ * USBD_GetPdoRegistryParameters (implemented) */ -#include +#include #include - +#define NDEBUG +#include #ifndef PLUGPLAY_REGKEY_DRIVER #define PLUGPLAY_REGKEY_DRIVER 2 #endif @@ -102,6 +103,7 @@ USBD_Debug_LogEntry(PCHAR Name, ULONG_PTR Info1, ULONG_PTR Info2, PVOID NTAPI USBD_AllocateDeviceName(ULONG Unknown) { + UNIMPLEMENTED return NULL; } @@ -139,6 +141,7 @@ USBD_CalculateUsbBandwidth( ULONG NTAPI USBD_Dispatch(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4) { + UNIMPLEMENTED return 1; } @@ -148,6 +151,7 @@ USBD_Dispatch(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4) VOID NTAPI USBD_FreeDeviceMutex(PVOID Unknown) { + UNIMPLEMENTED } /* @@ -156,6 +160,7 @@ USBD_FreeDeviceMutex(PVOID Unknown) VOID NTAPI USBD_FreeDeviceName(PVOID Unknown) { + UNIMPLEMENTED } /* @@ -164,6 +169,7 @@ USBD_FreeDeviceName(PVOID Unknown) VOID NTAPI USBD_WaitDeviceMutex(PVOID Unknown) { + UNIMPLEMENTED } /* @@ -172,6 +178,7 @@ USBD_WaitDeviceMutex(PVOID Unknown) ULONG NTAPI USBD_GetSuspendPowerState(ULONG Unknown1) { + UNIMPLEMENTED return 0; } @@ -182,6 +189,7 @@ NTSTATUS NTAPI USBD_InitializeDevice(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4, ULONG Unknown5, ULONG Unknown6) { + UNIMPLEMENTED return STATUS_NOT_SUPPORTED; } @@ -193,6 +201,7 @@ USBD_RegisterHostController(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4, ULONG Unknown5, ULONG Unknown6, ULONG Unknown7, ULONG Unknown8, ULONG Unknown9, ULONG Unknown10) { + UNIMPLEMENTED return STATUS_NOT_SUPPORTED; } @@ -202,6 +211,7 @@ USBD_RegisterHostController(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, NTSTATUS NTAPI USBD_GetDeviceInformation(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3) { + UNIMPLEMENTED return STATUS_NOT_SUPPORTED; } @@ -212,6 +222,7 @@ NTSTATUS NTAPI USBD_CreateDevice(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4, ULONG Unknown5) { + UNIMPLEMENTED return STATUS_NOT_SUPPORTED; } @@ -221,6 +232,7 @@ USBD_CreateDevice(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, NTSTATUS NTAPI USBD_RemoveDevice(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3) { + UNIMPLEMENTED return STATUS_NOT_SUPPORTED; } @@ -230,6 +242,7 @@ USBD_RemoveDevice(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3) VOID NTAPI USBD_CompleteRequest(ULONG Unknown1, ULONG Unknown2) { + UNIMPLEMENTED } /* @@ -241,6 +254,7 @@ USBD_RegisterHcFilter( PDEVICE_OBJECT FilterDeviceObject ) { + UNIMPLEMENTED } /* @@ -249,6 +263,7 @@ USBD_RegisterHcFilter( VOID NTAPI USBD_SetSuspendPowerState(ULONG Unknown1, ULONG Unknown2) { + UNIMPLEMENTED } /* @@ -257,6 +272,7 @@ USBD_SetSuspendPowerState(ULONG Unknown1, ULONG Unknown2) NTSTATUS NTAPI USBD_MakePdoName(ULONG Unknown1, ULONG Unknown2) { + UNIMPLEMENTED return STATUS_NOT_SUPPORTED; } @@ -269,6 +285,7 @@ USBD_QueryBusTime( PULONG CurrentFrame ) { + UNIMPLEMENTED return STATUS_NOT_SUPPORTED; } @@ -283,7 +300,7 @@ USBD_GetUSBDIVersion( if (Version != NULL) { Version->USBDI_Version = USBDI_VERSION; - Version->Supported_USB_Version = 0x100; + Version->Supported_USB_Version = 0x200; } } @@ -293,6 +310,7 @@ USBD_GetUSBDIVersion( NTSTATUS NTAPI USBD_RestoreDevice(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3) { + UNIMPLEMENTED return STATUS_NOT_SUPPORTED; } @@ -303,6 +321,7 @@ VOID NTAPI USBD_RegisterHcDeviceCapabilities(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3) { + UNIMPLEMENTED } /* @@ -410,16 +429,39 @@ USBD_ParseDescriptors( LONG DescriptorType ) { - PUSB_COMMON_DESCRIPTOR PComDes = StartPosition; + PUSB_COMMON_DESCRIPTOR CommonDescriptor; - while(PComDes) + /* use start position */ + CommonDescriptor = (PUSB_COMMON_DESCRIPTOR)StartPosition; + + + /* find next available descriptor */ + while(CommonDescriptor) { - if (PComDes >= (PUSB_COMMON_DESCRIPTOR) - ((PLONG)DescriptorBuffer + TotalLength) ) break; - if (PComDes->bDescriptorType == DescriptorType) return PComDes; - if (PComDes->bLength == 0) break; - PComDes = (PUSB_COMMON_DESCRIPTOR)((ULONG_PTR)PComDes + PComDes->bLength); + if ((ULONG_PTR)CommonDescriptor >= ((ULONG_PTR)DescriptorBuffer + TotalLength)) + { + /* end reached */ + DPRINT("End reached %p\n", CommonDescriptor); + return NULL; + } + + DPRINT("CommonDescriptor Type %x Length %x\n", CommonDescriptor->bDescriptorType, CommonDescriptor->bLength); + + /* is the requested one */ + if (CommonDescriptor->bDescriptorType == DescriptorType) + { + /* it is */ + return CommonDescriptor; + } + + /* sanity check */ + ASSERT(CommonDescriptor->bLength); + + /* move to next descriptor */ + CommonDescriptor = (PUSB_COMMON_DESCRIPTOR)((ULONG_PTR)CommonDescriptor + CommonDescriptor->bLength); } + + /* no descriptor found */ return NULL; } @@ -438,45 +480,97 @@ USBD_ParseConfigurationDescriptorEx( LONG InterfaceProtocol ) { - int x = 0; - PUSB_INTERFACE_DESCRIPTOR UsbInterfaceDesc = StartPosition; + BOOLEAN Found; + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; - while(UsbInterfaceDesc) + /* set to start position */ + InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)StartPosition; + + DPRINT("USBD_ParseConfigurationDescriptorEx\n"); + DPRINT("ConfigurationDescriptor %p Length %lu\n", ConfigurationDescriptor, ConfigurationDescriptor->wTotalLength); + DPRINT("CurrentOffset %p Offset %lu\n", StartPosition, ((ULONG_PTR)StartPosition - (ULONG_PTR)ConfigurationDescriptor)); + + while(InterfaceDescriptor) { - UsbInterfaceDesc = (PUSB_INTERFACE_DESCRIPTOR) - USBD_ParseDescriptors(ConfigurationDescriptor, - ConfigurationDescriptor->wTotalLength, - UsbInterfaceDesc, - USB_INTERFACE_DESCRIPTOR_TYPE); + /* get interface descriptor */ + InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR) USBD_ParseDescriptors(ConfigurationDescriptor, ConfigurationDescriptor->wTotalLength, InterfaceDescriptor, USB_INTERFACE_DESCRIPTOR_TYPE); + if (!InterfaceDescriptor) + { + /* no more descriptors available */ + break; + } - if (!UsbInterfaceDesc) break; + DPRINT("InterfaceDescriptor %p InterfaceNumber %x AlternateSetting %x Length %lu\n", InterfaceDescriptor, InterfaceDescriptor->bInterfaceNumber, InterfaceDescriptor->bAlternateSetting, InterfaceDescriptor->bLength); + /* set found */ + Found = TRUE; + + /* is there an interface number provided */ if(InterfaceNumber != -1) { - if(InterfaceNumber != UsbInterfaceDesc->bInterfaceNumber) x = 1; + if(InterfaceNumber != InterfaceDescriptor->bInterfaceNumber) + { + /* interface number does not match */ + Found = FALSE; + } } + + /* is there an alternate setting provided */ if(AlternateSetting != -1) { - if(AlternateSetting != UsbInterfaceDesc->bAlternateSetting) x = 1; + if(AlternateSetting != InterfaceDescriptor->bAlternateSetting) + { + /* alternate setting does not match */ + Found = FALSE; + } } + + /* match on interface class */ if(InterfaceClass != -1) { - if(InterfaceClass != UsbInterfaceDesc->bInterfaceClass) x = 1; + if(InterfaceClass != InterfaceDescriptor->bInterfaceClass) + { + /* no match with interface class criteria */ + Found = FALSE; + } } + + /* match on interface sub class */ if(InterfaceSubClass != -1) { - if(InterfaceSubClass != UsbInterfaceDesc->bInterfaceSubClass) x = 1; + if(InterfaceSubClass != InterfaceDescriptor->bInterfaceSubClass) + { + /* no interface sub class match */ + Found = FALSE; + } } + + /* interface protocol criteria */ if(InterfaceProtocol != -1) { - if(InterfaceProtocol != UsbInterfaceDesc->bInterfaceProtocol) x = 1; + if(InterfaceProtocol != InterfaceDescriptor->bInterfaceProtocol) + { + /* no interface protocol match */ + Found = FALSE; + } } - if (!x) return UsbInterfaceDesc; + if (Found) + { + /* the choosen one */ + return InterfaceDescriptor; + } - if (UsbInterfaceDesc->bLength == 0) break; - UsbInterfaceDesc = UsbInterfaceDesc + UsbInterfaceDesc->bLength; + /* sanity check */ + ASSERT(InterfaceDescriptor->bLength); + + /* move to next descriptor */ + InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)((ULONG_PTR)InterfaceDescriptor + InterfaceDescriptor->bLength); } + + DPRINT("No Descriptor With InterfaceNumber %ld AlternateSetting %ld InterfaceClass %ld InterfaceSubClass %ld InterfaceProtocol %ld found\n", InterfaceNumber, + AlternateSetting, InterfaceClass, InterfaceSubClass, InterfaceProtocol); + return NULL; } @@ -511,28 +605,58 @@ USBD_GetPdoRegistryParameter( NTSTATUS Status; HANDLE DevInstRegKey; + /* Open the device key */ Status = IoOpenDeviceRegistryKey(PhysicalDeviceObject, - PLUGPLAY_REGKEY_DRIVER, STANDARD_RIGHTS_ALL, &DevInstRegKey); + PLUGPLAY_REGKEY_DEVICE, STANDARD_RIGHTS_ALL, &DevInstRegKey); if (NT_SUCCESS(Status)) { - PKEY_VALUE_FULL_INFORMATION FullInfo; + PKEY_VALUE_PARTIAL_INFORMATION PartialInfo; UNICODE_STRING ValueName; ULONG Length; - RtlInitUnicodeString(&ValueName, KeyName); - Length = ParameterLength + KeyNameLength + sizeof(KEY_VALUE_FULL_INFORMATION); - FullInfo = ExAllocatePool(PagedPool, Length); - if (FullInfo) + /* Initialize the unicode string based on caller data */ + ValueName.Buffer = KeyName; + ValueName.Length = ValueName.MaximumLength = KeyNameLength; + + Length = ParameterLength + sizeof(KEY_VALUE_PARTIAL_INFORMATION); + PartialInfo = ExAllocatePool(PagedPool, Length); + if (PartialInfo) { Status = ZwQueryValueKey(DevInstRegKey, &ValueName, - KeyValueFullInformation, FullInfo, Length, &Length); + KeyValuePartialInformation, PartialInfo, Length, &Length); + if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL) + { + /* The caller doesn't want all the data */ + ExFreePool(PartialInfo); + PartialInfo = ExAllocatePool(PagedPool, Length); + if (PartialInfo) + { + Status = ZwQueryValueKey(DevInstRegKey, &ValueName, + KeyValuePartialInformation, PartialInfo, Length, &Length); + } + else + { + Status = STATUS_NO_MEMORY; + } + } + if (NT_SUCCESS(Status)) { + /* Compute the length to copy back */ + if (ParameterLength < PartialInfo->DataLength) + Length = ParameterLength; + else + Length = PartialInfo->DataLength; + RtlCopyMemory(Parameter, - ((PUCHAR)FullInfo) + FullInfo->DataOffset, - ParameterLength /*FullInfo->DataLength*/); + PartialInfo->Data, + Length); + } + + if (PartialInfo) + { + ExFreePool(PartialInfo); } - ExFreePool(FullInfo); } else Status = STATUS_NO_MEMORY; ZwClose(DevInstRegKey); diff --git a/reactos/drivers/usb/usbd/usbd.rbuild b/reactos/drivers/usb/usbd/usbd.rbuild index c5900b5ff35..18ef89793e4 100644 --- a/reactos/drivers/usb/usbd/usbd.rbuild +++ b/reactos/drivers/usb/usbd/usbd.rbuild @@ -1,6 +1,7 @@ + ntoskrnl hal diff --git a/reactos/drivers/usb/usbd/usbd.spec b/reactos/drivers/usb/usbd/usbd.spec index 7b98b931d00..229b91504d2 100644 --- a/reactos/drivers/usb/usbd/usbd.spec +++ b/reactos/drivers/usb/usbd/usbd.spec @@ -1,9 +1,13 @@ @ stdcall USBD_Debug_GetHeap(long long long long) @ stdcall USBD_Debug_RetHeap(ptr long long) +@ stdcall USBD_Debug_LogEntry(ptr ptr ptr ptr) @ stdcall USBD_CalculateUsbBandwidth(long long long) @ stdcall USBD_CreateConfigurationRequestEx(ptr ptr) @ stdcall USBD_CreateConfigurationRequest(ptr ptr) @ stdcall USBD_GetInterfaceLength(ptr ptr) +@ stdcall USBD_ParseConfigurationDescriptor(ptr long long) @ stdcall USBD_ParseConfigurationDescriptorEx(ptr ptr long long long long long) @ stdcall USBD_ParseDescriptors(ptr long ptr long) -;USBD_GetPdoRegistryParameters +@ stdcall USBD_GetPdoRegistryParameter(ptr ptr long ptr long) +@ stdcall USBD_GetUSBDIVersion(ptr) +@ stdcall USBD_QueryBusTime(ptr ptr) \ No newline at end of file diff --git a/reactos/drivers/usb/usbehci/CMakeLists.txt b/reactos/drivers/usb/usbehci/CMakeLists.txt index 0d8a7ba0cf5..8bad4b1d840 100644 --- a/reactos/drivers/usb/usbehci/CMakeLists.txt +++ b/reactos/drivers/usb/usbehci/CMakeLists.txt @@ -1,21 +1,34 @@ -list(APPEND SOURCE - usbehci.c - hardware.c - physmem.c - hwiface.c - transfer.c - fdo.c - pdo.c - common.c - misc.c - irp.c - usbiffn.c +set_cpp() + +remove_definitions(-D_WIN32_WINNT=0x502) +add_definitions(-D_WIN32_WINNT=0x600) + +add_library(usbehci SHARED + usbehci.cpp + usb_device.cpp + usb_request.cpp + usb_queue.cpp + hcd_controller.cpp + hardware.cpp + misc.cpp + purecall.cpp + hub_controller.cpp + memory_manager.cpp usbehci.rc) -add_library(usbehci SHARED ${SOURCE}) +target_link_libraries(usbehci + libcntpr + ${PSEH_LIB}) + +if(MSVC) + set_target_properties(usbehci PROPERTIES COMPILE_FLAGS "/GR-") +else() + target_link_libraries(usbehci -lgcc) + set_target_properties(usbehci PROPERTIES COMPILE_FLAGS "-fno-exceptions -fno-rtti") +endif(MSVC) set_module_type(usbehci kernelmodedriver) add_importlibs(usbehci ntoskrnl hal) -add_cab_target(usbehci 2) +add_cd_file(TARGET usbehci DESTINATION reactos/system32/drivers NO_CAB FOR all) \ No newline at end of file diff --git a/reactos/drivers/usb/usbehci/hardware.cpp b/reactos/drivers/usb/usbehci/hardware.cpp new file mode 100644 index 00000000000..8cec292b180 --- /dev/null +++ b/reactos/drivers/usb/usbehci/hardware.cpp @@ -0,0 +1,1513 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbehci/hcd_controller.cpp + * PURPOSE: USB EHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#define INITGUID +#include "usbehci.h" +#include "hardware.h" + +typedef VOID __stdcall HD_INIT_CALLBACK(IN PVOID CallBackContext); + +BOOLEAN +NTAPI +InterruptServiceRoutine( + IN PKINTERRUPT Interrupt, + IN PVOID ServiceContext); + +VOID +NTAPI +EhciDefferedRoutine( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2); + +VOID +NTAPI +StatusChangeWorkItemRoutine(PVOID Context); + +class CUSBHardwareDevice : public IUSBHardwareDevice +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + // com + NTSTATUS Initialize(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT FunctionalDeviceObject, PDEVICE_OBJECT PhysicalDeviceObject, PDEVICE_OBJECT LowerDeviceObject); + NTSTATUS PnpStart(PCM_RESOURCE_LIST RawResources, PCM_RESOURCE_LIST TranslatedResources); + NTSTATUS PnpStop(void); + NTSTATUS HandlePower(PIRP Irp); + NTSTATUS GetDeviceDetails(PUSHORT VendorId, PUSHORT DeviceId, PULONG NumberOfPorts, PULONG Speed); + NTSTATUS GetDMA(OUT struct IDMAMemoryManager **m_DmaManager); + NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue); + + NTSTATUS StartController(); + NTSTATUS StopController(); + NTSTATUS ResetController(); + NTSTATUS ResetPort(ULONG PortIndex); + + NTSTATUS GetPortStatus(ULONG PortId, OUT USHORT *PortStatus, OUT USHORT *PortChange); + NTSTATUS ClearPortStatus(ULONG PortId, ULONG Status); + NTSTATUS SetPortFeature(ULONG PortId, ULONG Feature); + + VOID SetAsyncListRegister(ULONG PhysicalAddress); + VOID SetPeriodicListRegister(ULONG PhysicalAddress); + struct _QUEUE_HEAD * GetAsyncListQueueHead(); + ULONG GetPeriodicListRegister(); + + VOID SetStatusChangeEndpointCallBack(PVOID CallBack, PVOID Context); + + KIRQL AcquireDeviceLock(void); + VOID ReleaseDeviceLock(KIRQL OldLevel); + // set command + VOID SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd); + + // get command + VOID GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd); + + + // local + BOOLEAN InterruptService(); + + // friend function + friend BOOLEAN NTAPI InterruptServiceRoutine(IN PKINTERRUPT Interrupt, IN PVOID ServiceContext); + friend VOID NTAPI EhciDefferedRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2); + friend VOID NTAPI StatusChangeWorkItemRoutine(PVOID Context); + // constructor / destructor + CUSBHardwareDevice(IUnknown *OuterUnknown){} + virtual ~CUSBHardwareDevice(){} + +protected: + LONG m_Ref; // reference count + PDRIVER_OBJECT m_DriverObject; // driver object + PDEVICE_OBJECT m_PhysicalDeviceObject; // pdo + PDEVICE_OBJECT m_FunctionalDeviceObject; // fdo (hcd controller) + PDEVICE_OBJECT m_NextDeviceObject; // lower device object + KSPIN_LOCK m_Lock; // hardware lock + PKINTERRUPT m_Interrupt; // interrupt object + KDPC m_IntDpcObject; // dpc object for deferred isr processing + PVOID VirtualBase; // virtual base for memory manager + PHYSICAL_ADDRESS PhysicalAddress; // physical base for memory manager + PULONG m_Base; // EHCI operational port base registers + PDMA_ADAPTER m_Adapter; // dma adapter object + ULONG m_MapRegisters; // map registers count + EHCI_CAPS m_Capabilities; // EHCI caps + USHORT m_VendorID; // vendor id + USHORT m_DeviceID; // device id + PQUEUE_HEAD AsyncQueueHead; // async queue head terminator + PUSBQUEUE m_UsbQueue; // usb request queue + PDMAMEMORYMANAGER m_MemoryManager; // memory manager + HD_INIT_CALLBACK* m_SCECallBack; // status change callback routine + PVOID m_SCEContext; // status change callback routine context + BOOLEAN m_DoorBellRingInProgress; // door bell ring in progress + WORK_QUEUE_ITEM m_StatusChangeWorkItem; // work item for status change callback + ULONG m_SyncFramePhysAddr; // periodic frame list physical address + BOOLEAN m_ResetInProgress[16]; // set when a reset is in progress + BUS_INTERFACE_STANDARD m_BusInterface; // pci bus interface + + // read register + ULONG EHCI_READ_REGISTER_ULONG(ULONG Offset); + + // write register + VOID EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value); +}; + +//================================================================================================= +// COM +// +NTSTATUS +STDMETHODCALLTYPE +CUSBHardwareDevice::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + if (IsEqualGUIDAligned(refiid, IID_IUnknown)) + { + *Output = PVOID(PUNKNOWN(this)); + PUNKNOWN(*Output)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS +CUSBHardwareDevice::Initialize( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT FunctionalDeviceObject, + PDEVICE_OBJECT PhysicalDeviceObject, + PDEVICE_OBJECT LowerDeviceObject) +{ + PCI_COMMON_CONFIG PciConfig; + NTSTATUS Status; + ULONG BytesRead; + + DPRINT("CUSBHardwareDevice::Initialize\n"); + + // + // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors. + // + Status = CreateDMAMemoryManager(&m_MemoryManager); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create DMAMemoryManager Object\n"); + return Status; + } + + // + // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules + // + Status = CreateUSBQueue(&m_UsbQueue); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create UsbQueue!\n"); + return Status; + } + + // + // store device objects + // + m_DriverObject = DriverObject; + m_FunctionalDeviceObject = FunctionalDeviceObject; + m_PhysicalDeviceObject = PhysicalDeviceObject; + m_NextDeviceObject = LowerDeviceObject; + + // + // initialize device lock + // + KeInitializeSpinLock(&m_Lock); + + // + // intialize status change work item + // + ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this)); + + m_VendorID = 0; + m_DeviceID = 0; + + Status = GetBusInterface(PhysicalDeviceObject, &m_BusInterface); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to get BusInteface!\n"); + return Status; + } + + BytesRead = (*m_BusInterface.GetBusData)(m_BusInterface.Context, + PCI_WHICHSPACE_CONFIG, + &PciConfig, + 0, + PCI_COMMON_HDR_LENGTH); + + if (BytesRead != PCI_COMMON_HDR_LENGTH) + { + DPRINT1("Failed to get pci config information!\n"); + return STATUS_SUCCESS; + } + + m_VendorID = PciConfig.VendorID; + m_DeviceID = PciConfig.DeviceID; + + + if (PciConfig.Command & PCI_ENABLE_BUS_MASTER) + { + // + // master is enabled + // + return STATUS_SUCCESS; + } + + DPRINT1("PCI Configuration shows this as a non Bus Mastering device! Enabling...\n"); + + PciConfig.Command |= PCI_ENABLE_BUS_MASTER; + m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &PciConfig, 0, PCI_COMMON_HDR_LENGTH); + + BytesRead = (*m_BusInterface.GetBusData)(m_BusInterface.Context, + PCI_WHICHSPACE_CONFIG, + &PciConfig, + 0, + PCI_COMMON_HDR_LENGTH); + + if (BytesRead != PCI_COMMON_HDR_LENGTH) + { + DPRINT1("Failed to get pci config information!\n"); + ASSERT(FALSE); + return STATUS_SUCCESS; + } + + if (!(PciConfig.Command & PCI_ENABLE_BUS_MASTER)) + { + PciConfig.Command |= PCI_ENABLE_BUS_MASTER; + DPRINT1("Failed to enable master\n"); + return STATUS_UNSUCCESSFUL; + } + return STATUS_SUCCESS; +} + +VOID +CUSBHardwareDevice::SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd) +{ + PULONG Register; + Register = (PULONG)UsbCmd; + WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD), *Register); +} + +VOID +CUSBHardwareDevice::GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd) +{ + PULONG Register; + Register = (PULONG)UsbCmd; + *Register = READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD)); +} + +ULONG +CUSBHardwareDevice::EHCI_READ_REGISTER_ULONG(ULONG Offset) +{ + return READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset)); +} + +VOID +CUSBHardwareDevice::EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value) +{ + WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset), Value); +} + +NTSTATUS +CUSBHardwareDevice::PnpStart( + PCM_RESOURCE_LIST RawResources, + PCM_RESOURCE_LIST TranslatedResources) +{ + ULONG Index, Count; + PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor; + DEVICE_DESCRIPTION DeviceDescription; + PHYSICAL_ADDRESS AsyncPhysicalAddress; + PVOID ResourceBase; + NTSTATUS Status; + UCHAR Value; + UCHAR PortCount; + + DPRINT("CUSBHardwareDevice::PnpStart\n"); + for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++) + { + // + // get resource descriptor + // + ResourceDescriptor = &TranslatedResources->List[0].PartialResourceList.PartialDescriptors[Index]; + + switch(ResourceDescriptor->Type) + { + case CmResourceTypeInterrupt: + { + KeInitializeDpc(&m_IntDpcObject, + EhciDefferedRoutine, + this); + + Status = IoConnectInterrupt(&m_Interrupt, + InterruptServiceRoutine, + (PVOID)this, + NULL, + ResourceDescriptor->u.Interrupt.Vector, + (KIRQL)ResourceDescriptor->u.Interrupt.Level, + (KIRQL)ResourceDescriptor->u.Interrupt.Level, + (KINTERRUPT_MODE)(ResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED), + (ResourceDescriptor->ShareDisposition != CmResourceShareDeviceExclusive), + ResourceDescriptor->u.Interrupt.Affinity, + FALSE); + + if (!NT_SUCCESS(Status)) + { + // + // failed to register interrupt + // + DPRINT1("IoConnect Interrupt failed with %x\n", Status); + return Status; + } + break; + } + case CmResourceTypeMemory: + { + // + // get resource base + // + ResourceBase = MmMapIoSpace(ResourceDescriptor->u.Memory.Start, ResourceDescriptor->u.Memory.Length, MmNonCached); + if (!ResourceBase) + { + // + // failed to map registers + // + DPRINT1("MmMapIoSpace failed\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Get controllers capabilities + // + m_Capabilities.Length = READ_REGISTER_UCHAR((PUCHAR)ResourceBase + EHCI_CAPLENGTH); + m_Capabilities.HCIVersion = READ_REGISTER_USHORT((PUSHORT)((ULONG)ResourceBase + EHCI_HCIVERSION)); + m_Capabilities.HCSParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + EHCI_HCSPARAMS)); + m_Capabilities.HCCParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + EHCI_HCCPARAMS)); + + DPRINT1("Controller has %d Length\n", m_Capabilities.Length); + DPRINT1("Controller has %d Ports\n", m_Capabilities.HCSParams.PortCount); + DPRINT1("Controller EHCI Version %x\n", m_Capabilities.HCIVersion); + DPRINT1("Controler EHCI Caps HCSParamsLong %x\n", m_Capabilities.HCSParamsLong); + DPRINT1("Controler EHCI Caps HCCParamsLong %x\n", m_Capabilities.HCCParamsLong); + DPRINT1("Controler EHCI Caps PowerControl %x\n", m_Capabilities.HCSParams.PortPowerControl); + + if (m_Capabilities.HCSParams.PortRouteRules) + { + Count = 0; + PortCount = max(m_Capabilities.HCSParams.PortCount/2, (m_Capabilities.HCSParams.PortCount+1)/2); + do + { + // + // each entry is a 4 bit field EHCI 2.2.5 + // + Value = READ_REGISTER_UCHAR((PUCHAR)(ULONG)ResourceBase + EHCI_HCSP_PORTROUTE + Count); + m_Capabilities.PortRoute[Count*2] = (Value & 0xF0); + + if ((Count*2) + 1 < m_Capabilities.HCSParams.PortCount) + m_Capabilities.PortRoute[(Count*2)+1] = (Value & 0x0F); + + Count++; + }while(Count < PortCount); + } + + // + // Set m_Base to the address of Operational Register Space + // + m_Base = (PULONG)((ULONG)ResourceBase + m_Capabilities.Length); + break; + } + } + } + + + // + // zero device description + // + RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION)); + + // + // initialize device description + // + DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION; + DeviceDescription.Master = TRUE; + DeviceDescription.ScatterGather = TRUE; + DeviceDescription.Dma32BitAddresses = TRUE; + DeviceDescription.DmaWidth = Width32Bits; + DeviceDescription.InterfaceType = PCIBus; + DeviceDescription.MaximumLength = MAXULONG; + + // + // get dma adapter + // + m_Adapter = IoGetDmaAdapter(m_PhysicalDeviceObject, &DeviceDescription, &m_MapRegisters); + if (!m_Adapter) + { + // + // failed to get dma adapter + // + DPRINT1("Failed to acquire dma adapter\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Create Common Buffer + // + VirtualBase = m_Adapter->DmaOperations->AllocateCommonBuffer(m_Adapter, + PAGE_SIZE * 4, + &PhysicalAddress, + FALSE); + if (!VirtualBase) + { + DPRINT1("Failed to allocate a common buffer\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Stop the controller before modifying schedules + // + Status = StopController(); + if (!NT_SUCCESS(Status)) + return Status; + + // + // Initialize the DMAMemoryManager + // + Status = m_MemoryManager->Initialize(this, &m_Lock, PAGE_SIZE * 4, VirtualBase, PhysicalAddress, 32); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to initialize the DMAMemoryManager\n"); + return Status; + } + + // + // Create a queuehead for the Async Register + // + m_MemoryManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&AsyncQueueHead, &AsyncPhysicalAddress); + + AsyncQueueHead->PhysicalAddr = AsyncPhysicalAddress.LowPart; + AsyncQueueHead->HorizontalLinkPointer = AsyncQueueHead->PhysicalAddr | QH_TYPE_QH; + AsyncQueueHead->EndPointCharacteristics.HeadOfReclamation = TRUE; + AsyncQueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED; + AsyncQueueHead->Token.Bits.Halted = TRUE; + + AsyncQueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x01; + AsyncQueueHead->NextPointer = TERMINATE_POINTER; + AsyncQueueHead->CurrentLinkPointer = TERMINATE_POINTER; + + InitializeListHead(&AsyncQueueHead->LinkedQueueHeads); + + // + // Initialize the UsbQueue now that we have an AdapterObject. + // + Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, &m_Lock); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to Initialize the UsbQueue\n"); + return Status; + } + + // + // Start the controller + // + DPRINT1("Starting Controller\n"); + Status = StartController(); + + // + // done + // + return Status; +} + +NTSTATUS +CUSBHardwareDevice::PnpStop(void) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CUSBHardwareDevice::HandlePower( + PIRP Irp) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CUSBHardwareDevice::GetDeviceDetails( + OUT OPTIONAL PUSHORT VendorId, + OUT OPTIONAL PUSHORT DeviceId, + OUT OPTIONAL PULONG NumberOfPorts, + OUT OPTIONAL PULONG Speed) +{ + if (VendorId) + *VendorId = m_VendorID; + if (DeviceId) + *DeviceId = m_DeviceID; + if (NumberOfPorts) + *NumberOfPorts = m_Capabilities.HCSParams.PortCount; + //FIXME: What to returned here? + if (Speed) + *Speed = 0x200; + return STATUS_SUCCESS; +} + +NTSTATUS CUSBHardwareDevice::GetDMA( + OUT struct IDMAMemoryManager **OutDMAMemoryManager) +{ + if (!m_MemoryManager) + return STATUS_UNSUCCESSFUL; + *OutDMAMemoryManager = m_MemoryManager; + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::GetUSBQueue( + OUT struct IUSBQueue **OutUsbQueue) +{ + if (!m_UsbQueue) + return STATUS_UNSUCCESSFUL; + *OutUsbQueue = m_UsbQueue; + return STATUS_SUCCESS; +} + + +NTSTATUS +CUSBHardwareDevice::StartController(void) +{ + EHCI_USBCMD_CONTENT UsbCmd; + ULONG UsbSts, FailSafe, ExtendedCapsSupport, Caps, Index; + UCHAR Value; + LARGE_INTEGER Timeout; + + // + // are extended caps supported + // + ExtendedCapsSupport = (m_Capabilities.HCCParamsLong >> EHCI_ECP_SHIFT) & EHCI_ECP_MASK; + if (ExtendedCapsSupport) + { + DPRINT1("[EHCI] Extended Caps Support detected!\n"); + + // + // sanity check + // + ASSERT(ExtendedCapsSupport >= PCI_COMMON_HDR_LENGTH); + m_BusInterface.GetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Caps, ExtendedCapsSupport, sizeof(ULONG)); + + // + // OS Handoff Synchronization support capability. EHCI 5.1 + // + if ((Caps & EHCI_LEGSUP_CAPID_MASK) == EHCI_LEGSUP_CAPID) + { + // + // is it bios owned + // + if ((Caps & EHCI_LEGSUP_BIOSOWNED)) + { + DPRINT1("[EHCI] Controller is BIOS owned, acquring control\n"); + + // + // acquire ownership + // + Value = 1; + m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, ExtendedCapsSupport+3, sizeof(UCHAR)); + + for(Index = 0; Index < 20; Index++) + { + // + // get status + // + m_BusInterface.GetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Caps, ExtendedCapsSupport, sizeof(ULONG)); + if ((Caps & EHCI_LEGSUP_BIOSOWNED)) + { + // + // lets wait a bit + // + Timeout.QuadPart = 50; + DPRINT1("Waiting %d milliseconds for port reset\n", Timeout.LowPart); + + // + // convert to 100 ns units (absolute) + // + Timeout.QuadPart *= -10000; + + // + // perform the wait + // + KeDelayExecutionThread(KernelMode, FALSE, &Timeout); + } + } + if ((Caps & EHCI_LEGSUP_BIOSOWNED)) + { + // + // failed to aquire ownership + // + DPRINT1("[EHCI] failed to acquire ownership\n"); + } + else if ((Caps & EHCI_LEGSUP_OSOWNED)) + { + // + // HC OS Owned Semaphore EHCI 2.1.7 + // + DPRINT1("[EHCI] acquired ownership\n"); + } +#if 0 + // + // explictly clear the bios owned flag 2.1.7 + // + Value = 0; + m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, ExtendedCapsSupport+2, sizeof(UCHAR)); + + // + // clear SMI interrupt EHCI 2.1.8 + // + Caps = 4; + m_BusInterface.SetBusData(m_BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Caps, ExtendedCapsSupport+4, sizeof(ULONG)); +#endif + } + } + } + + // + // get command register + // + GetCommandRegister(&UsbCmd); + + // + // disable running schedules + // + UsbCmd.PeriodicEnable = FALSE; + UsbCmd.AsyncEnable = FALSE; + SetCommandRegister(&UsbCmd); + + // + // Wait for execution to start + // + for (FailSafe = 100; FailSafe > 1; FailSafe--) + { + KeStallExecutionProcessor(100); + UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS); + + if (!(UsbSts & EHCI_STS_PSS) && (UsbSts & EHCI_STS_ASS)) + { + break; + } + } + + if ((UsbSts & (EHCI_STS_PSS | EHCI_STS_ASS))) + { + DPRINT1("Failed to stop running schedules %x\n", UsbSts); + //ASSERT(FALSE); + } + + + // + // Stop the controller if its running + // + UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS); + if (!(UsbSts & EHCI_STS_HALT)) + { + DPRINT1("Stopping Controller %x\n", UsbSts); + StopController(); + } + + // + // Reset the controller + // + ResetController(); + + // + // check caps + // + if (m_Capabilities.HCCParams.CurAddrBits) + { + // + // disable 64-bit addressing + // + EHCI_WRITE_REGISTER_ULONG(EHCI_CTRLDSSEGMENT, 0x0); + } + + // + // Enable Interrupts and start execution + // + ULONG Mask = EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR | EHCI_USBINTR_PC; + EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, Mask); + + KeStallExecutionProcessor(10); + + ULONG Status = EHCI_READ_REGISTER_ULONG(EHCI_USBINTR); + + DPRINT1("Interrupt Mask %x\n", Status); + ASSERT((Status & Mask) == Mask); + + // + // Assign the SyncList Register + // + EHCI_WRITE_REGISTER_ULONG(EHCI_PERIODICLISTBASE, m_SyncFramePhysAddr); + + // + // Set Schedules to Enable and Interrupt Threshold to 1ms. + // + RtlZeroMemory(&UsbCmd, sizeof(EHCI_USBCMD_CONTENT)); + + UsbCmd.PeriodicEnable = TRUE; + UsbCmd.IntThreshold = 0x8; //1ms + UsbCmd.Run = TRUE; + UsbCmd.FrameListSize = 0x0; //1024 + + if (m_Capabilities.HCCParams.ParkMode) + { + // + // enable async park mode + // + UsbCmd.AsyncParkEnable = TRUE; + UsbCmd.AsyncParkCount = 3; + } + + SetCommandRegister(&UsbCmd); + + + // + // Wait for execution to start + // + for (FailSafe = 100; FailSafe > 1; FailSafe--) + { + KeStallExecutionProcessor(100); + UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS); + + if (!(UsbSts & EHCI_STS_HALT) && (UsbSts & EHCI_STS_PSS)) + { + break; + } + } + + if (UsbSts & EHCI_STS_HALT) + { + DPRINT1("Could not start execution on the controller\n"); + //ASSERT(FALSE); + return STATUS_UNSUCCESSFUL; + } + + if (!(UsbSts & EHCI_STS_PSS)) + { + DPRINT1("Could not enable periodic scheduling\n"); + //ASSERT(FALSE); + return STATUS_UNSUCCESSFUL; + } + + // + // Assign the AsyncList Register + // + EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, AsyncQueueHead->PhysicalAddr); + + // + // get command register + // + GetCommandRegister(&UsbCmd); + + // + // preserve bits + // + UsbCmd.AsyncEnable = TRUE; + + // + // enable async + // + SetCommandRegister(&UsbCmd); + + // + // Wait for execution to start + // + for (FailSafe = 100; FailSafe > 1; FailSafe--) + { + KeStallExecutionProcessor(100); + UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS); + + if ((UsbSts & EHCI_STS_ASS)) + { + break; + } + } + + if (!(UsbSts & EHCI_STS_ASS)) + { + DPRINT1("Failed to enable async schedule UsbSts %x\n", UsbSts); + //ASSERT(FALSE); + return STATUS_UNSUCCESSFUL; + } + + DPRINT1("UsbSts %x\n", UsbSts); + GetCommandRegister(&UsbCmd); + + DPRINT1("UsbCmd.PeriodicEnable %x\n", UsbCmd.PeriodicEnable); + DPRINT1("UsbCmd.AsyncEnable %x\n", UsbCmd.AsyncEnable); + DPRINT1("UsbCmd.IntThreshold %x\n", UsbCmd.IntThreshold); + DPRINT1("UsbCmd.Run %x\n", UsbCmd.Run); + DPRINT1("UsbCmd.FrameListSize %x\n", UsbCmd.FrameListSize); + + // + // Set port routing to EHCI controller + // + EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG, 1); + + DPRINT1("EHCI Started!\n"); + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::StopController(void) +{ + EHCI_USBCMD_CONTENT UsbCmd; + ULONG UsbSts, FailSafe; + + // + // Disable Interrupts and stop execution + // + EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR, 0); + + GetCommandRegister(&UsbCmd); + UsbCmd.Run = FALSE; + SetCommandRegister(&UsbCmd); + + for (FailSafe = 100; FailSafe > 1; FailSafe--) + { + KeStallExecutionProcessor(10); + UsbSts = EHCI_READ_REGISTER_ULONG(EHCI_USBSTS); + if (UsbSts & EHCI_STS_HALT) + { + break; + } + } + + if (!(UsbSts & EHCI_STS_HALT)) + { + DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n"); + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::ResetController(void) +{ + EHCI_USBCMD_CONTENT UsbCmd; + ULONG FailSafe; + + GetCommandRegister(&UsbCmd); + UsbCmd.HCReset = TRUE; + SetCommandRegister(&UsbCmd); + + for (FailSafe = 100; FailSafe > 1; FailSafe--) + { + KeStallExecutionProcessor(100); + GetCommandRegister(&UsbCmd); + if (!UsbCmd.HCReset) + break; + } + + if (UsbCmd.HCReset) + { + DPRINT1("EHCI ERROR: Controller is not responding to reset request!\n"); + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::ResetPort( + IN ULONG PortIndex) +{ + ULONG PortStatus; + LARGE_INTEGER Timeout; + + if (PortIndex > m_Capabilities.HCSParams.PortCount) + return STATUS_UNSUCCESSFUL; + + PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex)); + // + // check slow speed line before reset + // + if (PortStatus & EHCI_PRT_SLOWSPEEDLINE) + { + DPRINT1("Non HighSpeed device. Releasing Ownership\n"); + EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP); + return STATUS_DEVICE_NOT_CONNECTED; + } + + ASSERT(PortStatus & EHCI_PRT_CONNECTED); + + // + // Reset and clean enable + // + PortStatus |= EHCI_PRT_RESET; + PortStatus &= ~EHCI_PRT_ENABLED; + EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus); + + // + // delay is 20 ms for port reset as per USB 2.0 spec + // + Timeout.QuadPart = 20; + DPRINT1("Waiting %d milliseconds for port reset\n", Timeout.LowPart); + + // + // convert to 100 ns units (absolute) + // + Timeout.QuadPart *= -10000; + + // + // perform the wait + // + KeDelayExecutionThread(KernelMode, FALSE, &Timeout); + + // + // Clear reset + // + PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex)); + PortStatus &= ~EHCI_PRT_RESET; + EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), PortStatus); + + do + { + // + // wait + // + KeStallExecutionProcessor(100); + + // + // Check that the port reset + // + PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex)); + if (!(PortStatus & EHCI_PRT_RESET)) + break; + } while (TRUE); + + // + // delay is 10 ms + // + Timeout.QuadPart = 10; + DPRINT1("Waiting %d milliseconds for port to recover after reset\n", Timeout.LowPart); + + // + // convert to 100 ns units (absolute) + // + Timeout.QuadPart *= -10000; + + // + // perform the wait + // + KeDelayExecutionThread(KernelMode, FALSE, &Timeout); + + // + // check slow speed line after reset + // + PortStatus = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex)); + if (PortStatus & EHCI_PRT_SLOWSPEEDLINE) + { + DPRINT1("Non HighSpeed device. Releasing Ownership\n"); + EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortIndex), EHCI_PRT_RELEASEOWNERSHIP); + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // this must be enabled now + // + if (PortStatus & EHCI_PRT_ENABLED) + { + DPRINT1("Port is not enabled after reset\n"); + //ASSERT(FALSE); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::GetPortStatus( + ULONG PortId, + OUT USHORT *PortStatus, + OUT USHORT *PortChange) +{ + ULONG Value; + USHORT Status = 0, Change = 0; + + if (PortId > m_Capabilities.HCSParams.PortCount) + return STATUS_UNSUCCESSFUL; + + // + // Get the value of the Port Status and Control Register + // + Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId)); + + // + // If the PowerPortControl is 0 then host controller does not have power control switches + if (!m_Capabilities.HCSParams.PortPowerControl) + { + Status |= USB_PORT_STATUS_POWER; + } + else + { + // Check the value of PortPower + if (Value & EHCI_PRT_POWER) + { + Status |= USB_PORT_STATUS_POWER; + } + } + + // Get Connected Status + if (Value & EHCI_PRT_CONNECTED) + { + Status |= USB_PORT_STATUS_CONNECT; + + // Get Speed. If SlowSpeedLine flag is there then its a slow speed device + if (Value & EHCI_PRT_SLOWSPEEDLINE) + Status |= USB_PORT_STATUS_LOW_SPEED; + else + Status |= USB_PORT_STATUS_HIGH_SPEED; + } + + // Get Enabled Status + if (Value & EHCI_PRT_ENABLED) + Status |= USB_PORT_STATUS_ENABLE; + + // Is it suspended? + if (Value & EHCI_PRT_SUSPEND) + Status |= USB_PORT_STATUS_SUSPEND; + + // a overcurrent is active? + if (Value & EHCI_PRT_OVERCURRENTACTIVE) + Status |= USB_PORT_STATUS_OVER_CURRENT; + + // In a reset state? + if ((Value & EHCI_PRT_RESET) || m_ResetInProgress[PortId]) + { + Status |= USB_PORT_STATUS_RESET; + Change |= USB_PORT_STATUS_RESET; + } + + // + // FIXME: Is the Change here correct? + // + if (Value & EHCI_PRT_CONNECTSTATUSCHANGE) + Change |= USB_PORT_STATUS_CONNECT; + + if (Value & EHCI_PRT_ENABLEDSTATUSCHANGE) + Change |= USB_PORT_STATUS_ENABLE; + + *PortStatus = Status; + *PortChange = Change; + + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::ClearPortStatus( + ULONG PortId, + ULONG Status) +{ + ULONG Value; + + DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status); + + if (PortId > m_Capabilities.HCSParams.PortCount) + return STATUS_UNSUCCESSFUL; + + if (Status == C_PORT_RESET) + { + // + // update port status + // + m_ResetInProgress[PortId] = FALSE; + } + + if (Status == C_PORT_CONNECTION) + { + LARGE_INTEGER Timeout; + + // + // reset status change bits + // + Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId)); + Value |= EHCI_PRT_CONNECTSTATUSCHANGE | EHCI_PRT_ENABLEDSTATUSCHANGE; + EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId), Value); + + // + // delay is 100 ms + // + Timeout.QuadPart = 100; + DPRINT1("Waiting %d milliseconds for port to stabilize after connection\n", Timeout.LowPart); + + // + // convert to 100 ns units (absolute) + // + Timeout.QuadPart *= -10000; + + // + // perform the wait + // + KeDelayExecutionThread(KernelMode, FALSE, &Timeout); + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +CUSBHardwareDevice::SetPortFeature( + ULONG PortId, + ULONG Feature) +{ + ULONG Value; + + DPRINT("CUSBHardwareDevice::SetPortFeature\n"); + + if (PortId > m_Capabilities.HCSParams.PortCount) + return STATUS_UNSUCCESSFUL; + + Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId)); + + if (Feature == PORT_ENABLE) + { + // + // FIXME: EHCI Ports can only be disabled via reset + // + DPRINT1("PORT_ENABLE not supported for EHCI\n"); + } + + if (Feature == PORT_RESET) + { + ResetPort(PortId); + + // + // update cached settings + // + m_ResetInProgress[PortId] = TRUE; + + // + // is there a status change callback + // + if (m_SCECallBack != NULL) + { + // + // issue callback + // + m_SCECallBack(m_SCEContext); + } + } + + if (Feature == PORT_POWER) + { + if (m_Capabilities.HCSParams.PortPowerControl) + { + ULONG Value; + LARGE_INTEGER Timeout; + + // + // enable port power + // + Value = EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * PortId)) | EHCI_PRT_POWER; + EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC, Value); + + // + // delay is 20 ms + // + Timeout.QuadPart = 20; + DPRINT1("Waiting %d milliseconds for port power up\n", Timeout.LowPart); + + // + // convert to 100 ns units (absolute) + // + Timeout.QuadPart *= -10000; + + // + // perform the wait + // + KeDelayExecutionThread(KernelMode, FALSE, &Timeout); + } + } + return STATUS_SUCCESS; +} + +VOID +CUSBHardwareDevice::SetAsyncListRegister( + ULONG PhysicalAddress) +{ + EHCI_WRITE_REGISTER_ULONG(EHCI_ASYNCLISTBASE, PhysicalAddress); +} + +VOID +CUSBHardwareDevice::SetPeriodicListRegister( + ULONG PhysicalAddress) +{ + // + // store physical address + // + m_SyncFramePhysAddr = PhysicalAddress; +} + +struct _QUEUE_HEAD * +CUSBHardwareDevice::GetAsyncListQueueHead() +{ + return AsyncQueueHead; +} + +ULONG CUSBHardwareDevice::GetPeriodicListRegister() +{ + UNIMPLEMENTED + return NULL; +} + +VOID CUSBHardwareDevice::SetStatusChangeEndpointCallBack( + PVOID CallBack, + PVOID Context) +{ + m_SCECallBack = (HD_INIT_CALLBACK*)CallBack; + m_SCEContext = Context; +} + +KIRQL +CUSBHardwareDevice::AcquireDeviceLock(void) +{ + KIRQL OldLevel; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // return old irql + // + return OldLevel; +} + + +VOID +CUSBHardwareDevice::ReleaseDeviceLock( + KIRQL OldLevel) +{ + KeReleaseSpinLock(&m_Lock, OldLevel); +} + +BOOLEAN +NTAPI +InterruptServiceRoutine( + IN PKINTERRUPT Interrupt, + IN PVOID ServiceContext) +{ + CUSBHardwareDevice *This; + ULONG CStatus; + + This = (CUSBHardwareDevice*) ServiceContext; + CStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_USBSTS); + + CStatus &= (EHCI_ERROR_INT | EHCI_STS_INT | EHCI_STS_IAA | EHCI_STS_PCD | EHCI_STS_FLR); + DPRINT("CStatus %x\n", CStatus); + + // + // Check that it belongs to EHCI + // + if (!CStatus) + return FALSE; + + // + // Clear the Status + // + This->EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, CStatus); + + if (CStatus & EHCI_STS_FATAL) + { + This->StopController(); + DPRINT1("EHCI: Host System Error!\n"); + return TRUE; + } + + if (CStatus & EHCI_ERROR_INT) + { + DPRINT1("EHCI Status = 0x%x\n", CStatus); + } + + if (CStatus & EHCI_STS_HALT) + { + DPRINT1("Host Error Unexpected Halt\n"); + // FIXME: Reset controller\n"); + return TRUE; + } + + KeInsertQueueDpc(&This->m_IntDpcObject, This, (PVOID)CStatus); + return TRUE; +} + +VOID NTAPI +EhciDefferedRoutine( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2) +{ + CUSBHardwareDevice *This; + ULONG CStatus, PortStatus, PortCount, i, ShouldRingDoorBell; + NTSTATUS Status = STATUS_SUCCESS; + EHCI_USBCMD_CONTENT UsbCmd; + + This = (CUSBHardwareDevice*) SystemArgument1; + CStatus = (ULONG) SystemArgument2; + + DPRINT("CStatus %x\n", CStatus); + + // + // check for completion of async schedule + // + if (CStatus & (EHCI_STS_RECL| EHCI_STS_INT | EHCI_ERROR_INT)) + { + // + // check if there is a door bell ring in progress + // + if (This->m_DoorBellRingInProgress == FALSE) + { + if (CStatus & EHCI_ERROR_INT) + { + // + // controller reported error + // + DPRINT1("CStatus %x\n", CStatus); + //ASSERT(FALSE); + } + + // + // inform IUSBQueue of a completed queue head + // + This->m_UsbQueue->InterruptCallback(Status, &ShouldRingDoorBell); + + // + // was a queue head completed? + // + if (ShouldRingDoorBell) + { + // + // set door ring bell in progress status flag + // + This->m_DoorBellRingInProgress = TRUE; + + // + // get command register + // + This->GetCommandRegister(&UsbCmd); + + // + // set door rang bell bit + // + UsbCmd.DoorBell = TRUE; + + // + // update command status + // + This->SetCommandRegister(&UsbCmd); + } + } + } + + // + // check if the controller has acknowledged the door bell + // + if (CStatus & EHCI_STS_IAA) + { + // + // controller has acknowledged, assert we rang the bell + // + PC_ASSERT(This->m_DoorBellRingInProgress == TRUE); + + // + // now notify IUSBQueue that it can free completed requests + // + This->m_UsbQueue->CompleteAsyncRequests(); + + // + // door ring bell completed + // + This->m_DoorBellRingInProgress = FALSE; + } + + This->GetDeviceDetails(NULL, NULL, &PortCount, NULL); + if (CStatus & EHCI_STS_PCD) + { + for (i = 0; i < PortCount; i++) + { + PortStatus = This->EHCI_READ_REGISTER_ULONG(EHCI_PORTSC + (4 * i)); + + // + // Device connected or removed + // + if (PortStatus & EHCI_PRT_CONNECTSTATUSCHANGE) + { + if (PortStatus & EHCI_PRT_CONNECTED) + { + DPRINT1("Device connected on port %d\n", i); + + // + //FIXME: Determine device speed + // + if (This->m_Capabilities.HCSParams.CHCCount) + { + if (PortStatus & EHCI_PRT_ENABLED) + { + DPRINT1("Misbeaving controller. Port should be disabled at this point\n"); + } + + if (PortStatus & EHCI_PRT_SLOWSPEEDLINE) + { + DPRINT1("Non HighSpeed device connected. Release ownership\n"); + This->EHCI_WRITE_REGISTER_ULONG(EHCI_PORTSC + (4 * i), EHCI_PRT_RELEASEOWNERSHIP); + continue; + } + } + } + else + { + DPRINT1("Device disconnected on port %d\n", i); + } + + // + // is there a status change callback + // + if (This->m_SCECallBack != NULL) + { + // + // queue work item for processing + // + ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue); + } + + // + // FIXME: This needs to be saved somewhere + // + } + } + } + return; +} + +VOID +NTAPI +StatusChangeWorkItemRoutine( + PVOID Context) +{ + // + // cast to hardware object + // + CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context; + + // + // is there a callback + // + if (This->m_SCECallBack) + { + // + // issue callback + // + This->m_SCECallBack(This->m_SCEContext); + } + +} + +NTSTATUS +CreateUSBHardware( + PUSBHARDWAREDEVICE *OutHardware) +{ + PUSBHARDWAREDEVICE This; + + This = new(NonPagedPool, TAG_USBEHCI) CUSBHardwareDevice(0); + + if (!This) + return STATUS_INSUFFICIENT_RESOURCES; + + This->AddRef(); + + // return result + *OutHardware = (PUSBHARDWAREDEVICE)This; + + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/usb/usbehci/hardware.h b/reactos/drivers/usb/usbehci/hardware.h index 211c578ae93..b940ab2097b 100644 --- a/reactos/drivers/usb/usbehci/hardware.h +++ b/reactos/drivers/usb/usbehci/hardware.h @@ -1,62 +1,105 @@ #pragma once #include -#include - -/* USB Command Register */ -#define EHCI_USBCMD 0x00 -#define EHCI_USBSTS 0x04 -#define EHCI_USBINTR 0x08 -#define EHCI_FRINDEX 0x0C -#define EHCI_CTRLDSSEGMENT 0x10 -#define EHCI_PERIODICLISTBASE 0x14 -#define EHCI_ASYNCLISTBASE 0x18 -#define EHCI_CONFIGFLAG 0x40 -#define EHCI_PORTSC 0x44 - -/* USB Interrupt Register Flags 32 Bits */ -#define EHCI_USBINTR_INTE 0x01 -#define EHCI_USBINTR_ERR 0x02 -#define EHCI_USBINTR_PC 0x04 -#define EHCI_USBINTR_FLROVR 0x08 -#define EHCI_USBINTR_HSERR 0x10 -#define EHCI_USBINTR_ASYNC 0x20 -/* Bits 6:31 Reserved */ - -/* Status Register Flags 32 Bits */ -#define EHCI_STS_INT 0x01 -#define EHCI_STS_ERR 0x02 -#define EHCI_STS_PCD 0x04 -#define EHCI_STS_FLR 0x08 -#define EHCI_STS_FATAL 0x10 -#define EHCI_STS_IAA 0x20 -/* Bits 11:6 Reserved */ -#define EHCI_STS_HALT 0x1000 -#define EHCI_STS_RECL 0x2000 -#define EHCI_STS_PSS 0x4000 -#define EHCI_STS_ASS 0x8000 -#define EHCI_ERROR_INT ( EHCI_STS_FATAL | EHCI_STS_ERR ) -/* Last bit in QUEUE ELEMENT TRANSFER DESCRIPTOR Next Pointer */ -/* Used for Queue Element Transfer Descriptor Pointers - and Queue Head Horizontal Link Pointers */ +// +// Host Controller Capability Registers +// +#define EHCI_CAPLENGTH 0x00 +#define EHCI_HCIVERSION 0x02 +#define EHCI_HCSPARAMS 0x04 +#define EHCI_HCCPARAMS 0x08 +#define EHCI_HCSP_PORTROUTE 0x0c + + +// +// Extended Capabilities +// +#define EHCI_ECP_SHIFT 8 +#define EHCI_ECP_MASK 0xff +#define EHCI_LEGSUP_CAPID_MASK 0xff +#define EHCI_LEGSUP_CAPID 0x01 +#define EHCI_LEGSUP_OSOWNED (1 << 24) +#define EHCI_LEGSUP_BIOSOWNED (1 << 16) + + +// +// EHCI Operational Registers +// +#define EHCI_USBCMD 0x00 +#define EHCI_USBSTS 0x04 +#define EHCI_USBINTR 0x08 +#define EHCI_FRINDEX 0x0C +#define EHCI_CTRLDSSEGMENT 0x10 +#define EHCI_PERIODICLISTBASE 0x14 +#define EHCI_ASYNCLISTBASE 0x18 +#define EHCI_CONFIGFLAG 0x40 +#define EHCI_PORTSC 0x44 + +// +// Interrupt Register Flags +// +#define EHCI_USBINTR_INTE 0x01 +#define EHCI_USBINTR_ERR 0x02 +#define EHCI_USBINTR_PC 0x04 +#define EHCI_USBINTR_FLROVR 0x08 +#define EHCI_USBINTR_HSERR 0x10 +#define EHCI_USBINTR_ASYNC 0x20 +// Bits 6:31 Reserved + +// +// Status Register Flags +// +#define EHCI_STS_INT 0x01 +#define EHCI_STS_ERR 0x02 +#define EHCI_STS_PCD 0x04 +#define EHCI_STS_FLR 0x08 +#define EHCI_STS_FATAL 0x10 +#define EHCI_STS_IAA 0x20 +// Bits 11:6 Reserved +#define EHCI_STS_HALT 0x1000 +#define EHCI_STS_RECL 0x2000 +#define EHCI_STS_PSS 0x4000 +#define EHCI_STS_ASS 0x8000 +#define EHCI_ERROR_INT (EHCI_STS_FATAL | EHCI_STS_ERR) + +// +// Port Register Flags +// +#define EHCI_PRT_CONNECTED 0x01 +#define EHCI_PRT_CONNECTSTATUSCHANGE 0x02 +#define EHCI_PRT_ENABLED 0x04 +#define EHCI_PRT_ENABLEDSTATUSCHANGE 0x08 +#define EHCI_PRT_OVERCURRENTACTIVE 0x10 +#define EHCI_PRT_OVERCURRENTCHANGE 0x20 +#define EHCI_PRT_FORCERESUME 0x40 +#define EHCI_PRT_SUSPEND 0x80 +#define EHCI_PRT_RESET 0x100 +#define EHCI_PRT_SLOWSPEEDLINE 0x400 +#define EHCI_PRT_POWER 0x1000 +#define EHCI_PRT_RELEASEOWNERSHIP 0x2000 + +#define EHCI_PORTSC_DATAMASK 0xffffffd1 +// +// Terminate Pointer used for QueueHeads and Element Transfer Descriptors to mark Pointers as the end +// #define TERMINATE_POINTER 0x01 -/* QUEUE ELEMENT TRANSFER DESCRIPTOR, Token defines and structs */ +// +// QUEUE ELEMENT TRANSFER DESCRIPTOR, defines and structs +// -/* PIDCodes for QETD_TOKEN -OR with QUEUE_TRANSFER_DESCRIPTOR Token.PIDCode*/ +// +// Token Flags +// #define PID_CODE_OUT_TOKEN 0x00 #define PID_CODE_IN_TOKEN 0x01 #define PID_CODE_SETUP_TOKEN 0x02 -/* Split Transaction States -OR with QUEUE_TRANSFER_DESCRIPTOR Token.SplitTransactionState */ #define DO_START_SPLIT 0x00 #define DO_COMPLETE_SPLIT 0x01 -/* Ping States, OR with QUEUE_TRANSFER_DESCRIPTOR Token. */ #define PING_STATE_DO_OUT 0x00 #define PING_STATE_DO_PING 0x01 @@ -67,8 +110,9 @@ typedef struct _PERIODICFRAMELIST ULONG Size; } PERIODICFRAMELIST, *PPERIODICFRAMELIST; - -/* QUEUE ELEMENT TRANSFER DESCRIPTOR TOKEN */ +// +// QUEUE ELEMENT TRANSFER DESCRIPTOR TOKEN +// typedef struct _QETD_TOKEN_BITS { ULONG PingState:1; @@ -87,7 +131,9 @@ typedef struct _QETD_TOKEN_BITS ULONG DataToggle:1; } QETD_TOKEN_BITS, *PQETD_TOKEN_BITS; -/* QUEUE ELEMENT TRANSFER DESCRIPTOR */ +// +// QUEUE ELEMENT TRANSFER DESCRIPTOR +// typedef struct _QUEUE_TRANSFER_DESCRIPTOR { //Hardware @@ -99,19 +145,21 @@ typedef struct _QUEUE_TRANSFER_DESCRIPTOR ULONG DWord; } Token; ULONG BufferPointer[5]; - + //Software - ULONG BufferPointerVA[5]; ULONG PhysicalAddr; - struct _QUEUE_TRANSFER_DESCRIPTOR *PreviousDescriptor; - struct _QUEUE_TRANSFER_DESCRIPTOR *NextDescriptor; + LIST_ENTRY DescriptorEntry; + ULONG TotalBytesToTransfer; } QUEUE_TRANSFER_DESCRIPTOR, *PQUEUE_TRANSFER_DESCRIPTOR; -/* EndPointSpeeds of END_POINT_CHARACTERISTICS */ +C_ASSERT(FIELD_OFFSET(QUEUE_TRANSFER_DESCRIPTOR, PhysicalAddr) == 0x20); + +// +// EndPointSpeeds Flags and END_POINT_CHARACTERISTICS +// #define QH_ENDPOINT_FULLSPEED 0x00 #define QH_ENDPOINT_LOWSPEED 0x01 #define QH_ENDPOINT_HIGHSPEED 0x02 - typedef struct _END_POINT_CHARACTERISTICS { ULONG DeviceAddress:7; @@ -125,39 +173,39 @@ typedef struct _END_POINT_CHARACTERISTICS ULONG NakCountReload:4; } END_POINT_CHARACTERISTICS, *PEND_POINT_CHARACTERISTICS; +// +// Capabilities +// typedef struct _END_POINT_CAPABILITIES { ULONG InterruptScheduleMask:8; ULONG SplitCompletionMask:8; ULONG HubAddr:6; ULONG PortNumber:6; - /* Multi */ ULONG NumberOfTransactionPerFrame:2; } END_POINT_CAPABILITIES, *PEND_POINT_CAPABILITIES; - -/* QUEUE HEAD defines and structs */ - -/* QUEUE HEAD Select Types, OR with QUEUE_HEAD HorizontalLinkPointer */ -#define QH_TYPE_IDT 0x00 -#define QH_TYPE_QH 0x02 +// +// QUEUE HEAD Flags and Struct +// +#define QH_TYPE_IDT 0x00 +#define QH_TYPE_QH 0x02 #define QH_TYPE_SITD 0x04 #define QH_TYPE_FSTN 0x06 -/* QUEUE HEAD */ typedef struct _QUEUE_HEAD { //Hardware ULONG HorizontalLinkPointer; END_POINT_CHARACTERISTICS EndPointCharacteristics; END_POINT_CAPABILITIES EndPointCapabilities; - /* TERMINATE_POINTER not valid for this member */ + // TERMINATE_POINTER not valid for this member ULONG CurrentLinkPointer; - /* TERMINATE_POINTER valid */ + // TERMINATE_POINTER valid ULONG NextPointer; - /* TERMINATE_POINTER valid, bits 1:4 is NAK_COUNTER */ + // TERMINATE_POINTER valid, bits 1:4 is NAK_COUNTERd ULONG AlternateNextPointer; - /* Only DataToggle, InterruptOnComplete, ErrorCounter, PingState valid */ + // Only DataToggle, InterruptOnComplete, ErrorCounter, PingState valid union { QETD_TOKEN_BITS Bits; @@ -167,18 +215,28 @@ typedef struct _QUEUE_HEAD //Software ULONG PhysicalAddr; - struct _QUEUE_HEAD *PreviousQueueHead; - struct _QUEUE_HEAD *NextQueueHead; - ULONG NumberOfTransferDescriptors; - PQUEUE_TRANSFER_DESCRIPTOR FirstTransferDescriptor; - PQUEUE_TRANSFER_DESCRIPTOR DeadDescriptor; - PIRP IrpToComplete; - PKEVENT Event; - PMDL Mdl; - BOOLEAN FreeMdl; + LIST_ENTRY LinkedQueueHeads; + LIST_ENTRY TransferDescriptorListHead; + PVOID Request; } QUEUE_HEAD, *PQUEUE_HEAD; -/* USBCMD register 32 bits */ +C_ASSERT(sizeof(END_POINT_CHARACTERISTICS) == 4); +C_ASSERT(sizeof(END_POINT_CAPABILITIES) == 4); + +C_ASSERT(FIELD_OFFSET(QUEUE_HEAD, HorizontalLinkPointer) == 0x00); +C_ASSERT(FIELD_OFFSET(QUEUE_HEAD, EndPointCharacteristics) == 0x04); +C_ASSERT(FIELD_OFFSET(QUEUE_HEAD, EndPointCapabilities) == 0x08); +C_ASSERT(FIELD_OFFSET(QUEUE_HEAD, CurrentLinkPointer) == 0xC); +C_ASSERT(FIELD_OFFSET(QUEUE_HEAD, NextPointer) == 0x10); +C_ASSERT(FIELD_OFFSET(QUEUE_HEAD, AlternateNextPointer) == 0x14); +C_ASSERT(FIELD_OFFSET(QUEUE_HEAD, Token) == 0x18); +C_ASSERT(FIELD_OFFSET(QUEUE_HEAD, BufferPointer) == 0x1C); +C_ASSERT(FIELD_OFFSET(QUEUE_HEAD, PhysicalAddr) == 0x30); + + +// +// Command register content +// typedef struct _EHCI_USBCMD_CONTENT { ULONG Run : 1; @@ -194,41 +252,8 @@ typedef struct _EHCI_USBCMD_CONTENT ULONG Reserved1 : 4; ULONG IntThreshold : 8; ULONG Reserved2 : 8; - } EHCI_USBCMD_CONTENT, *PEHCI_USBCMD_CONTENT; -typedef struct _EHCI_USBSTS_CONTENT -{ - ULONG USBInterrupt:1; - ULONG ErrorInterrupt:1; - ULONG DetectChangeInterrupt:1; - ULONG FrameListRolloverInterrupt:1; - ULONG HostSystemErrorInterrupt:1; - ULONG AsyncAdvanceInterrupt:1; - ULONG Reserved:6; - ULONG HCHalted:1; - ULONG Reclamation:1; - ULONG PeriodicScheduleStatus:1; - ULONG AsynchronousScheduleStatus:1; -} EHCI_USBSTS_CONTEXT, *PEHCI_USBSTS_CONTEXT; - -typedef struct _EHCI_USBPORTSC_CONTENT -{ - ULONG CurrentConnectStatus:1; - ULONG ConnectStatusChange:1; - ULONG PortEnabled:1; - ULONG PortEnableChanged:1; - ULONG OverCurrentActive:1; - ULONG OverCurrentChange:1; - ULONG ForcePortResume:1; - ULONG Suspend:1; - ULONG PortReset:1; - ULONG Reserved:1; - ULONG LineStatus:2; - ULONG PortPower:1; - ULONG PortOwner:1; -} EHCI_USBPORTSC_CONTENT, *PEHCI_USBPORTSC_CONTENT; - typedef struct _EHCI_HCS_CONTENT { ULONG PortCount : 4; @@ -265,63 +290,18 @@ typedef struct _EHCI_CAPS { EHCI_HCS_CONTENT HCSParams; ULONG HCSParamsLong; }; - ULONG HCCParams; - UCHAR PortRoute [8]; + union + { + EHCI_HCC_CONTENT HCCParams; + ULONG HCCParamsLong; + }; + UCHAR PortRoute [15]; } EHCI_CAPS, *PEHCI_CAPS; -typedef struct _EHCIPORTS +typedef struct { - ULONG PortNumber; - ULONG PortType; - USHORT PortStatus; - USHORT PortChange; -} EHCIPORTS, *PEHCIPORTS; + ULONG PortStatus; + ULONG PortChange; +}EHCI_PORT_STATUS; -typedef struct _EHCI_HOST_CONTROLLER -{ - PDMA_ADAPTER pDmaAdapter; - ULONG MapRegisters; - ULONG OpRegisters; - EHCI_CAPS ECHICaps; - ULONG NumberOfPorts; - EHCIPORTS Ports[127]; - PVOID CommonBufferVA[16]; - PHYSICAL_ADDRESS CommonBufferPA[16]; - ULONG CommonBufferSize; - PQUEUE_HEAD AsyncListQueue; - PQUEUE_HEAD CompletedListQueue; - KSPIN_LOCK Lock; -} EHCI_HOST_CONTROLLER, *PEHCI_HOST_CONTROLLER; -ULONG -ReadControllerStatus(PEHCI_HOST_CONTROLLER hcd); - -VOID -ClearControllerStatus(PEHCI_HOST_CONTROLLER hcd, ULONG Status); - -VOID -GetCapabilities(PEHCI_CAPS PCap, ULONG CapRegister); - -VOID -ResetPort(PEHCI_HOST_CONTROLLER hcd, UCHAR Port); - -VOID -StartEhci(PEHCI_HOST_CONTROLLER hcd); - -VOID -StopEhci(PEHCI_HOST_CONTROLLER hcd); - -VOID -SetAsyncListQueueRegister(PEHCI_HOST_CONTROLLER hcd, ULONG PhysicalAddr); - -ULONG -GetAsyncListQueueRegister(PEHCI_HOST_CONTROLLER hcd); - -VOID -SetPeriodicFrameListRegister(PEHCI_HOST_CONTROLLER hcd, ULONG PhysicalAddr); - -ULONG -GetPeriodicFrameListRegister(PEHCI_HOST_CONTROLLER hcd); - -BOOLEAN -EnumControllerPorts(PEHCI_HOST_CONTROLLER hcd); diff --git a/reactos/drivers/usb/usbehci/hcd_controller.cpp b/reactos/drivers/usb/usbehci/hcd_controller.cpp new file mode 100644 index 00000000000..bbbf04ccd1a --- /dev/null +++ b/reactos/drivers/usb/usbehci/hcd_controller.cpp @@ -0,0 +1,774 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbehci/hcd_controller.cpp + * PURPOSE: USB EHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#define INITGUID +#include "usbehci.h" + +class CHCDController : public IHCDController, + public IDispatchIrp +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + // IHCDController interface functions + NTSTATUS Initialize(IN PROOTHDCCONTROLLER RootHCDController, IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject); + + // IDispatchIrp interface functions + NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + + // local functions + NTSTATUS CreateFDO(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * OutDeviceObject); + NTSTATUS SetSymbolicLink(BOOLEAN Enable); + + // constructor / destructor + CHCDController(IUnknown *OuterUnknown){} + virtual ~CHCDController(){} + +protected: + LONG m_Ref; + PROOTHDCCONTROLLER m_RootController; + PDRIVER_OBJECT m_DriverObject; + PDEVICE_OBJECT m_PhysicalDeviceObject; + PDEVICE_OBJECT m_FunctionalDeviceObject; + PDEVICE_OBJECT m_NextDeviceObject; + PUSBHARDWAREDEVICE m_Hardware; + PHUBCONTROLLER m_HubController; + ULONG m_FDODeviceNumber; +}; + +//================================================================================================= +// COM +// +NTSTATUS +STDMETHODCALLTYPE +CHCDController::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} + +//------------------------------------------------------------------------------------------------- +NTSTATUS +CHCDController::Initialize( + IN PROOTHDCCONTROLLER RootHCDController, + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) +{ + NTSTATUS Status; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + + // + // create usb hardware + // + Status = CreateUSBHardware(&m_Hardware); + if (!NT_SUCCESS(Status)) + { + // + // failed to create hardware object + // + DPRINT1("Failed to create hardware object\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize members + // + m_DriverObject = DriverObject; + m_PhysicalDeviceObject = PhysicalDeviceObject; + m_RootController = RootHCDController; + + // + // create FDO + // + Status = CreateFDO(m_DriverObject, &m_FunctionalDeviceObject); + if (!NT_SUCCESS(Status)) + { + // + // failed to create PDO + // + return Status; + } + + // + // now attach to device stack + // + m_NextDeviceObject = IoAttachDeviceToDeviceStack(m_FunctionalDeviceObject, m_PhysicalDeviceObject); + if (!m_NextDeviceObject) + { + // + // failed to attach to device stack + // + IoDeleteDevice(m_FunctionalDeviceObject); + m_FunctionalDeviceObject = 0; + + return STATUS_NO_SUCH_DEVICE; + } + + // + // initialize hardware object + // + Status = m_Hardware->Initialize(m_DriverObject, m_FunctionalDeviceObject, m_PhysicalDeviceObject, m_NextDeviceObject); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to initialize hardware object %x\n", Status); + + // + // failed to initialize hardware object, detach from device stack + // + IoDetachDevice(m_NextDeviceObject); + + // + // now delete the device + // + IoDeleteDevice(m_FunctionalDeviceObject); + + // + // nullify pointers :) + // + m_FunctionalDeviceObject = 0; + m_NextDeviceObject = 0; + + return Status; + } + + + // + // set device flags + // + m_FunctionalDeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; + + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)m_FunctionalDeviceObject->DeviceExtension; + PC_ASSERT(DeviceExtension); + + // + // initialize device extension + // + DeviceExtension->IsFDO = TRUE; + DeviceExtension->IsHub = FALSE; + DeviceExtension->Dispatcher = PDISPATCHIRP(this); + + // + // device is initialized + // + m_FunctionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + + // + // is there a root controller + // + if (m_RootController) + { + // + // add reference + // + m_RootController->AddRef(); + + // + // register with controller + // + m_RootController->RegisterHCD(this); + } + + + // + // done + // + return STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------------------------- +NTSTATUS +CHCDController::HandleDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + PUSB_HCD_DRIVERKEY_NAME DriverKey; + ULONG ResultLength; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + PC_ASSERT(DeviceExtension->IsFDO); + + DPRINT1("HandleDeviceControl>Type: IoCtl %x InputBufferLength %lu OutputBufferLength %lu\n", + IoStack->Parameters.DeviceIoControl.IoControlCode, + IoStack->Parameters.DeviceIoControl.InputBufferLength, + IoStack->Parameters.DeviceIoControl.OutputBufferLength); + + // + // perform ioctl for FDO + // + if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_HCD_DRIVERKEY_NAME) + { + // + // check if sizee is at least >= USB_HCD_DRIVERKEY_NAME + // + if(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(USB_HCD_DRIVERKEY_NAME)) + { + // + // get device property size + // + Status = IoGetDeviceProperty(m_PhysicalDeviceObject, DevicePropertyDriverKeyName, 0, NULL, &ResultLength); + + // + // get input buffer + // + DriverKey = (PUSB_HCD_DRIVERKEY_NAME)Irp->AssociatedIrp.SystemBuffer; + + // + // check result + // + if (Status == STATUS_BUFFER_TOO_SMALL) + { + // + // does the caller provide enough buffer space + // + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength >= ResultLength) + { + // + // it does + // + Status = IoGetDeviceProperty(m_PhysicalDeviceObject, DevicePropertyDriverKeyName, IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG), DriverKey->DriverKeyName, &ResultLength); + + if (NT_SUCCESS(Status)) + { + // + // informal debug print + // + DPRINT1("Result %S\n", DriverKey->DriverKeyName); + } + } + + // + // store result + // + DriverKey->ActualLength = ResultLength + FIELD_OFFSET(USB_HCD_DRIVERKEY_NAME, DriverKeyName) + sizeof(WCHAR); + Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength; + Status = STATUS_SUCCESS; + } + } + else + { + // + // buffer is certainly too small + // + Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = sizeof(USB_HCD_DRIVERKEY_NAME); + } + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_ROOT_HUB_NAME) + { + // + // check if sizee is at least >= USB_HCD_DRIVERKEY_NAME + // + if(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(USB_HCD_DRIVERKEY_NAME)) + { + // + // sanity check + // + PC_ASSERT(m_HubController); + + // + // get input buffer + // + DriverKey = (PUSB_HCD_DRIVERKEY_NAME)Irp->AssociatedIrp.SystemBuffer; + + // + // get symbolic link + // + Status = m_HubController->GetHubControllerSymbolicLink(IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG), DriverKey->DriverKeyName, &ResultLength); + + + if (NT_SUCCESS(Status)) + { + // + // null terminate it + // + PC_ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG) - sizeof(WCHAR) >= ResultLength); + + DriverKey->DriverKeyName[ResultLength / sizeof(WCHAR)] = L'\0'; + DPRINT1("Result %S\n", DriverKey->DriverKeyName); + } + + // + // store result + // + DriverKey->ActualLength = ResultLength + FIELD_OFFSET(USB_HCD_DRIVERKEY_NAME, DriverKeyName) + sizeof(WCHAR); + Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength; + Status = STATUS_SUCCESS; + } + else + { + // + // buffer is certainly too small + // + Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = sizeof(USB_HCD_DRIVERKEY_NAME); + } + } + + // + // complete the request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // done + // + return Status; +} + +NTSTATUS +CHCDController::HandlePnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PCM_RESOURCE_LIST RawResourceList; + PCM_RESOURCE_LIST TranslatedResourceList; + PDEVICE_RELATIONS DeviceRelations; + NTSTATUS Status; + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + PC_ASSERT(DeviceExtension->IsFDO); + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + switch(IoStack->MinorFunction) + { + case IRP_MN_START_DEVICE: + { + DPRINT("CHCDController::HandlePnp IRP_MN_START FDO\n"); + + // + // first start lower device object + // + Status = SyncForwardIrp(m_NextDeviceObject, Irp); + + if (NT_SUCCESS(Status)) + { + // + // operation succeeded, lets start the device + // + RawResourceList = IoStack->Parameters.StartDevice.AllocatedResources; + TranslatedResourceList = IoStack->Parameters.StartDevice.AllocatedResourcesTranslated; + + if (m_Hardware) + { + // + // start the hardware + // + Status = m_Hardware->PnpStart(RawResourceList, TranslatedResourceList); + } + + // + // enable symbolic link + // + Status = SetSymbolicLink(TRUE); + } + + DPRINT("CHCDController::HandlePnp IRP_MN_START FDO: Status %x\n", Status); + break; + } + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + DPRINT("CHCDController::HandlePnp IRP_MN_QUERY_DEVICE_RELATIONS Type %lx\n", IoStack->Parameters.QueryDeviceRelations.Type); + + if (m_HubController == NULL) + { + // + // create hub controller + // + Status = CreateHubController(&m_HubController); + if (!NT_SUCCESS(Status)) + { + // + // failed to create hub controller + // + break; + } + + // + // initialize hub controller + // + Status = m_HubController->Initialize(m_DriverObject, PHCDCONTROLLER(this), m_Hardware, TRUE, 0 /* FIXME*/); + if (!NT_SUCCESS(Status)) + { + // + // failed to initialize hub controller + // + break; + } + + // + // add reference to prevent it from getting deleting while hub driver adds / removes references + // + m_HubController->AddRef(); + } + + if (IoStack->Parameters.QueryDeviceRelations.Type == BusRelations) + { + // + // allocate device relations + // + DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS)); + + if (!DeviceRelations) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // init device relations + // + DeviceRelations->Count = 1; + Status = m_HubController->GetHubControllerDeviceObject(&DeviceRelations->Objects [0]); + + // + // sanity check + // + PC_ASSERT(Status == STATUS_SUCCESS); + + ObReferenceObject(DeviceRelations->Objects [0]); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + Status = STATUS_SUCCESS; + } + else + { + // + // not supported + // + Status = STATUS_NOT_SUPPORTED; + } + break; + } + case IRP_MN_STOP_DEVICE: + { + DPRINT("CHCDController::HandlePnp IRP_MN_STOP_DEVICE\n"); + + if (m_Hardware) + { + // + // stop the hardware + // + Status = m_Hardware->PnpStop(); + } + else + { + // + // fake success + // + Status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(Status)) + { + // + // stop lower device + // + Status = SyncForwardIrp(m_NextDeviceObject, Irp); + } + break; + } + case IRP_MN_QUERY_REMOVE_DEVICE: + case IRP_MN_QUERY_STOP_DEVICE: + { + // + // sure + // + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(m_NextDeviceObject, Irp); + } + case IRP_MN_REMOVE_DEVICE: + { + DPRINT("CHCDController::HandlePnp IRP_MN_REMOVE_DEVICE FDO\n"); + + // + // delete the symbolic link + // + SetSymbolicLink(FALSE); + + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + IoCallDriver(m_NextDeviceObject, Irp); + + // + // detach device from device stack + // + IoDetachDevice(m_NextDeviceObject); + + // + // delete device + // + IoDeleteDevice(m_FunctionalDeviceObject); + + return STATUS_SUCCESS; + } + default: + { + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(m_NextDeviceObject, Irp); + } + } + + // + // store result and complete request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} + +NTSTATUS +CHCDController::HandlePower( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + UNIMPLEMENTED + + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CHCDController::CreateFDO( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT * OutDeviceObject) +{ + WCHAR CharDeviceName[64]; + NTSTATUS Status; + ULONG UsbDeviceNumber = 0; + UNICODE_STRING DeviceName; + + while (TRUE) + { + // + // construct device name + // + swprintf(CharDeviceName, L"\\Device\\USBFDO-%d", UsbDeviceNumber); + + // + // initialize device name + // + RtlInitUnicodeString(&DeviceName, CharDeviceName); + + // + // create device + // + Status = IoCreateDevice(DriverObject, + sizeof(COMMON_DEVICE_EXTENSION), + &DeviceName, + FILE_DEVICE_CONTROLLER, + 0, + FALSE, + OutDeviceObject); + + // + // check for success + // + if (NT_SUCCESS(Status)) + break; + + // + // is there a device object with that same name + // + if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION)) + { + // + // Try the next name + // + UsbDeviceNumber++; + continue; + } + + // + // bail out on other errors + // + if (!NT_SUCCESS(Status)) + { + DPRINT1("CreateFDO: Failed to create %wZ, Status %x\n", &DeviceName, Status); + return Status; + } + } + + // + // store FDO number + // + m_FDODeviceNumber = UsbDeviceNumber; + + DPRINT("CreateFDO: DeviceName %wZ\n", &DeviceName); + + /* done */ + return Status; +} + +NTSTATUS +CHCDController::SetSymbolicLink( + BOOLEAN Enable) +{ + NTSTATUS Status; + WCHAR LinkName[32]; + WCHAR FDOName[32]; + UNICODE_STRING Link, FDO; + + if (Enable) + { + // + // create legacy link + // + swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber); + swprintf(FDOName, L"\\Device\\USBFDO-%d", m_FDODeviceNumber); + RtlInitUnicodeString(&Link, LinkName); + RtlInitUnicodeString(&FDO, FDOName); + + // + // create symbolic link + // + Status = IoCreateSymbolicLink(&Link, &FDO); + + if (!NT_SUCCESS(Status)) + { + // + // FIXME: handle me + // + ASSERT(0); + } + } + else + { + // + // create legacy link + // + swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber); + RtlInitUnicodeString(&Link, LinkName); + + // + // now delete the symbolic link + // + Status = IoDeleteSymbolicLink(&Link); + + if (!NT_SUCCESS(Status)) + { + // + // FIXME: handle me + // + ASSERT(0); + } + } + + // + // done + // + return Status; +} + +NTSTATUS +CreateHCDController( + PHCDCONTROLLER *OutHcdController) +{ + PHCDCONTROLLER This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBEHCI) CHCDController(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutHcdController = (PHCDCONTROLLER)This; + + // + // done + // + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/usb/usbehci/hub_controller.cpp b/reactos/drivers/usb/usbehci/hub_controller.cpp new file mode 100644 index 00000000000..9b97655df11 --- /dev/null +++ b/reactos/drivers/usb/usbehci/hub_controller.cpp @@ -0,0 +1,3768 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbehci/hub_controller.cpp + * PURPOSE: USB EHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#define INITGUID +#include "usbehci.h" + +VOID StatusChangeEndpointCallBack( + PVOID Context); + +class CHubController : public IHubController, + public IDispatchIrp +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + // IHubController interface functions + virtual NTSTATUS Initialize(IN PDRIVER_OBJECT DriverObject, IN PHCDCONTROLLER Controller, IN PUSBHARDWAREDEVICE Device, IN BOOLEAN IsRootHubDevice, IN ULONG DeviceAddress); + virtual NTSTATUS GetHubControllerDeviceObject(PDEVICE_OBJECT * HubDeviceObject); + virtual NTSTATUS GetHubControllerSymbolicLink(ULONG BufferLength, PVOID Buffer, PULONG RequiredLength); + + // IDispatchIrp interface functions + virtual NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + virtual NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + virtual NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + + // local functions + NTSTATUS HandleQueryInterface(PIO_STACK_LOCATION IoStack); + NTSTATUS SetDeviceInterface(BOOLEAN bEnable); + NTSTATUS CreatePDO(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * OutDeviceObject); + PUSBHARDWAREDEVICE GetUsbHardware(); + ULONG AcquireDeviceAddress(); + VOID ReleaseDeviceAddress(ULONG DeviceAddress); + BOOLEAN ValidateUsbDevice(PUSBDEVICE UsbDevice); + NTSTATUS AddUsbDevice(PUSBDEVICE UsbDevice); + NTSTATUS RemoveUsbDevice(PUSBDEVICE UsbDevice); + VOID SetNotification(PVOID CallbackContext, PRH_INIT_CALLBACK CallbackRoutine); + // internal ioctl routines + NTSTATUS HandleGetDescriptor(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleGetDescriptorFromInterface(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassDevice(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleGetStatusFromDevice(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleSelectConfiguration(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleSelectInterface(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassOther(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassInterface(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassEndpoint(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleBulkOrInterruptTransfer(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleIsochronousTransfer(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClearStall(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleSyncResetAndClearStall(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleAbortPipe(IN OUT PIRP Irp, PURB Urb); + + friend VOID StatusChangeEndpointCallBack(PVOID Context); + + // constructor / destructor + CHubController(IUnknown *OuterUnknown){} + virtual ~CHubController(){} + +protected: + LONG m_Ref; + PHCDCONTROLLER m_Controller; + PUSBHARDWAREDEVICE m_Hardware; + BOOLEAN m_IsRootHubDevice; + ULONG m_DeviceAddress; + + BOOLEAN m_InterfaceEnabled; + UNICODE_STRING m_HubDeviceInterfaceString; + + PDEVICE_OBJECT m_HubControllerDeviceObject; + PDRIVER_OBJECT m_DriverObject; + + PVOID m_HubCallbackContext; + PRH_INIT_CALLBACK m_HubCallbackRoutine; + + USB_DEVICE_DESCRIPTOR m_DeviceDescriptor; + + KSPIN_LOCK m_Lock; + RTL_BITMAP m_DeviceAddressBitmap; + PULONG m_DeviceAddressBitmapBuffer; + LIST_ENTRY m_UsbDeviceList; + PIRP m_PendingSCEIrp; + + //Internal Functions + BOOLEAN QueryStatusChageEndpoint(PIRP Irp); +}; + +typedef struct +{ + LIST_ENTRY Entry; + PUSBDEVICE Device; +}USBDEVICE_ENTRY, *PUSBDEVICE_ENTRY; + +/* Lifted from Linux with slight changes */ +const UCHAR ROOTHUB2_DEVICE_DESCRIPTOR [] = +{ + 0x12, /* bLength; */ + USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType; Device */ + 0x00, 0x20, /* bcdUSB; v1.1 */ + USB_DEVICE_CLASS_HUB, /* bDeviceClass; HUB_CLASSCODE */ + 0x01, /* bDeviceSubClass; */ + 0x00, /* bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* bMaxPacketSize0; 8 Bytes */ + /* Fill Vendor and Product in when init root hub */ + 0x00, 0x00, /* idVendor; */ + 0x00, 0x00, /* idProduct; */ + 0x00, 0x00, /* bcdDevice */ + 0x00, /* iManufacturer; */ + 0x00, /* iProduct; */ + 0x00, /* iSerialNumber; */ + 0x01 /* bNumConfigurations; */ + +}; + +const USB_CONFIGURATION_DESCRIPTOR ROOTHUB2_CONFIGURATION_DESCRIPTOR = +{ + sizeof(USB_CONFIGURATION_DESCRIPTOR), + USB_CONFIGURATION_DESCRIPTOR_TYPE, + sizeof(USB_CONFIGURATION_DESCRIPTOR) + sizeof(USB_INTERFACE_DESCRIPTOR) + sizeof(USB_ENDPOINT_DESCRIPTOR), + 1, + 1, + 0, + 0x40, /* self powered */ + 0x0 +}; + +const USB_INTERFACE_DESCRIPTOR ROOTHUB2_INTERFACE_DESCRIPTOR = +{ + sizeof(USB_INTERFACE_DESCRIPTOR), /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType; Interface */ + 0, /* bInterfaceNumber; */ + 0, /* bAlternateSetting; */ + 0x1, /* bNumEndpoints; */ + 0x09, /* bInterfaceClass; HUB_CLASSCODE */ + 0x01, /* bInterfaceSubClass; */ + 0x00, /* bInterfaceProtocol: */ + 0x00, /* iInterface; */ +}; + +const USB_ENDPOINT_DESCRIPTOR ROOTHUB2_ENDPOINT_DESCRIPTOR = +{ + sizeof(USB_ENDPOINT_DESCRIPTOR), /* bLength */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + 0x81, /* bEndPointAddress */ + USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ + 0x01, /* wMaxPacketSize */ + 0xC /* bInterval */ +}; + +//---------------------------------------------------------------------------------------- +NTSTATUS +STDMETHODCALLTYPE +CHubController::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} +//---------------------------------------------------------------------------------------- +NTSTATUS +CHubController::Initialize( + IN PDRIVER_OBJECT DriverObject, + IN PHCDCONTROLLER Controller, + IN PUSBHARDWAREDEVICE Device, + IN BOOLEAN IsRootHubDevice, + IN ULONG DeviceAddress) +{ + NTSTATUS Status; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + USHORT VendorID, DeviceID; + ULONG Dummy1; + + DPRINT1("CHubController::Initialize\n"); + + // + // initialize members + // + m_Controller = Controller; + m_Hardware = Device; + m_IsRootHubDevice = IsRootHubDevice; + m_DeviceAddress = DeviceAddress; + m_DriverObject = DriverObject; + KeInitializeSpinLock(&m_Lock); + InitializeListHead(&m_UsbDeviceList); + + // + // allocate device address bitmap buffer + // + m_DeviceAddressBitmapBuffer = (PULONG)ExAllocatePoolWithTag(NonPagedPool, 16, TAG_USBEHCI); + if (!m_DeviceAddressBitmapBuffer) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize device address bitmap + // + RtlInitializeBitMap(&m_DeviceAddressBitmap, m_DeviceAddressBitmapBuffer, 128); + RtlClearAllBits(&m_DeviceAddressBitmap); + + + // + // create PDO + // + Status = CreatePDO(m_DriverObject, &m_HubControllerDeviceObject); + if (!NT_SUCCESS(Status)) + { + // + // failed to create hub device object + // + return Status; + } + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)m_HubControllerDeviceObject->DeviceExtension; + + // + // initialize device extension + // + DeviceExtension->IsFDO = FALSE; + DeviceExtension->IsHub = TRUE; //FIXME + DeviceExtension->Dispatcher = PDISPATCHIRP(this); + + // + // intialize device descriptor + // + C_ASSERT(sizeof(USB_DEVICE_DESCRIPTOR) == sizeof(ROOTHUB2_DEVICE_DESCRIPTOR)); + RtlMoveMemory(&m_DeviceDescriptor, ROOTHUB2_DEVICE_DESCRIPTOR, sizeof(USB_DEVICE_DESCRIPTOR)); + + if (NT_SUCCESS(m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &Dummy1, &Dummy1))) + { + // + // update device descriptor + // + m_DeviceDescriptor.idVendor = VendorID; + m_DeviceDescriptor.idProduct = DeviceID; + m_DeviceDescriptor.bcdUSB = 0x200; //FIXME + } + + // + // Set the SCE Callback that the Hardware Device will call on port status change + // + Device->SetStatusChangeEndpointCallBack((PVOID)StatusChangeEndpointCallBack, this); + + // + // clear init flag + // + m_HubControllerDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + return STATUS_SUCCESS; +} + +// +// Queries the ports to see if there has been a device connected or removed. +// +BOOLEAN +CHubController::QueryStatusChageEndpoint( + PIRP Irp) +{ + ULONG PortCount, PortId; + PIO_STACK_LOCATION IoStack; + USHORT PortStatus, PortChange; + PURB Urb; + PUCHAR TransferBuffer; + UCHAR Changed = FALSE; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + ASSERT(IoStack); + + // + // Get the Urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + ASSERT(Urb); + + // + // Get the number of ports and check each one for device connected + // + m_Hardware->GetDeviceDetails(NULL, NULL, &PortCount, NULL); + DPRINT1("SCE Request %p TransferBufferLength %lu Flags %x MDL %p\n", Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength, Urb->UrbBulkOrInterruptTransfer.TransferFlags, Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL); + + TransferBuffer = (PUCHAR)Urb->UrbBulkOrInterruptTransfer.TransferBuffer; + + // + // Loop the ports + // + for (PortId = 0; PortId < PortCount; PortId++) + { + m_Hardware->GetPortStatus(PortId, &PortStatus, &PortChange); + + DPRINT1("Port %d: Status %x, Change %x\n", PortId, PortStatus, PortChange); + + + // + // If theres a flag in PortChange return TRUE so the SCE Irp will be completed + // + if (PortChange != 0) + { + DPRINT1("Change state on port %d\n", PortId); + // Set the value for the port number + *TransferBuffer = 1 << ((PortId + 1) & 7); + Changed = TRUE; + } + } + + return Changed; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::GetHubControllerDeviceObject(PDEVICE_OBJECT * HubDeviceObject) +{ + // + // store controller object + // + *HubDeviceObject = m_HubControllerDeviceObject; + + return STATUS_SUCCESS; +} +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::GetHubControllerSymbolicLink( + ULONG BufferLength, + PVOID Buffer, + PULONG RequiredLength) +{ + if (!m_InterfaceEnabled) + { + // + // device interface not yet enabled + // + return STATUS_UNSUCCESSFUL; + } + + if (BufferLength < (ULONG)m_HubDeviceInterfaceString.Length - 8) + { + // + // buffer too small + // length is without '\??\' + // + *RequiredLength = m_HubDeviceInterfaceString.Length- 8; + + // + // done + // + return STATUS_BUFFER_OVERFLOW; + } + + // + // copy symbolic link + // + RtlCopyMemory(Buffer, &m_HubDeviceInterfaceString.Buffer[4], m_HubDeviceInterfaceString.Length - 8); + + // + // store length, length is without '\??\' + // + *RequiredLength = m_HubDeviceInterfaceString.Length - 8; + + // + // done + // + return STATUS_SUCCESS; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandlePnp( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PDEVICE_CAPABILITIES DeviceCapabilities; + PPNP_BUS_INFORMATION BusInformation; + PDEVICE_RELATIONS DeviceRelations; + NTSTATUS Status; + ULONG Index = 0, Length; + USHORT VendorID, DeviceID; + ULONG HiSpeed, NumPorts; + WCHAR Buffer[300]; + LPWSTR DeviceName; + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(DeviceExtension->IsFDO == FALSE); + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + switch(IoStack->MinorFunction) + { + case IRP_MN_START_DEVICE: + { + DPRINT("CHubController::HandlePnp IRP_MN_START_DEVICE\n"); + // + // register device interface + // + Status = SetDeviceInterface(TRUE); + break; + } + case IRP_MN_QUERY_STOP_DEVICE: + case IRP_MN_QUERY_REMOVE_DEVICE: + { + // + // sure + // + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_ID: + { + DPRINT("CHubController::HandlePnp IRP_MN_QUERY_ID Type %x\n", IoStack->Parameters.QueryId.IdType); + + if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID) + { + if (m_Hardware) + { + // + // query device id + // + Status = m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &NumPorts, &HiSpeed); + + if (HiSpeed == 0x200) + { + // + // USB 2.0 hub + // + swprintf(Buffer, L"USB\\ROOT_HUB20"); + } + else + { + // + // USB 1.1 hub + // + swprintf(Buffer, L"USB\\ROOT_HUB"); + } + + DPRINT("Name %S\n", Buffer); + + // + // calculate length + // + Length = (wcslen(Buffer) + 1); + + // + // allocate buffer + // + DeviceName = (LPWSTR)ExAllocatePoolWithTag(PagedPool, Length * sizeof(WCHAR), TAG_USBEHCI); + + if (!DeviceName) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // copy device name + // + wcscpy(DeviceName, Buffer); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceName; + Status = STATUS_SUCCESS; + break; + } + Status = STATUS_UNSUCCESSFUL; + PC_ASSERT(0); + break; + } + + if (IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs) + { + if (m_Hardware) + { + // + // query device id + // + Status = m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &NumPorts, &HiSpeed); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("CHubController::HandlePnp> failed to get hardware id %x\n", Status); + VendorID = 0x8086; + DeviceID = 0x3A37; + } + + if (HiSpeed == 0x200) + { + // + // USB 2.0 hub + // + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20&VID%04x&PID%04x&REV0000", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20&VID%04x&PID%04x", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20") + 1; + } + else + { + // + // USB 1.1 hub + // + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB&VID%04x&PID%04x&REV0000", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB&VID%04x&PID%04x", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB") + 1; + } + + Buffer[Index] = UNICODE_NULL; + Index++; + + + DPRINT1("Name %S\n", Buffer); + + // + // allocate buffer + // + DeviceName = (LPWSTR)ExAllocatePoolWithTag(PagedPool, Index * sizeof(WCHAR), TAG_USBEHCI); + + if (!DeviceName) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // copy device name + // + RtlMoveMemory(DeviceName, Buffer, Index * sizeof(WCHAR)); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceName; + Status = STATUS_SUCCESS; + break; + } + } + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_CAPABILITIES: + { + DPRINT("CHubController::HandlePnp IRP_MN_QUERY_CAPABILITIES\n"); + + DeviceCapabilities = (PDEVICE_CAPABILITIES)IoStack->Parameters.DeviceCapabilities.Capabilities; + + DeviceCapabilities->LockSupported = FALSE; + DeviceCapabilities->EjectSupported = FALSE; + DeviceCapabilities->Removable = FALSE; + DeviceCapabilities->DockDevice = FALSE; + DeviceCapabilities->UniqueID = FALSE; + DeviceCapabilities->SilentInstall = FALSE; + DeviceCapabilities->RawDeviceOK = FALSE; + DeviceCapabilities->SurpriseRemovalOK = FALSE; + DeviceCapabilities->Address = 0; + DeviceCapabilities->UINumber = 0; + DeviceCapabilities->DeviceD2 = 1; + + /* FIXME */ + DeviceCapabilities->HardwareDisabled = FALSE; + DeviceCapabilities->NoDisplayInUI = FALSE; + DeviceCapabilities->DeviceState[0] = PowerDeviceD0; + for (Index = 1; Index < PowerSystemMaximum; Index++) + DeviceCapabilities->DeviceState[Index] = PowerDeviceD3; + DeviceCapabilities->DeviceWake = PowerDeviceUnspecified; + DeviceCapabilities->D1Latency = 0; + DeviceCapabilities->D2Latency = 0; + DeviceCapabilities->D3Latency = 0; + + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_INTERFACE: + { + DPRINT("CHubController::HandlePnp IRP_MN_QUERY_INTERFACE\n"); + + // + // handle device interface requests + // + Status = HandleQueryInterface(IoStack); + break; + } + case IRP_MN_REMOVE_DEVICE: + { + DPRINT("CHubController::HandlePnp IRP_MN_REMOVE_DEVICE\n"); + + // + // deactivate device interface for BUS PDO + // + SetDeviceInterface(FALSE); + + // + // complete the request first + // + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // now delete device + // + IoDeleteDevice(m_HubControllerDeviceObject); + + // + // nullify pointer + // + m_HubControllerDeviceObject = 0; + + // + // done + // + return STATUS_SUCCESS; + } + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + DPRINT("CHubController::HandlePnp IRP_MN_QUERY_DEVICE_RELATIONS Type %x\n", IoStack->Parameters.QueryDeviceRelations.Type); + + if (IoStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation) + { + // + // allocate device relations + // + DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), TAG_USBEHCI); + if (!DeviceRelations) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // initialize device relations + // + DeviceRelations->Count = 1; + DeviceRelations->Objects[0] = DeviceObject; + ObReferenceObject(DeviceObject); + + // + // done + // + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + } + else + { + // + // not handled + // + Status = Irp->IoStatus.Status; + } + break; + } + case IRP_MN_QUERY_BUS_INFORMATION: + { + DPRINT("CHubController::HandlePnp IRP_MN_QUERY_BUS_INFORMATION\n"); + + // + // allocate buffer for bus information + // + BusInformation = (PPNP_BUS_INFORMATION)ExAllocatePool(PagedPool, sizeof(PNP_BUS_INFORMATION)); + if (BusInformation) + { + // + // copy BUS guid + // + RtlMoveMemory(&BusInformation->BusTypeGuid, &GUID_BUS_TYPE_USB, sizeof(GUID)); + + // + // set bus type + // + BusInformation->LegacyBusType = PNPBus; + BusInformation->BusNumber = 0; + + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = (ULONG_PTR)BusInformation; + } + else + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + } + break; + } + case IRP_MN_STOP_DEVICE: + { + DPRINT("CHubController::HandlePnp IRP_MN_STOP_DEVICE\n"); + // + // stop device + // + Status = STATUS_SUCCESS; + break; + } + default: + { + // + // ignore request with default status + // + Status = Irp->IoStatus.Status; + break; + } + } + + // + // complete request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandlePower( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + UNIMPLEMENTED + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleIsochronousTransfer( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT_DESCRIPTOR EndPointDesc = NULL; + + // + // Check PipeHandle to determine if this is a Bulk or Interrupt Transfer Request + // + EndPointDesc = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbIsochronousTransfer.PipeHandle; + + if (!EndPointDesc) + { + DPRINT1("No EndpointDesc\n"); + Urb->UrbIsochronousTransfer.Hdr.Status = USBD_STATUS_INVALID_PIPE_HANDLE; + return STATUS_INVALID_PARAMETER; + } + + // + // sanity checks + // + ASSERT(EndPointDesc); + ASSERT((EndPointDesc->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_ISOCHRONOUS); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleIsochronousTransfer invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + return UsbDevice->SubmitIrp(Irp); +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleBulkOrInterruptTransfer( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT EndPointDesc = NULL; + // + // First check if the request is for the Status Change Endpoint + // + + // + // Is the Request for the root hub + // + if (Urb->UrbHeader.UsbdDeviceHandle == 0) + { + ASSERT(m_PendingSCEIrp == NULL); + if (QueryStatusChageEndpoint(Irp)) + { + StatusChangeEndpointCallBack(this); + return STATUS_SUCCESS; + } + + // + // Else pend the IRP, to be completed when a device connects or disconnects. + // + DPRINT("Pending SCE Irp\n"); + m_PendingSCEIrp = Irp; + IoMarkIrpPending(Irp); + return STATUS_PENDING; + } + + // + // Check PipeHandle to determine if this is a Bulk or Interrupt Transfer Request + // + EndPointDesc = (PUSB_ENDPOINT)Urb->UrbBulkOrInterruptTransfer.PipeHandle; + + // + // sanity checks + // + ASSERT(EndPointDesc); + ASSERT((EndPointDesc->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK || (EndPointDesc->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleBulkOrInterruptTransfer invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + return UsbDevice->SubmitIrp(Irp); +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassOther( + IN OUT PIRP Irp, + PURB Urb) +{ + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + USHORT PortStatus = 0, PortChange = 0; + PUSHORT Buffer; + ULONG NumPort; + ULONG PortId; + + DPRINT("CHubController::HandleClassOther> Request %x Value %x\n", Urb->UrbControlVendorClassRequest.Request, Urb->UrbControlVendorClassRequest.Value); + + // + // get number of ports available + // + Status = m_Hardware->GetDeviceDetails(NULL, NULL, &NumPort, NULL); + PC_ASSERT(Status == STATUS_SUCCESS); + + // + // sanity check + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.Index - 1 < (USHORT)NumPort); + + // + // port range reported start from 1 -n + // convert back port id so it matches the hardware + // + PortId = Urb->UrbControlVendorClassRequest.Index - 1; + + // + // check request code + // + switch(Urb->UrbControlVendorClassRequest.Request) + { + case USB_REQUEST_GET_STATUS: + { + // + // sanity check + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength == sizeof(USHORT) * 2); + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer); + + // + // get port status + // + Status = m_Hardware->GetPortStatus(PortId, &PortStatus, &PortChange); + + if (NT_SUCCESS(Status)) + { + // + // request contains buffer of 2 ushort which are used from submitting port status and port change status + // + DPRINT("PortId %x PortStatus %x PortChange %x\n", PortId, PortStatus, PortChange); + Buffer = (PUSHORT)Urb->UrbControlVendorClassRequest.TransferBuffer; + + // + // store status, then port change + // + *Buffer = PortStatus; + Buffer++; + *Buffer = PortChange; + } + + // + // done + // + break; + } + case USB_REQUEST_CLEAR_FEATURE: + { + switch (Urb->UrbControlVendorClassRequest.Value) + { + case C_PORT_CONNECTION: + Status = m_Hardware->ClearPortStatus(PortId, C_PORT_CONNECTION); + break; + case C_PORT_RESET: + Status= m_Hardware->ClearPortStatus(PortId, C_PORT_RESET); + break; + default: + DPRINT("Unknown Value for Clear Feature %x \n", Urb->UrbControlVendorClassRequest.Value); + break; + } + + Status = STATUS_SUCCESS; + break; + } + case USB_REQUEST_SET_FEATURE: + { + // + // request set feature + // + switch(Urb->UrbControlVendorClassRequest.Value) + { + case PORT_ENABLE: + { + // + // port enable is a no-op for EHCI + // + Status = STATUS_SUCCESS; + break; + } + + case PORT_SUSPEND: + { + // + // set suspend port feature + // + Status = m_Hardware->SetPortFeature(PortId, PORT_SUSPEND); + break; + } + case PORT_POWER: + { + // + // set power feature on port + // + Status = m_Hardware->SetPortFeature(PortId, PORT_POWER); + break; + } + + case PORT_RESET: + { + // + // reset port feature + // + Status = m_Hardware->SetPortFeature(PortId, PORT_RESET); + PC_ASSERT(Status == STATUS_SUCCESS); + break; + } + default: + DPRINT1("Unsupported request id %x\n", Urb->UrbControlVendorClassRequest.Value); + PC_ASSERT(FALSE); + } + break; + } + default: + DPRINT1("CHubController::HandleClassOther Unknown request code %x\n", Urb->UrbControlVendorClassRequest.Request); + PC_ASSERT(0); + Status = STATUS_INVALID_DEVICE_REQUEST; + } + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleSelectConfiguration( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + PUSBD_INTERFACE_INFORMATION InterfaceInfo; + + // + // is the request for the Root Hub + // + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // FIXME: support setting device to unconfigured state + // + PC_ASSERT(Urb->UrbSelectConfiguration.ConfigurationDescriptor); + + // + // set device handle + // + Urb->UrbSelectConfiguration.ConfigurationHandle = (PVOID)&ROOTHUB2_CONFIGURATION_DESCRIPTOR; + + // + // copy interface info + // + InterfaceInfo = &Urb->UrbSelectConfiguration.Interface; + + InterfaceInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)&ROOTHUB2_INTERFACE_DESCRIPTOR; + InterfaceInfo->Class = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceClass; + InterfaceInfo->SubClass = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceSubClass; + InterfaceInfo->Protocol = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceProtocol; + InterfaceInfo->Reserved = 0; + + // + // sanity check + // + PC_ASSERT(InterfaceInfo->NumberOfPipes == 1); + + // + // copy pipe info + // + InterfaceInfo->Pipes[0].MaximumPacketSize = ROOTHUB2_ENDPOINT_DESCRIPTOR.wMaxPacketSize; + InterfaceInfo->Pipes[0].EndpointAddress = ROOTHUB2_ENDPOINT_DESCRIPTOR.bEndpointAddress; + InterfaceInfo->Pipes[0].Interval = ROOTHUB2_ENDPOINT_DESCRIPTOR.bInterval; + InterfaceInfo->Pipes[0].PipeType = (USBD_PIPE_TYPE)(ROOTHUB2_ENDPOINT_DESCRIPTOR.bmAttributes & USB_ENDPOINT_TYPE_MASK); + InterfaceInfo->Pipes[0].PipeHandle = (PVOID)&ROOTHUB2_ENDPOINT_DESCRIPTOR; + + return STATUS_SUCCESS; + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleSelectConfiguration invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // select configuration + // + return UsbDevice->SelectConfiguration(Urb->UrbSelectConfiguration.ConfigurationDescriptor, &Urb->UrbSelectConfiguration.Interface, &Urb->UrbSelectConfiguration.ConfigurationHandle); + } +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleSelectInterface( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + + // + // sanity check + // + PC_ASSERT(Urb->UrbSelectInterface.ConfigurationHandle); + + // + // is the request for the Root Hub + // + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // no op for root hub + // + return STATUS_SUCCESS; + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleSelectInterface invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // select interface + // + return UsbDevice->SelectInterface(Urb->UrbSelectInterface.ConfigurationHandle, &Urb->UrbSelectInterface.Interface); + } +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleGetStatusFromDevice( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSHORT DeviceStatus; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + + // + // sanity checks + // + PC_ASSERT(Urb->UrbControlGetStatusRequest.TransferBufferLength >= sizeof(USHORT)); + PC_ASSERT(Urb->UrbControlGetStatusRequest.TransferBuffer); + + // + // get status buffer + // + DeviceStatus = (PUSHORT)Urb->UrbControlGetStatusRequest.TransferBuffer; + + + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // FIXME need more flags ? + // + *DeviceStatus = USB_PORT_STATUS_CONNECT; + return STATUS_SUCCESS; + } + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleGetStatusFromDevice invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_STATUS; + CtrlSetup.wValue.LowByte = 0; + CtrlSetup.wValue.HiByte = 0; + CtrlSetup.wIndex.W = Urb->UrbControlGetStatusRequest.Index; + CtrlSetup.wLength = (USHORT)Urb->UrbControlGetStatusRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0x80; + + + if (Urb->UrbHeader.Function == URB_FUNCTION_GET_STATUS_FROM_INTERFACE) + { + // + // add interface type + // + CtrlSetup.bmRequestType.B |= 0x01; + } + else if (Urb->UrbHeader.Function == URB_FUNCTION_GET_STATUS_FROM_ENDPOINT) + { + // + // add interface type + // + CtrlSetup.bmRequestType.B |= 0x02; + } + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + ASSERT(Status == STATUS_SUCCESS); + DPRINT1("CHubController::HandleGetStatusFromDevice Status %x Length %lu DeviceStatus %x\n", Status, Urb->UrbControlDescriptorRequest.TransferBufferLength, *DeviceStatus); + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassDevice( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + PUSB_HUB_DESCRIPTOR UsbHubDescriptor; + ULONG PortCount, Dummy2; + USHORT Dummy1; + PUSBDEVICE UsbDevice; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + + DPRINT("CHubController::HandleClassDevice Request %x Class %x\n", Urb->UrbControlVendorClassRequest.Request, Urb->UrbControlVendorClassRequest.Value >> 8); + + // + // check class request type + // + switch(Urb->UrbControlVendorClassRequest.Request) + { + case USB_REQUEST_GET_STATUS: + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleClassDevice invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_STATUS; + CtrlSetup.wValue.LowByte = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value; + CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wLength = (USHORT)Urb->UrbControlGetStatusRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0xA0; + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + ASSERT(Status == STATUS_SUCCESS); + break; + } + case USB_REQUEST_GET_DESCRIPTOR: + { + switch (Urb->UrbControlVendorClassRequest.Value >> 8) + { + case USB_DEVICE_CLASS_RESERVED: // FALL THROUGH + case USB_DEVICE_CLASS_HUB: + { + // + // sanity checks + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength >= sizeof(USB_HUB_DESCRIPTOR)); + + // + // get hub descriptor + // + UsbHubDescriptor = (PUSB_HUB_DESCRIPTOR)Urb->UrbControlVendorClassRequest.TransferBuffer; + + // + // one hub is handled + // + UsbHubDescriptor->bDescriptorLength = sizeof(USB_HUB_DESCRIPTOR); + Urb->UrbControlVendorClassRequest.TransferBufferLength = sizeof(USB_HUB_DESCRIPTOR); + + // + // type should 0x29 according to msdn + // + UsbHubDescriptor->bDescriptorType = 0x29; + + // + // get port count + // + Status = m_Hardware->GetDeviceDetails(&Dummy1, &Dummy1, &PortCount, &Dummy2); + PC_ASSERT(Status == STATUS_SUCCESS); + + // + // FIXME: retrieve values + // + UsbHubDescriptor->bNumberOfPorts = (UCHAR)PortCount; + UsbHubDescriptor->wHubCharacteristics = 0x00; + UsbHubDescriptor->bPowerOnToPowerGood = 0x01; + UsbHubDescriptor->bHubControlCurrent = 0x00; + + // + // done + // + Status = STATUS_SUCCESS; + break; + } + default: + DPRINT1("CHubController::HandleClassDevice Class %x not implemented\n", Urb->UrbControlVendorClassRequest.Value >> 8); + break; + } + break; + } + default: + DPRINT1("CHubController::HandleClassDevice Type %x not implemented\n", Urb->UrbControlVendorClassRequest.Request); + } + + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleGetDescriptorFromInterface( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + PUSBDEVICE UsbDevice; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + + // + // sanity check + // + ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength); + ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleGetDescriptorFromInterface invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; + CtrlSetup.wValue.LowByte = Urb->UrbControlDescriptorRequest.Index; + CtrlSetup.wValue.HiByte = Urb->UrbControlDescriptorRequest.DescriptorType; + CtrlSetup.wIndex.W = Urb->UrbControlDescriptorRequest.LanguageId; + CtrlSetup.wLength = (USHORT)Urb->UrbControlDescriptorRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0x81; + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + ASSERT(Status == STATUS_SUCCESS); + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleGetDescriptor( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + PUCHAR Buffer; + PUSBDEVICE UsbDevice; + ULONG Length; + + DPRINT("CHubController::HandleGetDescriptor\n"); + + // + // check descriptor type + // + switch(Urb->UrbControlDescriptorRequest.DescriptorType) + { + case USB_DEVICE_DESCRIPTOR_TYPE: + { + // + // sanity check + // + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength >= sizeof(USB_DEVICE_DESCRIPTOR)); + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // copy root hub device descriptor + // + RtlCopyMemory((PUCHAR)Urb->UrbControlDescriptorRequest.TransferBuffer, &m_DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); + Status = STATUS_SUCCESS; + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleGetDescriptor invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // retrieve device descriptor from device + // + UsbDevice->GetDeviceDescriptor((PUSB_DEVICE_DESCRIPTOR)Urb->UrbControlDescriptorRequest.TransferBuffer); + Status = STATUS_SUCCESS; + } + break; + } + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + { + // + // sanity checks + // + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength >= sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // request is for the root bus controller + // + RtlCopyMemory(Urb->UrbControlDescriptorRequest.TransferBuffer, &ROOTHUB2_CONFIGURATION_DESCRIPTOR, sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + // + // get configuration descriptor, very retarded! + // + ConfigurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)Urb->UrbControlDescriptorRequest.TransferBuffer; + + // + // check if buffer can hold interface and endpoint descriptor + // + if (ConfigurationDescriptor->wTotalLength > Urb->UrbControlDescriptorRequest.TransferBufferLength) + { + // + // buffer too small + // + Status = STATUS_SUCCESS; + ASSERT(FALSE); + break; + } + + // + // copy interface descriptor template + // + Buffer = (PUCHAR)(ConfigurationDescriptor + 1); + RtlCopyMemory(Buffer, &ROOTHUB2_INTERFACE_DESCRIPTOR, sizeof(USB_INTERFACE_DESCRIPTOR)); + + // + // copy end point descriptor template + // + Buffer += sizeof(USB_INTERFACE_DESCRIPTOR); + RtlCopyMemory(Buffer, &ROOTHUB2_ENDPOINT_DESCRIPTOR, sizeof(USB_ENDPOINT_DESCRIPTOR)); + + // + // done + // + Status = STATUS_SUCCESS; + + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("USB_CONFIGURATION_DESCRIPTOR_TYPE invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + if (sizeof(USB_CONFIGURATION_DESCRIPTOR) > Urb->UrbControlDescriptorRequest.TransferBufferLength) + { + // + // buffer too small + // + Urb->UrbControlDescriptorRequest.TransferBufferLength = UsbDevice->GetConfigurationDescriptorsLength(); + + // + // bail out + // + Status = STATUS_SUCCESS; + break; + } + + // + // perform work in IUSBDevice + // + UsbDevice->GetConfigurationDescriptors((PUSB_CONFIGURATION_DESCRIPTOR)Urb->UrbControlDescriptorRequest.TransferBuffer, Urb->UrbControlDescriptorRequest.TransferBufferLength, &Length); + + // + // sanity check + // + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength >= Length); + + // + // store result size + // + Urb->UrbControlDescriptorRequest.TransferBufferLength = Length; + Status = STATUS_SUCCESS; + } + break; + } + case USB_STRING_DESCRIPTOR_TYPE: + { + // + // sanity check + // + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength); + + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("USB_STRING_DESCRIPTOR_TYPE invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; + CtrlSetup.wValue.LowByte = Urb->UrbControlDescriptorRequest.Index; + CtrlSetup.wValue.HiByte = Urb->UrbControlDescriptorRequest.DescriptorType; + CtrlSetup.wIndex.W = Urb->UrbControlDescriptorRequest.LanguageId; + CtrlSetup.wLength = (USHORT)Urb->UrbControlDescriptorRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0x80; + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + break; + } + default: + DPRINT1("CHubController::HandleGetDescriptor DescriptorType %x unimplemented\n", Urb->UrbControlDescriptorRequest.DescriptorType); + break; + } + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassEndpoint( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + + // + // sanity check + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength); + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleClassEndpoint invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + DPRINT1("URB_FUNCTION_CLASS_ENDPOINT\n"); + DPRINT1("TransferFlags %x\n", Urb->UrbControlVendorClassRequest.TransferFlags); + DPRINT1("TransferBufferLength %x\n", Urb->UrbControlVendorClassRequest.TransferBufferLength); + DPRINT1("TransferBuffer %x\n", Urb->UrbControlVendorClassRequest.TransferBuffer); + DPRINT1("TransferBufferMDL %x\n", Urb->UrbControlVendorClassRequest.TransferBufferMDL); + DPRINT1("RequestTypeReservedBits %x\n", Urb->UrbControlVendorClassRequest.RequestTypeReservedBits); + DPRINT1("Request %x\n", Urb->UrbControlVendorClassRequest.Request); + DPRINT1("Value %x\n", Urb->UrbControlVendorClassRequest.Value); + DPRINT1("Index %x\n", Urb->UrbControlVendorClassRequest.Index); + + // + // initialize setup packet + // + CtrlSetup.bmRequestType.B = 0x22; //FIXME: Const. + CtrlSetup.bRequest = Urb->UrbControlVendorClassRequest.Request; + CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value; + CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wLength = Urb->UrbControlVendorClassRequest.TransferBufferLength; + + if (Urb->UrbControlVendorClassRequest.TransferFlags & USBD_TRANSFER_DIRECTION_IN) + { + // + // data direction is device to host + // + CtrlSetup.bmRequestType.B |= 0x80; + } + + + // + // issue request + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlVendorClassRequest.TransferBufferLength, Urb->UrbControlVendorClassRequest.TransferBuffer); + + // + // assert on failure + // + PC_ASSERT(NT_SUCCESS(Status)); + + + // + // done + // + return Status; +} + +NTSTATUS +CHubController::HandleSyncResetAndClearStall( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status = STATUS_SUCCESS; + PUSB_ENDPOINT EndpointDescriptor; + ULONG Type; + + // + // sanity check + // + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST)); + PC_ASSERT(Urb->UrbPipeRequest.PipeHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleAbortPipe invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get endpoint descriptor + // + EndpointDescriptor = (PUSB_ENDPOINT)Urb->UrbPipeRequest.PipeHandle; + + // + // get type + // + Type = (EndpointDescriptor->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK); + if (Type != USB_ENDPOINT_TYPE_ISOCHRONOUS) + { + // + // clear stall + // + Status = HandleClearStall(Irp, Urb); + } + DPRINT1("URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL Status %x\n", Status); + + // + // reset data toggle + // + ASSERT(NT_SUCCESS(Status)); + EndpointDescriptor->DataToggle = 0x0; + + // + // done + // + return Status; +} + +NTSTATUS +CHubController::HandleAbortPipe( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status; + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + + // + // sanity check + // + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST)); + PC_ASSERT(Urb->UrbPipeRequest.PipeHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleAbortPipe invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get endpoint descriptor + // + EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbPipeRequest.PipeHandle; + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + // + // issue request + // + Status = UsbDevice->AbortPipe(EndpointDescriptor); + DPRINT1("URB_FUNCTION_ABORT_PIPE Status %x\n", Status); + + // + // done + // + return Status; +} + + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClearStall( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + + + // + // sanity check + // + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST)); + PC_ASSERT(Urb->UrbPipeRequest.PipeHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleClearStall invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get endpoint descriptor + // + EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbPipeRequest.PipeHandle; + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + DPRINT1("URB_FUNCTION_SYNC_CLEAR_STALL\n"); + + // + // initialize setup packet + // + CtrlSetup.bmRequestType.B = 0x02; + CtrlSetup.bRequest = USB_REQUEST_CLEAR_FEATURE; + CtrlSetup.wValue.W = USB_FEATURE_ENDPOINT_STALL; + CtrlSetup.wIndex.W = EndpointDescriptor->bEndpointAddress; + CtrlSetup.wLength = 0; + CtrlSetup.wValue.W = 0; + + // + // issue request + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, 0, 0); + + DPRINT1("URB_FUNCTION_CLEAR_STALL Status %x\n", Status); + + // + // done + // + return Status; +} + + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassInterface( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + + // + // sanity check + // + //ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer || Urb->UrbControlVendorClassRequest.TransferBufferMDL); + //ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength); + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleClassInterface invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + DPRINT1("URB_FUNCTION_CLASS_INTERFACE\n"); + DPRINT1("TransferFlags %x\n", Urb->UrbControlVendorClassRequest.TransferFlags); + DPRINT1("TransferBufferLength %x\n", Urb->UrbControlVendorClassRequest.TransferBufferLength); + DPRINT1("TransferBuffer %x\n", Urb->UrbControlVendorClassRequest.TransferBuffer); + DPRINT1("TransferBufferMDL %x\n", Urb->UrbControlVendorClassRequest.TransferBufferMDL); + DPRINT1("RequestTypeReservedBits %x\n", Urb->UrbControlVendorClassRequest.RequestTypeReservedBits); + DPRINT1("Request %x\n", Urb->UrbControlVendorClassRequest.Request); + DPRINT1("Value %x\n", Urb->UrbControlVendorClassRequest.Value); + DPRINT1("Index %x\n", Urb->UrbControlVendorClassRequest.Index); + + // + // initialize setup packet + // + CtrlSetup.bmRequestType.B = 0x21; + CtrlSetup.bRequest = Urb->UrbControlVendorClassRequest.Request; + CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value; + CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wLength = Urb->UrbControlVendorClassRequest.TransferBufferLength; + + if (Urb->UrbControlVendorClassRequest.TransferFlags & USBD_TRANSFER_DIRECTION_IN) + { + // + // data direction is device to host + // + CtrlSetup.bmRequestType.B |= 0x80; + } + + // + // issue request + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlVendorClassRequest.TransferBufferLength, Urb->UrbControlVendorClassRequest.TransferBuffer); + + // + // assert on failure + // + if (!NT_SUCCESS(Status)) + { + // + // display error + // + DPRINT1("URB_FUNCTION_CLASS_INTERFACE failed with Urb Status %x\n", Urb->UrbHeader.Status); + } + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PURB Urb; + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // determine which request should be performed + // + switch(IoStack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_INTERNAL_USB_SUBMIT_URB: + { + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + PC_ASSERT(Urb); + + switch (Urb->UrbHeader.Function) + { + case URB_FUNCTION_SYNC_RESET_PIPE: + case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL: + Status = HandleSyncResetAndClearStall(Irp, Urb); + break; + case URB_FUNCTION_ABORT_PIPE: + Status = HandleAbortPipe(Irp, Urb); + break; + case URB_FUNCTION_SYNC_CLEAR_STALL: + Status = HandleClearStall(Irp, Urb); + break; + case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE: + Status = HandleGetDescriptorFromInterface(Irp, Urb); + break; + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + Status = HandleGetDescriptor(Irp, Urb); + break; + case URB_FUNCTION_CLASS_DEVICE: + Status = HandleClassDevice(Irp, Urb); + break; + case URB_FUNCTION_GET_STATUS_FROM_DEVICE: + case URB_FUNCTION_GET_STATUS_FROM_INTERFACE: + case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT: + Status = HandleGetStatusFromDevice(Irp, Urb); + break; + case URB_FUNCTION_SELECT_CONFIGURATION: + Status = HandleSelectConfiguration(Irp, Urb); + break; + case URB_FUNCTION_SELECT_INTERFACE: + Status = HandleSelectInterface(Irp, Urb); + break; + case URB_FUNCTION_CLASS_OTHER: + Status = HandleClassOther(Irp, Urb); + break; + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + Status = HandleBulkOrInterruptTransfer(Irp, Urb); + break; + case URB_FUNCTION_ISOCH_TRANSFER: + Status = HandleIsochronousTransfer(Irp, Urb); + break; + case URB_FUNCTION_CLASS_INTERFACE: + Status = HandleClassInterface(Irp, Urb); + break; + case URB_FUNCTION_CLASS_ENDPOINT: + Status = HandleClassEndpoint(Irp, Urb); + break; + default: + DPRINT1("IOCTL_INTERNAL_USB_SUBMIT_URB Function %x NOT IMPLEMENTED\n", Urb->UrbHeader.Function); + break; + } + // + // request completed + // + break; + } + case IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE: + { + DPRINT("IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE %p\n", this); + + if (IoStack->Parameters.Others.Argument1) + { + // + // store object as device handle + // + *(PVOID *)IoStack->Parameters.Others.Argument1 = (PVOID)this; + Status = STATUS_SUCCESS; + } + else + { + // + // mis-behaving hub driver + // + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + // + // request completed + // + break; + } + case IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO: + { + DPRINT("IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO\n"); + + // + // this is the first request send, it delivers the PDO to the caller + // + if (IoStack->Parameters.Others.Argument1) + { + // + // store root hub pdo object + // + *(PVOID *)IoStack->Parameters.Others.Argument1 = DeviceObject; + } + + if (IoStack->Parameters.Others.Argument2) + { + // + // documentation claims to deliver the hcd controller object, although it is wrong + // + *(PVOID *)IoStack->Parameters.Others.Argument2 = DeviceObject; + } + + // + // request completed + // + Status = STATUS_SUCCESS; + break; + } + case IOCTL_INTERNAL_USB_GET_HUB_COUNT: + { + DPRINT("IOCTL_INTERNAL_USB_GET_HUB_COUNT\n"); + + // + // after IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO is delivered, the usbhub driver + // requests this ioctl to deliver the number of presents. + + if (IoStack->Parameters.Others.Argument1) + { + // + // FIXME / verify: there is only one hub + // + *(PULONG)IoStack->Parameters.Others.Argument1 = 1; + } + + // + // request completed + // + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(ULONG); + break; + } + case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION: + { + DPRINT1("IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION UNIMPLEMENTED\n"); + Status = STATUS_SUCCESS; + break; + } + default: + { + DPRINT1("HandleDeviceControl>Type: IoCtl %x InputBufferLength %lu OutputBufferLength %lu NOT IMPLEMENTED\n", + IoStack->Parameters.DeviceIoControl.IoControlCode, + IoStack->Parameters.DeviceIoControl.InputBufferLength, + IoStack->Parameters.DeviceIoControl.OutputBufferLength); + break; + } + } + if (Status != STATUS_PENDING) + { + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + return Status; +} + +//----------------------------------------------------------------------------------------- +PUSBHARDWAREDEVICE +CHubController::GetUsbHardware() +{ + return m_Hardware; +} + +//----------------------------------------------------------------------------------------- +ULONG +CHubController::AcquireDeviceAddress() +{ + KIRQL OldLevel; + ULONG DeviceAddress; + + // + // acquire device lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // find address + // + DeviceAddress = RtlFindClearBits(&m_DeviceAddressBitmap, 1, 0); + if (DeviceAddress != MAXULONG) + { + // + // reserve address + // + RtlSetBits(&m_DeviceAddressBitmap, DeviceAddress, 1); + + // + // device addresses start from 0x1 - 0xFF + // + DeviceAddress++; + } + + // + // release spin lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // return device address + // + return DeviceAddress; +} +//----------------------------------------------------------------------------------------- +VOID +CHubController::ReleaseDeviceAddress( + ULONG DeviceAddress) +{ + KIRQL OldLevel; + + // + // acquire device lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // sanity check + // + PC_ASSERT(DeviceAddress != 0); + + // + // convert back to bit number + // + DeviceAddress--; + + // + // clear bit + // + RtlClearBits(&m_DeviceAddressBitmap, DeviceAddress, 1); + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); +} +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::RemoveUsbDevice( + PUSBDEVICE UsbDevice) +{ + PUSBDEVICE_ENTRY DeviceEntry; + PLIST_ENTRY Entry; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + KIRQL OldLevel; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // point to first entry + // + Entry = m_UsbDeviceList.Flink; + + // + // find matching entry + // + while(Entry != &m_UsbDeviceList) + { + // + // get entry + // + DeviceEntry = (PUSBDEVICE_ENTRY)CONTAINING_RECORD(Entry, USBDEVICE_ENTRY, Entry); + + // + // is it current entry + // + if (DeviceEntry->Device == UsbDevice) + { + // + // remove entry + // + RemoveEntryList(Entry); + + // + // free entry + // + ExFreePoolWithTag(DeviceEntry, TAG_USBEHCI); + + // + // done + // + Status = STATUS_SUCCESS; + break; + } + + // + // goto next device + // + Entry = Entry->Flink; + } + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // return result + // + return Status; +} +//----------------------------------------------------------------------------------------- +BOOLEAN +CHubController::ValidateUsbDevice(PUSBDEVICE UsbDevice) +{ + PUSBDEVICE_ENTRY DeviceEntry; + PLIST_ENTRY Entry; + KIRQL OldLevel; + BOOLEAN Result = FALSE; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // point to first entry + // + Entry = m_UsbDeviceList.Flink; + + // + // find matching entry + // + while(Entry != &m_UsbDeviceList) + { + // + // get entry + // + DeviceEntry = (PUSBDEVICE_ENTRY)CONTAINING_RECORD(Entry, USBDEVICE_ENTRY, Entry); + + // + // is it current entry + // + if (DeviceEntry->Device == UsbDevice) + { + // + // device is valid + // + Result = TRUE; + break; + } + + // + // goto next device + // + Entry = Entry->Flink; + } + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // return result + // + return Result; + +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::AddUsbDevice( + PUSBDEVICE UsbDevice) +{ + PUSBDEVICE_ENTRY DeviceEntry; + KIRQL OldLevel; + + // + // allocate device entry + // + DeviceEntry = (PUSBDEVICE_ENTRY)ExAllocatePoolWithTag(NonPagedPool, sizeof(USBDEVICE_ENTRY), TAG_USBEHCI); + if (!DeviceEntry) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize entry + // + DeviceEntry->Device = UsbDevice; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // insert entry + // + InsertTailList(&m_UsbDeviceList, &DeviceEntry->Entry); + + // + // release spin lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // done + // + return STATUS_SUCCESS; +} + +//----------------------------------------------------------------------------------------- +VOID +CHubController::SetNotification( + PVOID CallbackContext, + PRH_INIT_CALLBACK CallbackRoutine) +{ + KIRQL OldLevel; + + // + // acquire hub controller lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // now set the callback routine and context of the hub + // + m_HubCallbackContext = CallbackContext; + m_HubCallbackRoutine = CallbackRoutine; + + // + // release hub controller lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); +} + +//================================================================================================= +// +// Generic Interface functions +// +VOID +USB_BUSIFFN +USBI_InterfaceReference( + PVOID BusContext) +{ + CHubController * Controller = (CHubController*)BusContext; + + DPRINT1("USBH_InterfaceReference\n"); + + // + // add reference + // + Controller->AddRef(); +} + +VOID +USB_BUSIFFN +USBI_InterfaceDereference( + PVOID BusContext) +{ + CHubController * Controller = (CHubController*)BusContext; + + DPRINT1("USBH_InterfaceDereference\n"); + + // + // release + // + Controller->Release(); +} +//================================================================================================= +// +// USB Hub Interface functions +// +NTSTATUS +USB_BUSIFFN +USBHI_CreateUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE *NewDevice, + PUSB_DEVICE_HANDLE HubDeviceHandle, + USHORT PortStatus, + USHORT PortNumber) +{ + PUSBDEVICE NewUsbDevice; + CHubController * Controller; + NTSTATUS Status; + + DPRINT1("USBHI_CreateUsbDevice\n"); + + // + // first get hub controller + // + Controller = (CHubController *)BusContext; + + // + // sanity check + // + PC_ASSERT(Controller); + PC_ASSERT(BusContext == HubDeviceHandle); + + // + // now allocate usb device + // + Status = CreateUSBDevice(&NewUsbDevice); + + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // release controller + // + Controller->Release(); + DPRINT1("USBHI_CreateUsbDevice: failed to create usb device %x\n", Status); + return Status; + } + + // + // now initialize device + // + Status = NewUsbDevice->Initialize(PHUBCONTROLLER(Controller), Controller->GetUsbHardware(), HubDeviceHandle, PortNumber, PortStatus); + + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // release usb device + // + NewUsbDevice->Release(); + DPRINT1("USBHI_CreateUsbDevice: failed to initialize usb device %x\n", Status); + return Status; + } + + // + // insert into list + // + Status = Controller->AddUsbDevice(NewUsbDevice); + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // release usb device + // + NewUsbDevice->Release(); + + DPRINT1("USBHI_CreateUsbDevice: failed to add usb device %x\n", Status); + return Status; + } + + // + // store the handle + // + *NewDevice = NewUsbDevice; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_InitializeUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + ULONG DeviceAddress; + NTSTATUS Status; + ULONG Index = 0; + + DPRINT1("USBHI_InitializeUsbDevice\n"); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_InitializeUsbDevice invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // now reserve an address + // + DeviceAddress = Controller->AcquireDeviceAddress(); + + // + // is the device address valid + // + if (DeviceAddress == MAXULONG) + { + // + // failed to get an device address from the device address pool + // + DPRINT1("USBHI_InitializeUsbDevice failed to get device address\n"); + return STATUS_DEVICE_DATA_ERROR; + } + + do + { + // + // now set the device address + // + Status = UsbDevice->SetDeviceAddress((UCHAR)DeviceAddress); + + if (NT_SUCCESS(Status)) + break; + + }while(Index++ < 3 ); + + // + // check for failure + // + if (!NT_SUCCESS(Status)) + { + // + // failed to set device address + // + DPRINT1("USBHI_InitializeUsbDevice failed to set address with %x\n", Status); + + // + // release address + // + Controller->ReleaseDeviceAddress(DeviceAddress); + + // + // return error + // + return STATUS_DEVICE_DATA_ERROR; + } + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetUsbDescriptors( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle, + PUCHAR DeviceDescriptorBuffer, + PULONG DeviceDescriptorBufferLength, + PUCHAR ConfigDescriptorBuffer, + PULONG ConfigDescriptorBufferLength) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + + DPRINT1("USBHI_GetUsbDescriptors\n"); + + // + // sanity check + // + PC_ASSERT(DeviceDescriptorBuffer); + PC_ASSERT(DeviceDescriptorBufferLength); + PC_ASSERT(*DeviceDescriptorBufferLength >= sizeof(USB_DEVICE_DESCRIPTOR)); + PC_ASSERT(ConfigDescriptorBufferLength); + PC_ASSERT(*ConfigDescriptorBufferLength >= sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_GetUsbDescriptors invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device descriptor + // + UsbDevice->GetDeviceDescriptor((PUSB_DEVICE_DESCRIPTOR)DeviceDescriptorBuffer); + + // + // store result length + // + *DeviceDescriptorBufferLength = sizeof(USB_DEVICE_DESCRIPTOR); + + // + // get configuration descriptor + // + UsbDevice->GetConfigurationDescriptors((PUSB_CONFIGURATION_DESCRIPTOR)ConfigDescriptorBuffer, *ConfigDescriptorBufferLength, ConfigDescriptorBufferLength); + + // + // complete the request + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_RemoveUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle, + ULONG Flags) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + NTSTATUS Status; + + DPRINT1("USBHI_RemoveUsbDevice\n"); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_RemoveUsbDevice invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // check if there were flags passed + // + if (Flags & USBD_KEEP_DEVICE_DATA || Flags & USBD_MARK_DEVICE_BUSY) + { + // + // ignore flags for now + // + return STATUS_SUCCESS; + } + + // + // remove device + // + Status = Controller->RemoveUsbDevice(UsbDevice); + if (!NT_SUCCESS(Status)) + { + // + // invalid device handle + // + DPRINT1("USBHI_RemoveUsbDevice Invalid device handle %p\n", UsbDevice); + PC_ASSERT(0); + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // release usb device + // + UsbDevice->Release(); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_RestoreUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE OldDeviceHandle, + PUSB_DEVICE_HANDLE NewDeviceHandle) +{ + PUSBDEVICE OldUsbDevice, NewUsbDevice; + CHubController * Controller; + + DPRINT1("USBHI_RestoreUsbDevice\n"); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + OldUsbDevice = (PUSBDEVICE)OldDeviceHandle; + NewUsbDevice = (PUSBDEVICE)NewDeviceHandle; + PC_ASSERT(OldUsbDevice); + PC_ASSERT(NewDeviceHandle); + + // + // validate device handle + // + PC_ASSERT(Controller->ValidateUsbDevice(NewUsbDevice)); + PC_ASSERT(Controller->ValidateUsbDevice(OldUsbDevice)); + + DPRINT1("NewUsbDevice: DeviceAddress %x\n", NewUsbDevice->GetDeviceAddress()); + DPRINT1("OldUsbDevice: DeviceAddress %x\n", OldUsbDevice->GetDeviceAddress()); + + // + // remove old device handle + // + USBHI_RemoveUsbDevice(BusContext, OldDeviceHandle, 0); + + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_QueryDeviceInformation( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle, + PVOID DeviceInformationBuffer, + ULONG DeviceInformationBufferLength, + PULONG LengthReturned) +{ + PUSB_DEVICE_INFORMATION_0 DeviceInfo; + PUSBDEVICE UsbDevice; + CHubController * Controller; + + DPRINT1("USBHI_QueryDeviceInformation %p\n", BusContext); + + // + // sanity check + // + PC_ASSERT(DeviceInformationBufferLength >= sizeof(USB_DEVICE_INFORMATION_0)); + PC_ASSERT(DeviceInformationBuffer); + PC_ASSERT(LengthReturned); + + // + // get controller object + // + Controller = (CHubController*)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + if (BusContext != DeviceHandle) + { + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_QueryDeviceInformation invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // access information buffer + // + DeviceInfo = (PUSB_DEVICE_INFORMATION_0)DeviceInformationBuffer; + + // + // initialize with default values + // + DeviceInfo->InformationLevel = 0; + DeviceInfo->ActualLength = sizeof(USB_DEVICE_INFORMATION_0); + DeviceInfo->PortNumber = UsbDevice->GetPort(); + DeviceInfo->CurrentConfigurationValue = UsbDevice->GetConfigurationValue(); + DeviceInfo->DeviceAddress = UsbDevice->GetDeviceAddress(); + DeviceInfo->HubAddress = 0; //FIXME + DeviceInfo->DeviceSpeed = UsbDevice->GetSpeed(); + DeviceInfo->DeviceType = UsbDevice->GetType(); + DeviceInfo->NumberOfOpenPipes = 0; //FIXME + + // + // get device descriptor + // + UsbDevice->GetDeviceDescriptor(&DeviceInfo->DeviceDescriptor); + + // + // FIXME return pipe information + // + + // + // store result length + // + *LengthReturned = sizeof(USB_DEVICE_INFORMATION_0); + + return STATUS_SUCCESS; + } + + // + // access information buffer + // + DeviceInfo = (PUSB_DEVICE_INFORMATION_0)DeviceInformationBuffer; + + // + // initialize with default values + // + DeviceInfo->InformationLevel = 0; + DeviceInfo->ActualLength = sizeof(USB_DEVICE_INFORMATION_0); + DeviceInfo->PortNumber = 0; + DeviceInfo->CurrentConfigurationValue = 0; //FIXME; + DeviceInfo->DeviceAddress = 0; + DeviceInfo->HubAddress = 0; //FIXME + DeviceInfo->DeviceSpeed = UsbHighSpeed; //FIXME + DeviceInfo->DeviceType = Usb20Device; //FIXME + DeviceInfo->NumberOfOpenPipes = 0; //FIXME + + // + // get device descriptor + // + RtlMoveMemory(&DeviceInfo->DeviceDescriptor, ROOTHUB2_DEVICE_DESCRIPTOR, sizeof(USB_DEVICE_DESCRIPTOR)); + + // + // FIXME return pipe information + // + + // + // store result length + // +#ifdef _MSC_VER + *LengthReturned = FIELD_OFFSET(USB_DEVICE_INFORMATION_0, PipeList[DeviceInfo->NumberOfOpenPipes]); +#else + *LengthReturned = sizeof(USB_DEVICE_INFORMATION_0) + (DeviceInfo->NumberOfOpenPipes > 1 ? (DeviceInfo->NumberOfOpenPipes - 1) * sizeof(USB_PIPE_INFORMATION_0) : 0); +#endif + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetControllerInformation( + PVOID BusContext, + PVOID ControllerInformationBuffer, + ULONG ControllerInformationBufferLength, + PULONG LengthReturned) +{ + PUSB_CONTROLLER_INFORMATION_0 ControllerInfo; + + DPRINT1("USBHI_GetControllerInformation\n"); + + // + // sanity checks + // + PC_ASSERT(ControllerInformationBuffer); + PC_ASSERT(ControllerInformationBufferLength >= sizeof(USB_CONTROLLER_INFORMATION_0)); + + // + // get controller info buffer + // + ControllerInfo = (PUSB_CONTROLLER_INFORMATION_0)ControllerInformationBuffer; + + // + // FIXME only version 0 is supported for now + // + PC_ASSERT(ControllerInfo->InformationLevel == 0); + + // + // fill in information + // + ControllerInfo->ActualLength = sizeof(USB_CONTROLLER_INFORMATION_0); + ControllerInfo->SelectiveSuspendEnabled = FALSE; //FIXME + ControllerInfo->IsHighSpeedController = TRUE; + + // + // set length returned + // + *LengthReturned = ControllerInfo->ActualLength; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_ControllerSelectiveSuspend( + PVOID BusContext, + BOOLEAN Enable) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetExtendedHubInformation( + PVOID BusContext, + PDEVICE_OBJECT HubPhysicalDeviceObject, + PVOID HubInformationBuffer, + ULONG HubInformationBufferLength, + PULONG LengthReturned) +{ + PUSB_EXTHUB_INFORMATION_0 HubInfo; + CHubController * Controller; + PUSBHARDWAREDEVICE Hardware; + ULONG Index; + ULONG NumPort, Dummy2; + USHORT Dummy1; + NTSTATUS Status; + + DPRINT1("USBHI_GetExtendedHubInformation\n"); + + // + // sanity checks + // + PC_ASSERT(HubInformationBuffer); + PC_ASSERT(HubInformationBufferLength == sizeof(USB_EXTHUB_INFORMATION_0)); + PC_ASSERT(LengthReturned); + + // + // get hub controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get usb hardware device + // + Hardware = Controller->GetUsbHardware(); + + // + // retrieve number of ports + // + Status = Hardware->GetDeviceDetails(&Dummy1, &Dummy1, &NumPort, &Dummy2); + if (!NT_SUCCESS(Status)) + { + // + // failed to get hardware details, ouch ;) + // + DPRINT1("USBHI_GetExtendedHubInformation failed to get hardware details with %x\n", Status); + return Status; + } + + // + // get hub information buffer + // + HubInfo = (PUSB_EXTHUB_INFORMATION_0)HubInformationBuffer; + + // + // initialize hub information + // + HubInfo->InformationLevel = 0; + + // + // store port count + // + HubInfo->NumberOfPorts = NumPort; + + // + // initialize port information + // + for(Index = 0; Index < NumPort; Index++) + { + HubInfo->Port[Index].PhysicalPortNumber = Index + 1; + HubInfo->Port[Index].PortLabelNumber = Index + 1; + HubInfo->Port[Index].VidOverride = 0; + HubInfo->Port[Index].PidOverride = 0; + HubInfo->Port[Index].PortAttributes = USB_PORTATTR_SHARED_USB2; //FIXME + } + + // + // store result length + // +#ifdef _MSC_VER + *LengthReturned = FIELD_OFFSET(USB_EXTHUB_INFORMATION_0, Port[HubInfo->NumberOfPorts]); +#else + *LengthReturned = FIELD_OFFSET(USB_EXTHUB_INFORMATION_0, Port) + sizeof(USB_EXTPORT_INFORMATION_0) * HubInfo->NumberOfPorts; +#endif + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetRootHubSymbolicName( + PVOID BusContext, + PVOID HubSymNameBuffer, + ULONG HubSymNameBufferLength, + PULONG HubSymNameActualLength) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +PVOID +USB_BUSIFFN +USBHI_GetDeviceBusContext( + PVOID HubBusContext, + PVOID DeviceHandle) +{ + UNIMPLEMENTED + return NULL; +} + +NTSTATUS +USB_BUSIFFN +USBHI_Initialize20Hub( + PVOID BusContext, + PUSB_DEVICE_HANDLE HubDeviceHandle, + ULONG TtCount) +{ + DPRINT("USBHI_Initialize20Hub HubDeviceHandle %p UNIMPLEMENTED TtCount %lu\n", HubDeviceHandle, TtCount); + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_RootHubInitNotification( + PVOID BusContext, + PVOID CallbackContext, + PRH_INIT_CALLBACK CallbackRoutine) +{ + CHubController * Controller; + + DPRINT("USBHI_RootHubInitNotification %p \n", CallbackContext); + + // + // get controller object + // + Controller = (CHubController*)BusContext; + PC_ASSERT(Controller); + + // + // set notification routine + // + Controller->SetNotification(CallbackContext, CallbackRoutine); + + // + // FIXME: determine when to perform callback + // + CallbackRoutine(CallbackContext); + + // + // done + // + return STATUS_SUCCESS; +} + +VOID +USB_BUSIFFN +USBHI_FlushTransfers( + PVOID BusContext, + PVOID DeviceHandle) +{ + UNIMPLEMENTED +} + +VOID +USB_BUSIFFN +USBHI_SetDeviceHandleData( + PVOID BusContext, + PVOID DeviceHandle, + PDEVICE_OBJECT UsbDevicePdo) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + + // + // get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device handle + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_SetDeviceHandleData DeviceHandle %p is invalid\n", DeviceHandle); + + // + // invalid handle + // + return; + } + else + { + // + // usbhub sends this request as a part of the Pnp startup sequence + // looks like we need apply a dragon voodoo to fixup the device stack + // otherwise usbhub will cause a bugcheck + // + DPRINT1("USBHI_SetDeviceHandleData %p\n", UsbDevicePdo); + + // + // sanity check + // + PC_ASSERT(UsbDevicePdo->AttachedDevice); + + // + // should be usbstor + // fixup device stack voodoo part #2 + // + UsbDevicePdo->AttachedDevice->StackSize++; + + // + // set device handle data + // + UsbDevice->SetDeviceHandleData(UsbDevicePdo); + } +} + +//================================================================================================= +// +// USB Device Interface functions +// + +VOID +USB_BUSIFFN +USBDI_GetUSBDIVersion( + PVOID BusContext, + PUSBD_VERSION_INFORMATION VersionInformation, + PULONG HcdCapabilites) +{ + CHubController * Controller; + PUSBHARDWAREDEVICE Device; + ULONG Speed, Dummy2; + USHORT Dummy1; + + DPRINT1("USBDI_GetUSBDIVersion\n"); + + // + // get controller + // + Controller = (CHubController*)BusContext; + + // + // get usb hardware + // + Device = Controller->GetUsbHardware(); + PC_ASSERT(Device); + + if (VersionInformation) + { + // + // windows xp supported + // + VersionInformation->USBDI_Version = 0x00000500; + + // + // get device speed + // + Device->GetDeviceDetails(&Dummy1, &Dummy1, &Dummy2, &Speed); + + // + // store speed details + // + VersionInformation->Supported_USB_Version = Speed; + } + + // + // no flags supported + // + *HcdCapabilites = 0; +} + +NTSTATUS +USB_BUSIFFN +USBDI_QueryBusTime( + PVOID BusContext, + PULONG CurrentFrame) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +USB_BUSIFFN +USBDI_SubmitIsoOutUrb( + PVOID BusContext, + PURB Urb) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +USB_BUSIFFN +USBDI_QueryBusInformation( + PVOID BusContext, + ULONG Level, + PVOID BusInformationBuffer, + PULONG BusInformationBufferLength, + PULONG BusInformationActualLength) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +BOOLEAN +USB_BUSIFFN +USBDI_IsDeviceHighSpeed( + PVOID BusContext) +{ + CHubController * Controller; + PUSBHARDWAREDEVICE Device; + ULONG Speed, Dummy2; + USHORT Dummy1; + + DPRINT1("USBDI_IsDeviceHighSpeed\n"); + + // + // get controller + // + Controller = (CHubController*)BusContext; + + // + // get usb hardware + // + Device = Controller->GetUsbHardware(); + PC_ASSERT(Device); + + // + // get device speed + // + Device->GetDeviceDetails(&Dummy1, &Dummy1, &Dummy2, &Speed); + + // + // USB 2.0 equals 0x200 + // + return (Speed == 0x200); +} + +NTSTATUS +USB_BUSIFFN +USBDI_EnumLogEntry( + PVOID BusContext, + ULONG DriverTag, + ULONG EnumTag, + ULONG P1, + ULONG P2) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CHubController::HandleQueryInterface( + PIO_STACK_LOCATION IoStack) +{ + PUSB_BUS_INTERFACE_HUB_V5 InterfaceHub; + PUSB_BUS_INTERFACE_USBDI_V2 InterfaceDI; + UNICODE_STRING GuidBuffer; + NTSTATUS Status; + + if (IsEqualGUIDAligned(*IoStack->Parameters.QueryInterface.InterfaceType, USB_BUS_INTERFACE_HUB_GUID)) + { + // + // get request parameters + // + InterfaceHub = (PUSB_BUS_INTERFACE_HUB_V5)IoStack->Parameters.QueryInterface.Interface; + InterfaceHub->Version = IoStack->Parameters.QueryInterface.Version; + + // + // check version + // + if (IoStack->Parameters.QueryInterface.Version >= 6) + { + DPRINT1("USB_BUS_INTERFACE_HUB_GUID version %x not supported!\n", IoStack->Parameters.QueryInterface.Version); + + // + // version not supported + // + return STATUS_NOT_SUPPORTED; + } + + // + // Interface version 0 + // + if (IoStack->Parameters.QueryInterface.Version >= 0) + { + InterfaceHub->Size = IoStack->Parameters.QueryInterface.Size; + InterfaceHub->BusContext = PVOID(this); + InterfaceHub->InterfaceReference = USBI_InterfaceReference; + InterfaceHub->InterfaceDereference = USBI_InterfaceDereference; + } + + // + // Interface version 1 + // + if (IoStack->Parameters.QueryInterface.Version >= 1) + { + InterfaceHub->CreateUsbDevice = USBHI_CreateUsbDevice; + InterfaceHub->InitializeUsbDevice = USBHI_InitializeUsbDevice; + InterfaceHub->GetUsbDescriptors = USBHI_GetUsbDescriptors; + InterfaceHub->RemoveUsbDevice = USBHI_RemoveUsbDevice; + InterfaceHub->RestoreUsbDevice = USBHI_RestoreUsbDevice; + InterfaceHub->QueryDeviceInformation = USBHI_QueryDeviceInformation; + } + + // + // Interface version 2 + // + if (IoStack->Parameters.QueryInterface.Version >= 2) + { + InterfaceHub->GetControllerInformation = USBHI_GetControllerInformation; + InterfaceHub->ControllerSelectiveSuspend = USBHI_ControllerSelectiveSuspend; + InterfaceHub->GetExtendedHubInformation = USBHI_GetExtendedHubInformation; + InterfaceHub->GetRootHubSymbolicName = USBHI_GetRootHubSymbolicName; + InterfaceHub->GetDeviceBusContext = USBHI_GetDeviceBusContext; + InterfaceHub->Initialize20Hub = USBHI_Initialize20Hub; + + } + + // + // Interface version 3 + // + if (IoStack->Parameters.QueryInterface.Version >= 3) + { + InterfaceHub->RootHubInitNotification = USBHI_RootHubInitNotification; + } + + // + // Interface version 4 + // + if (IoStack->Parameters.QueryInterface.Version >= 4) + { + InterfaceHub->FlushTransfers = USBHI_FlushTransfers; + } + + // + // Interface version 5 + // + if (IoStack->Parameters.QueryInterface.Version >= 5) + { + InterfaceHub->SetDeviceHandleData = USBHI_SetDeviceHandleData; + } + + // + // request completed + // + return STATUS_SUCCESS; + } + else if (IsEqualGUIDAligned(*IoStack->Parameters.QueryInterface.InterfaceType, USB_BUS_INTERFACE_USBDI_GUID)) + { + // + // get request parameters + // + InterfaceDI = (PUSB_BUS_INTERFACE_USBDI_V2) IoStack->Parameters.QueryInterface.Interface; + InterfaceDI->Version = IoStack->Parameters.QueryInterface.Version; + + // + // check version + // + if (IoStack->Parameters.QueryInterface.Version >= 3) + { + DPRINT1("USB_BUS_INTERFACE_USBDI_GUID version %x not supported!\n", IoStack->Parameters.QueryInterface.Version); + + // + // version not supported + // + return STATUS_NOT_SUPPORTED; + } + + // + // interface version 0 + // + if (IoStack->Parameters.QueryInterface.Version >= 0) + { + InterfaceDI->Size = IoStack->Parameters.QueryInterface.Size; + InterfaceDI->BusContext = PVOID(this); + InterfaceDI->InterfaceReference = USBI_InterfaceReference; + InterfaceDI->InterfaceDereference = USBI_InterfaceDereference; + InterfaceDI->GetUSBDIVersion = USBDI_GetUSBDIVersion; + InterfaceDI->QueryBusTime = USBDI_QueryBusTime; + InterfaceDI->SubmitIsoOutUrb = USBDI_SubmitIsoOutUrb; + InterfaceDI->QueryBusInformation = USBDI_QueryBusInformation; + } + + // + // interface version 1 + // + if (IoStack->Parameters.QueryInterface.Version >= 1) + { + InterfaceDI->IsDeviceHighSpeed = USBDI_IsDeviceHighSpeed; + } + + // + // interface version 2 + // + if (IoStack->Parameters.QueryInterface.Version >= 2) + { + InterfaceDI->EnumLogEntry = USBDI_EnumLogEntry; + } + + // + // request completed + // + return STATUS_SUCCESS; + } + else + { + // + // convert guid to string + // + Status = RtlStringFromGUID(*IoStack->Parameters.QueryInterface.InterfaceType, &GuidBuffer); + if (NT_SUCCESS(Status)) + { + // + // print interface + // + DPRINT1("HandleQueryInterface UNKNOWN INTERFACE GUID: %wZ Version %x\n", &GuidBuffer, IoStack->Parameters.QueryInterface.Version); + + // + // free guid buffer + // + RtlFreeUnicodeString(&GuidBuffer); + } + } + return STATUS_NOT_SUPPORTED; +} + +NTSTATUS +CHubController::SetDeviceInterface( + BOOLEAN Enable) +{ + NTSTATUS Status = STATUS_SUCCESS; + + if (Enable) + { + // + // register device interface + // + Status = IoRegisterDeviceInterface(m_HubControllerDeviceObject, &GUID_DEVINTERFACE_USB_HUB, 0, &m_HubDeviceInterfaceString); + + if (NT_SUCCESS(Status)) + { + // + // now enable the device interface + // + Status = IoSetDeviceInterfaceState(&m_HubDeviceInterfaceString, TRUE); + + // + // enable interface + // + m_InterfaceEnabled = TRUE; + } + } + else if (m_InterfaceEnabled) + { + // + // disable device interface + // + Status = IoSetDeviceInterfaceState(&m_HubDeviceInterfaceString, FALSE); + + if (NT_SUCCESS(Status)) + { + // + // now delete interface string + // + RtlFreeUnicodeString(&m_HubDeviceInterfaceString); + } + + // + // disable interface + // + m_InterfaceEnabled = FALSE; + } + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CHubController::CreatePDO( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT * OutDeviceObject) +{ + WCHAR CharDeviceName[64]; + NTSTATUS Status; + ULONG UsbDeviceNumber = 0; + UNICODE_STRING DeviceName; + + while (TRUE) + { + // + // construct device name + // + swprintf(CharDeviceName, L"\\Device\\USBPDO-%d", UsbDeviceNumber); + + // + // initialize device name + // + RtlInitUnicodeString(&DeviceName, CharDeviceName); + + // + // create device + // + Status = IoCreateDevice(DriverObject, + sizeof(COMMON_DEVICE_EXTENSION), + &DeviceName, + FILE_DEVICE_CONTROLLER, + 0, + FALSE, + OutDeviceObject); + + /* check for success */ + if (NT_SUCCESS(Status)) + break; + + // + // is there a device object with that same name + // + if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION)) + { + // + // Try the next name + // + UsbDeviceNumber++; + continue; + } + + // + // bail out on other errors + // + if (!NT_SUCCESS(Status)) + { + DPRINT1("CreatePDO: Failed to create %wZ, Status %x\n", &DeviceName, Status); + return Status; + } + } + + DPRINT1("CHubController::CreatePDO: DeviceName %wZ\n", &DeviceName); + + // + // fixup device stack voodoo part #1 + // + (*OutDeviceObject)->StackSize++; + + /* done */ + return Status; +} + + + +NTSTATUS +CreateHubController( + PHUBCONTROLLER *OutHcdController) +{ + PHUBCONTROLLER This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBEHCI) CHubController(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutHcdController = (PHUBCONTROLLER)This; + + // + // done + // + return STATUS_SUCCESS; +} + +VOID StatusChangeEndpointCallBack(PVOID Context) +{ + CHubController* This; + PIRP Irp; + This = (CHubController*)Context; + + ASSERT(This); + + Irp = This->m_PendingSCEIrp; + if (!Irp) + { + DPRINT1("There was no pending IRP for SCE. Did the usb hub 2.0 driver (usbhub2) load?\n"); + return; + } + + This->m_PendingSCEIrp = NULL; + This->QueryStatusChageEndpoint(Irp); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); +} diff --git a/reactos/drivers/usb/usbehci/interfaces.h b/reactos/drivers/usb/usbehci/interfaces.h new file mode 100644 index 00000000000..8527948903d --- /dev/null +++ b/reactos/drivers/usb/usbehci/interfaces.h @@ -0,0 +1,867 @@ + +#ifndef INTERFACES_HPP +#define INTERFACES_HPP + +//--------------------------------------------------------------------------- +// +// Object Hierachy +// -------------------------------------------------------------------- +// | IRootHCDController | +// | IHCDController Intel USB Universal Host Controller - 3A37 | +// | IHCDController - Intel USB Universal HostController - 3A38 | +// | IHCDController - Intel USB Universal HostController - 3A38 | +// |------------------------------------------------------------------| +// +// +// IHCDController Intel USB Universal Host Controller - 3A37 +// IHubController +// IUSBHardwareDevice +// IDMAMemoryManager +// IUSBQueue <- interacts with -> IUSBRequest +// +// +// Each IHCDController creates an IUSBHardwareDevice class upon initialization. The +// IUSBHardwardeDevice class is used to abstract usb controller specifics. The IHubController +// manages all attached devices and handles hub control ioctl requests. +// +// Each IUSBHardwareDevice has one IDMAMemoryManager and one IUSBQueue. The IDMAMemoryManager +// is used to handle dma memory allocations. The IUSBQueue manages requests which are send to the +// usb hardware. See IUSBRequest class for details. +// + + +//========================================================================================= +// +// class IRootHCDController +// +// Description: This class serves as the root host controller. The host controller mantains +// a list of registered controllers and provides support functions for the host controllers + +struct IHCDController; + +DECLARE_INTERFACE_(IRootHCDController, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: This function initializes the root host controller. It allocates the resources +// required to manage the registered controllers + + virtual NTSTATUS Initialize() = 0; + +//----------------------------------------------------------------------------------------- +// +// RegisterHCD +// +// Description: this function registers a host controller with the root host controller + + virtual NTSTATUS RegisterHCD(struct IHCDController * Controller) = 0; + +//----------------------------------------------------------------------------------------- +// +// UnregisterHCD +// +// Description: this function unregistes a host controller + + virtual NTSTATUS UnregisterHCD(struct IHCDController * Controller) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetControllerCount +// +// Description: returns the number of host controllers registered + + virtual ULONG GetControllerCount() = 0; + +}; + +typedef IRootHCDController *PROOTHDCCONTROLLER; + +//========================================================================================= +// +// class IHCDController +// +// Description: This class is used to manage a single USB host controller +// + +DECLARE_INTERFACE_(IHCDController, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: This function initializes the IHCDController implementation. +// It creates an IUSBHardwareDevice object and initializes it. It also registeres itself with +// the IRootHCDController +// + virtual NTSTATUS Initialize(IN PROOTHDCCONTROLLER RootHCDController, + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) = 0; + +}; + +typedef IHCDController *PHCDCONTROLLER; + + +//========================================================================================= +// +// class IUSBHardwareDevice +// +// Description: This class provides access to the usb hardware controller +// + +struct IDMAMemoryManager; +struct IUSBQueue; + +DECLARE_INTERFACE_(IUSBHardwareDevice, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: Initializes the usb device controller + + virtual NTSTATUS Initialize(PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT FunctionalDeviceObject, + PDEVICE_OBJECT PhysicalDeviceObject, + PDEVICE_OBJECT LowerDeviceObject) = 0; + +//----------------------------------------------------------------------------------------- +// +// PnpStart +// +// Description: handles pnp start request from device. It registeres the interrupt, +// sets up the ports and prepares the device. It then starts the controller + + virtual NTSTATUS PnpStart(PCM_RESOURCE_LIST RawResources, + PCM_RESOURCE_LIST TranslatedResources) = 0; + +//----------------------------------------------------------------------------------------- +// +// PnpStop +// +// Description: handles pnp stop request from device. It unregisteres the interrupt, releases ports and dma object. + + virtual NTSTATUS PnpStop(void) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetDeviceDetails +// +// Description: returns the device details such as vendor id, device id, number of ports and speed + + virtual NTSTATUS GetDeviceDetails(OUT OPTIONAL PUSHORT VendorId, + OUT OPTIONAL PUSHORT DeviceId, + OUT OPTIONAL PULONG NumberOfPorts, + OUT OPTIONAL PULONG Speed) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetUSBQueue +// +// Description: returns interface to internal IUSBQueue +// Interface is reference counted, you need to call release method when you are done with it +// Do not call Initialize on IUSBQueue, the object is already initialized + + virtual NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetDMA +// +// Description: returns the DMA object which can be used to allocate memory from the common buffer + + virtual NTSTATUS GetDMA(OUT struct IDMAMemoryManager **OutDMAMemoryManager) = 0; + + +//----------------------------------------------------------------------------------------- +// +// ResetController() +// +// Description: this function resets the controller +// Returns STATUS_SUCCESS when the controller was successfully reset + + virtual NTSTATUS ResetController() = 0; + +//----------------------------------------------------------------------------------------- +// +// StartController +// +// Description: this functions starts controller allowing interrupts for device connects/removal, and execution of +// Periodic and Asynchronous Schedules. +// + + virtual NTSTATUS StartController() = 0; + +//----------------------------------------------------------------------------------------- +// +// StopController +// +// Description: this functions stops controller disabling interrupts for device connects/removal, and execution of +// Periodic and Asynchronous Schedules. +// + + virtual NTSTATUS StopController() = 0; + +//----------------------------------------------------------------------------------------- +// +// ResetPort +// +// Description: this functions resets the port on the controller +// + + virtual NTSTATUS ResetPort(ULONG PortNumber) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetPortStatus +// +// Description: this functions return status and change state of port +// + virtual NTSTATUS GetPortStatus(ULONG PortId, OUT USHORT *PortStatus, OUT USHORT *PortChange) = 0; + +//----------------------------------------------------------------------------------------- +// +// ClearPortStatus +// +// Description: Clears Status of Port, for example Connection, Enable and Reset +// + virtual NTSTATUS ClearPortStatus(ULONG PortId, ULONG Status) = 0; + +//----------------------------------------------------------------------------------------- +// +// SetPortFeature +// +// Description: this functions Sets Feature on Port, for example Enable, Power and Reset +// + virtual NTSTATUS SetPortFeature(ULONG PortId, ULONG Feature) = 0; + +//----------------------------------------------------------------------------------------- +// +// SetAsyncListRegister +// +// Description: this functions sets the register to a address that is the physical address of a QueueHead. +// This is the location at which the controller will start executing the Asynchronous Schedule. +// +// FIXME: This is only available for USB 2.0 + virtual VOID SetAsyncListRegister(ULONG PhysicalAddress) = 0; + +//----------------------------------------------------------------------------------------- +// +// SetPeriodicListRegister +// +// Description: this functions sets the register to a address that is the physical address of a ???. +// This is the location at which the controller will start executing the Periodic Schedule. +// + virtual VOID SetPeriodicListRegister(ULONG PhysicalAddress) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetAsyncListRegister +// +// Description: Returns the memory address used in the Asynchronous Register +// + virtual struct _QUEUE_HEAD * GetAsyncListQueueHead() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetPeriodicListRegister +// +// Description: Returns the the memory address used in the Periodic Register +// + virtual ULONG GetPeriodicListRegister() = 0; + +//----------------------------------------------------------------------------------------- +// +// SetStatusChangeEndpointCallBack +// +// Description: Used to callback to the hub controller when SCE detected +// + virtual VOID SetStatusChangeEndpointCallBack(PVOID CallBack,PVOID Context) = 0; + +//----------------------------------------------------------------------------------------- +// +// AcquireDeviceLock +// +// Description: acquires the device lock + + virtual KIRQL AcquireDeviceLock(void) = 0; + +//----------------------------------------------------------------------------------------- +// +// ReleaseLock +// +// Description: releases the device lock + + virtual void ReleaseDeviceLock(KIRQL OldLevel) = 0; + + // set command + virtual void SetCommandRegister(struct _EHCI_USBCMD_CONTENT *UsbCmd) = 0; + + // get command + virtual void GetCommandRegister(struct _EHCI_USBCMD_CONTENT *UsbCmd) = 0; + + + +}; + +typedef IUSBHardwareDevice *PUSBHARDWAREDEVICE; + + +//========================================================================================= +// +// class IDMAMemoryManager +// +// Description: This class provides access to the dma buffer. It provides methods to +// allocate and free from the dma buffer +// + +DECLARE_INTERFACE_(IDMAMemoryManager, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: initializes the memory manager + + virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Device, + IN PKSPIN_LOCK Lock, + IN ULONG DmaBufferSize, + IN PVOID VirtualBase, + IN PHYSICAL_ADDRESS PhysicalAddress, + IN ULONG DefaultBlockSize) = 0; + +//----------------------------------------------------------------------------------------- +// +// Allocate +// +// Description: allocates block of memory from allocator + + virtual NTSTATUS Allocate(IN ULONG Size, + OUT PVOID *OutVirtualBase, + OUT PPHYSICAL_ADDRESS OutPhysicalAddress) = 0; + + +//----------------------------------------------------------------------------------------- +// +// Free +// +// Description: releases memory block + + virtual NTSTATUS Release(IN PVOID VirtualBase, + IN ULONG Size) = 0; + +}; + +typedef IDMAMemoryManager *PDMAMEMORYMANAGER; + + +//========================================================================================= +// +// class IUSBRequest +// +// Description: This class is used to issue request to usb controller. The class is +// initialized using InitializeXXX methods. You also need to call SetEndpoint to define the endpoint +// In addition you can call SetCompletionDetails if you need to wait for the end of +// the request or want to complete an irp. You call AddUSBRequest to add the request to the queue. +// Once the request is completed the CompletionCallback is invoked. The CompletionCallback +// will take care of any completion details which have been set. If the request is cancelled, the +// CancelCallback routine is invoked. +// + +struct _QUEUE_HEAD; +struct _USB_ENDPOINT; + +DECLARE_INTERFACE_(IUSBRequest, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// InitializeWithSetupPacket +// +// Description: initializes the request packet with an setup packet +// If there is a TransferBuffer, the TransferBufferLength contains the length of the buffer + + + virtual NTSTATUS InitializeWithSetupPacket(IN PDMAMEMORYMANAGER DmaManager, + IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, + IN UCHAR DeviceAddress, + IN OPTIONAL struct _USB_ENDPOINT *EndpointDescriptor, + IN OUT ULONG TransferBufferLength, + IN OUT PMDL TransferBuffer) = 0; + +//----------------------------------------------------------------------------------------- +// +// InitializeWithIrp +// +// Description: initializes the request with an IRP +// The irp contains an URB block which contains all necessary information + + virtual NTSTATUS InitializeWithIrp(IN PDMAMEMORYMANAGER DmaManager, + IN OUT PIRP Irp) = 0; + +//----------------------------------------------------------------------------------------- +// +// CompletionCallback +// +// Description: called when request has been completed. It is called when +// IUSBQueue completes a queue head + + virtual VOID CompletionCallback(IN NTSTATUS NtStatusCode, + IN ULONG UrbStatusCode, + IN struct _QUEUE_HEAD *QueueHead) = 0; + +//----------------------------------------------------------------------------------------- +// +// CancelCallback +// +// Description: called when the queue head is cancelled + + virtual VOID CancelCallback(IN NTSTATUS NtStatusCode, + IN struct _QUEUE_HEAD *QueueHead) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetQueueHead +// +// Description: returns an initialized queue head which contains all transfer descriptors + + virtual NTSTATUS GetQueueHead(struct _QUEUE_HEAD ** OutHead) = 0; + +//----------------------------------------------------------------------------------------- +// +// IsRequestComplete +// +// Description: returns true when the request has been completed +// Should be called after the CompletionCallback has been invoked +// This function is called by IUSBQueue after queue head has been completed +// If the function returns true, IUSBQueue will then call ShouldReleaseRequestAfterCompletion +// If that function returns also true, it calls Release() to delete the IUSBRequest + + virtual BOOLEAN IsRequestComplete() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetTransferType +// +// Description: returns the type of the request: control, bulk, iso, interrupt + + virtual ULONG GetTransferType() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetResultStatus +// +// Description: returns the status code of the result +// Note: this function will block the caller untill the request has been completed + + virtual VOID GetResultStatus(OUT OPTIONAL NTSTATUS * NtStatusCode, + OUT OPTIONAL PULONG UrbStatusCode) = 0; + +//----------------------------------------------------------------------------------------- +// +// IsRequestInitialized +// +// Description: returns true when the request has been successfully initialized using InitializeXXX methods + + virtual BOOLEAN IsRequestInitialized() = 0; + +//----------------------------------------------------------------------------------------- +// +// ShouldReleaseRequestAfterCompletion +// +// Description: this function gets called when the request returns +// IUSBQueue will then call Release() on the object to release all associated memory +// This function will typically return true when the request has been initialized with an irp +// If the request was initialized with an setup packet, it will return false + + virtual BOOLEAN ShouldReleaseRequestAfterCompletion() = 0; + +//---------------------------------------------------------------------------------------- +// +// FreeQueueHead +// +// Description: frees the queue head with the associated transfer descriptors + + virtual VOID FreeQueueHead(struct _QUEUE_HEAD * QueueHead) = 0; + +//--------------------------------------------------------------------------------------- +// +// GetTransferBuffer +// +// Description: this function returns the transfer buffer mdl and length +// Used by IUSBQueue for mapping buffer contents with DMA + + virtual VOID GetTransferBuffer(OUT PMDL * OutMDL, + OUT PULONG TransferLength) = 0; + +//-------------------------------------------------------------------------------------- +// +// IsQueueHeadComplete +// +// Description: returns true when the queue head which was passed as a parameter has been completed + + virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead) = 0; +}; + + +typedef IUSBRequest *PUSBREQUEST; + +//========================================================================================= +// +// class IUSBQueue +// +// Description: This class manages pending requests +// + +DECLARE_INTERFACE_(IUSBQueue, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: initializes the object + + virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Hardware, + IN PDMA_ADAPTER AdapterObject, + IN PDMAMEMORYMANAGER MemManager, + IN OPTIONAL PKSPIN_LOCK Lock) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetPendingRequestCount +// +// Description: returns the number of pending requests true from IsRequestComplete + + virtual ULONG GetPendingRequestCount() = 0; + +//----------------------------------------------------------------------------------------- +// +// AddUSBRequest +// +// Description: adds an usb request to the queue. +// Returns status success when successful + + virtual NTSTATUS AddUSBRequest(IUSBRequest * Request) = 0; + virtual NTSTATUS AddUSBRequest(PURB Urb) = 0; +//----------------------------------------------------------------------------------------- +// +// CancelRequests() +// +// Description: cancels all requests + + virtual NTSTATUS CancelRequests() = 0; + +//----------------------------------------------------------------------------------------- +// +// CreateUSBRequest +// +// Description: creates an usb request + + virtual NTSTATUS CreateUSBRequest(IUSBRequest **OutRequest) = 0; + +//-------------------------------------------------------------------------------------- +// +// InterruptCallback +// +// Description: callback when the periodic / asynchronous queue has been completed / queue head been completed + + virtual VOID InterruptCallback(IN NTSTATUS Status, OUT PULONG ShouldRingDoorBell) = 0; + +//-------------------------------------------------------------------------------------- +// +// CompleteAsyncRequests +// +// Description: once a request has been completed it is moved to pending queue. Since a queue head should only be freed +// after a door bell ring, this needs some synchronization. +// This function gets called by IUSBHardware after it the Interrupt on Async Advance bit has been set + + virtual VOID CompleteAsyncRequests() = 0; + +//----------------------------------------------------------------------------------------- +// +// AbortDevicePipe +// +// Description: aborts all pending requsts of an device + + virtual NTSTATUS AbortDevicePipe(UCHAR DeviceAddress, IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor) = 0; + +}; + +typedef IUSBQueue *PUSBQUEUE; + +//========================================================================================= +// +// class IHubController +// +// Description: This class implements a hub controller +// + +DECLARE_INTERFACE_(IHubController, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//---------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: Initializes the hub controller + + virtual NTSTATUS Initialize(IN PDRIVER_OBJECT DriverObject, + IN PHCDCONTROLLER Controller, + IN PUSBHARDWAREDEVICE Device, + IN BOOLEAN IsRootHubDevice, + IN ULONG DeviceAddress) = 0; + +//---------------------------------------------------------------------------------------- +// +// GetHubControllerDeviceObject +// +// Description: Returns the hub controller device object + + virtual NTSTATUS GetHubControllerDeviceObject(PDEVICE_OBJECT * HubDeviceObject) = 0; + +//---------------------------------------------------------------------------------------- +// +// GetHubControllerSymbolicLink +// +// Description: Returns the symbolic link of the root hub + + virtual NTSTATUS GetHubControllerSymbolicLink(ULONG BufferLength, PVOID Buffer, PULONG RequiredLength) = 0; + + +}; + +typedef IHubController *PHUBCONTROLLER; + +//========================================================================================= +// +// class IDispatchIrp +// +// Description: This class is used to handle irp dispatch requests +// + +DECLARE_INTERFACE_(IDispatchIrp, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// HandlePnp +// +// Description: This function handles all pnp requests + + virtual NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) = 0; + +//----------------------------------------------------------------------------------------- +// +// HandlePower +// +// Description: This function handles all power pnp requests +// + virtual NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) = 0; + +//----------------------------------------------------------------------------------------- +// +// HandleDeviceControl +// +// Description: handles device io control requests + + virtual NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) = 0; +}; + +typedef IDispatchIrp *PDISPATCHIRP; + +//========================================================================================= +// +// class IUSBDevice +// +// Description: This class is used to abstract details of a usb device +// + +DECLARE_INTERFACE_(IUSBDevice, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//---------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: Initializes the usb device + + virtual NTSTATUS Initialize(IN PHUBCONTROLLER HubController, + IN PUSBHARDWAREDEVICE Device, + IN PVOID Parent, + IN ULONG Port, + IN ULONG PortStatus) = 0; + +//----------------------------------------------------------------------------------------- +// +// IsHub +// +// Description: returns true when device is a hub + + virtual BOOLEAN IsHub() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetParent +// +// Description: gets the parent device of the this device + + virtual NTSTATUS GetParent(PVOID * Parent) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetDeviceAddress +// +// Description: gets the device address of the this device + + virtual UCHAR GetDeviceAddress() = 0; + + +//----------------------------------------------------------------------------------------- +// +// GetPort +// +// Description: gets the port to which this device is connected + + virtual ULONG GetPort() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetSpeed +// +// Description: gets the speed of the device + + virtual USB_DEVICE_SPEED GetSpeed() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetType +// +// Description: gets the type of the device, either 1.1 or 2.0 device + + virtual USB_DEVICE_TYPE GetType() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetState +// +// Description: gets the device state + + virtual ULONG GetState() = 0; + +//----------------------------------------------------------------------------------------- +// +// SetDeviceHandleData +// +// Description: sets device handle data + + virtual void SetDeviceHandleData(PVOID Data) = 0; + +//----------------------------------------------------------------------------------------- +// +// SetDeviceAddress +// +// Description: sets device handle data + + virtual NTSTATUS SetDeviceAddress(UCHAR DeviceAddress) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetDeviceDescriptor +// +// Description: sets device handle data + + virtual void GetDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetConfigurationValue +// +// Description: gets current selected configuration index + + virtual UCHAR GetConfigurationValue() = 0; + +//----------------------------------------------------------------------------------------- +// +// SubmitIrp +// +// Description: submits an irp containing an urb + + virtual NTSTATUS SubmitIrp(PIRP Irp) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetConfigurationDescriptors +// +// Description: returns one or more configuration descriptors + + virtual VOID GetConfigurationDescriptors(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptorBuffer, + IN ULONG BufferLength, + OUT PULONG OutBufferLength) = 0; + +//----------------------------------------------------------------------------------------- +// +// Description: returns length of configuration descriptors +// + virtual ULONG GetConfigurationDescriptorsLength() = 0; + +//----------------------------------------------------------------------------------------- +// +// SubmitSetupPacket +// +// Description: submits an setup packet. The usb device will then create an usb request from it and submit it to the queue + + virtual NTSTATUS SubmitSetupPacket(IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, + IN OUT ULONG BufferLength, + OUT PVOID Buffer) = 0; + +//----------------------------------------------------------------------------------------- +// +// SelectConfiguration +// +// Description: selects a configuration + + virtual NTSTATUS SelectConfiguration(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + IN PUSBD_INTERFACE_INFORMATION Interface, + OUT USBD_CONFIGURATION_HANDLE *ConfigurationHandle) = 0; + +//----------------------------------------------------------------------------------------- +// +// SelectConfiguration +// +// Description: selects a interface of an configuration + + virtual NTSTATUS SelectInterface(IN USBD_CONFIGURATION_HANDLE ConfigurationHandle, + IN OUT PUSBD_INTERFACE_INFORMATION Interface) = 0; + +//----------------------------------------------------------------------------------------- +// +// AbortPipe +// +// Description: aborts all pending requsts + + virtual NTSTATUS AbortPipe(IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor) = 0; + +}; + +typedef IUSBDevice *PUSBDEVICE; + +#endif diff --git a/reactos/drivers/usb/usbehci/memory_manager.cpp b/reactos/drivers/usb/usbehci/memory_manager.cpp new file mode 100644 index 00000000000..4b60d968b4c --- /dev/null +++ b/reactos/drivers/usb/usbehci/memory_manager.cpp @@ -0,0 +1,369 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbehci/memory_manager.cpp + * PURPOSE: USB EHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbehci.h" + +class CDMAMemoryManager : public IDMAMemoryManager +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + // IDMAMemoryManager interface functions + virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Device, IN PKSPIN_LOCK Lock, IN ULONG DmaBufferSize, IN PVOID VirtualBase, IN PHYSICAL_ADDRESS PhysicalAddress, IN ULONG DefaultBlockSize); + virtual NTSTATUS Allocate(IN ULONG Size, OUT PVOID *OutVirtualBase, OUT PPHYSICAL_ADDRESS OutPhysicalAddress); + virtual NTSTATUS Release(IN PVOID VirtualBase, IN ULONG Size); + + // constructor / destructor + CDMAMemoryManager(IUnknown *OuterUnknown){} + virtual ~CDMAMemoryManager(){} + +protected: + LONG m_Ref; + PUSBHARDWAREDEVICE m_Device; + PKSPIN_LOCK m_Lock; + LONG m_DmaBufferSize; + PVOID m_VirtualBase; + PHYSICAL_ADDRESS m_PhysicalAddress; + ULONG m_BlockSize; + + PULONG m_BitmapBuffer; + RTL_BITMAP m_Bitmap; +}; + +//---------------------------------------------------------------------------------------- +NTSTATUS +STDMETHODCALLTYPE +CDMAMemoryManager::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS +CDMAMemoryManager::Initialize( + IN PUSBHARDWAREDEVICE Device, + IN PKSPIN_LOCK Lock, + IN ULONG DmaBufferSize, + IN PVOID VirtualBase, + IN PHYSICAL_ADDRESS PhysicalAddress, + IN ULONG DefaultBlockSize) +{ + ULONG BitmapLength; + + // + // sanity checks + // + PC_ASSERT(DmaBufferSize >= PAGE_SIZE); + PC_ASSERT(DmaBufferSize % PAGE_SIZE == 0); + PC_ASSERT(DefaultBlockSize == 32 || DefaultBlockSize == 64 || DefaultBlockSize == 128); + + // + // calculate bitmap length + // + BitmapLength = (DmaBufferSize / DefaultBlockSize) / 8; + + // + // allocate bitmap buffer + // + m_BitmapBuffer = (PULONG)ExAllocatePoolWithTag(NonPagedPool, BitmapLength, TAG_USBEHCI); + if (!m_BitmapBuffer) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize bitmap + // + RtlInitializeBitMap(&m_Bitmap, m_BitmapBuffer, BitmapLength * 8); + + // + // clear all bits + // + RtlClearAllBits(&m_Bitmap); + + // + // initialize rest of memory allocator + // + m_PhysicalAddress = PhysicalAddress; + m_VirtualBase = VirtualBase; + m_DmaBufferSize = DmaBufferSize; + m_BitmapBuffer = m_BitmapBuffer; + m_Lock = Lock; + m_BlockSize = DefaultBlockSize; + + /* done */ + return STATUS_SUCCESS; +} + +NTSTATUS +CDMAMemoryManager::Allocate( + IN ULONG Size, + OUT PVOID *OutVirtualAddress, + OUT PPHYSICAL_ADDRESS OutPhysicalAddress) +{ + ULONG Length, BlockCount, FreeIndex, StartPage, EndPage; + KIRQL OldLevel; + ULONG BlocksPerPage; + + // + // sanity checks + // + ASSERT(Size <= PAGE_SIZE); + //ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); + + // + // align request + // + Length = (Size + m_BlockSize -1) & ~(m_BlockSize -1); + + // + // sanity check + // + ASSERT(Length); + + // + // convert to block count + // + BlockCount = Length / m_BlockSize; + + // + // acquire lock + // + KeAcquireSpinLock(m_Lock, &OldLevel); + + // + // helper variable + // + BlocksPerPage = PAGE_SIZE / m_BlockSize; + + // + // start search + // + FreeIndex = 0; + do + { + // + // search for an free index + // + FreeIndex = RtlFindClearBits(&m_Bitmap, BlockCount, FreeIndex); + + // + // check if there was a block found + // + if (FreeIndex == MAXULONG) + { + // + // no free block found + // + break; + } + + // + // check that the allocation does not spawn over page boundaries + // + StartPage = (FreeIndex * m_BlockSize); + StartPage = (StartPage != 0 ? StartPage / PAGE_SIZE : 0); + EndPage = ((FreeIndex + BlockCount) * m_BlockSize) / PAGE_SIZE; + + // + // does the request start and end on the same page + // + if (StartPage == EndPage) + { + // + // reserve block + // + RtlSetBits(&m_Bitmap, FreeIndex, BlockCount); + + // + // reserve block + // + break; + } + else if ((BlockCount == BlocksPerPage) && (FreeIndex % BlocksPerPage == 0)) + { + // + // the request equals PAGE_SIZE and is aligned at page boundary + // reserve block + // + RtlSetBits(&m_Bitmap, FreeIndex, BlockCount); + + // + // reserve block + // + break; + } + else + { + // + // request spawned over page boundary + // restart search on next page + // + FreeIndex = (EndPage * PAGE_SIZE) / m_BlockSize; + } + } + while(TRUE); + + // + // release lock + // + KeReleaseSpinLock(m_Lock, OldLevel); + + // + // did allocation succeed + // + if (FreeIndex == MAXULONG) + { + // + // failed to allocate block, requestor must retry + // + return STATUS_UNSUCCESSFUL; + } + + // + // return result + // + *OutVirtualAddress = (PVOID)((ULONG_PTR)m_VirtualBase + FreeIndex * m_BlockSize); + OutPhysicalAddress->QuadPart = m_PhysicalAddress.QuadPart + FreeIndex * m_BlockSize; + + // + // clear block + // + RtlZeroMemory(*OutVirtualAddress, Length); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CDMAMemoryManager::Release( + IN PVOID VirtualAddress, + IN ULONG Size) +{ + KIRQL OldLevel; + ULONG BlockOffset = 0, BlockLength, BlockCount; + + // + // sanity checks + // + PC_ASSERT(VirtualAddress); + PC_ASSERT((ULONG_PTR)VirtualAddress >= (ULONG_PTR)m_VirtualBase); + PC_ASSERT((ULONG_PTR)m_VirtualBase + m_DmaBufferSize > (ULONG_PTR)m_VirtualBase); + + // + // calculate block length + // + BlockLength = ((ULONG_PTR)VirtualAddress - (ULONG_PTR)m_VirtualBase); + + // + // check if its the first block + // + if (BlockLength) + { + // + // divide by base block size + // + BlockOffset = BlockLength / m_BlockSize; + } + + // + // align length to block size + // + Size = (Size + m_BlockSize - 1) & ~(m_BlockSize - 1); + + // + // convert to blocks + // + BlockCount = Size / m_BlockSize; + ASSERT(BlockCount); + + // + // acquire lock + // + KeAcquireSpinLock(m_Lock, &OldLevel); + + // + // sanity check + // + ASSERT(RtlAreBitsSet(&m_Bitmap, BlockOffset, BlockCount)); + + // + // release buffer + // + RtlClearBits(&m_Bitmap, BlockOffset, BlockCount); + + // + // release lock + // + KeReleaseSpinLock(m_Lock, OldLevel); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CreateDMAMemoryManager( + PDMAMEMORYMANAGER *OutMemoryManager) +{ + CDMAMemoryManager* This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBEHCI) CDMAMemoryManager(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutMemoryManager = (PDMAMEMORYMANAGER)This; + + // + // done + // + return STATUS_SUCCESS; +} + diff --git a/reactos/drivers/usb/usbehci/misc.cpp b/reactos/drivers/usb/usbehci/misc.cpp new file mode 100644 index 00000000000..56c35cc806b --- /dev/null +++ b/reactos/drivers/usb/usbehci/misc.cpp @@ -0,0 +1,134 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbehci/misc.cpp + * PURPOSE: USB EHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbehci.h" + +// +// driver verifier +// +IO_COMPLETION_ROUTINE SyncForwardIrpCompletionRoutine; + +NTSTATUS +NTAPI +SyncForwardIrpCompletionRoutine( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context) +{ + if (Irp->PendingReturned) + { + KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE); + } + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +NTAPI +SyncForwardIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + KEVENT Event; + NTSTATUS Status; + + // + // initialize event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // copy irp stack location + // + IoCopyCurrentIrpStackLocationToNext(Irp); + + // + // set completion routine + // + IoSetCompletionRoutine(Irp, SyncForwardIrpCompletionRoutine, &Event, TRUE, TRUE, TRUE); + + + // + // call driver + // + Status = IoCallDriver(DeviceObject, Irp); + + + // + // check if pending + // + if (Status == STATUS_PENDING) + { + // + // wait for the request to finish + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + // + // copy status code + // + Status = Irp->IoStatus.Status; + } + + // + // done + // + return Status; +} + +NTSTATUS +NTAPI +GetBusInterface( + PDEVICE_OBJECT DeviceObject, + PBUS_INTERFACE_STANDARD busInterface) +{ + KEVENT Event; + NTSTATUS Status; + PIRP Irp; + IO_STATUS_BLOCK IoStatus; + PIO_STACK_LOCATION Stack; + + if ((!DeviceObject) || (!busInterface)) + return STATUS_UNSUCCESSFUL; + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, + DeviceObject, + NULL, + 0, + NULL, + &Event, + &IoStatus); + + if (Irp == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Stack=IoGetNextIrpStackLocation(Irp); + Stack->MajorFunction = IRP_MJ_PNP; + Stack->MinorFunction = IRP_MN_QUERY_INTERFACE; + Stack->Parameters.QueryInterface.Size = sizeof(BUS_INTERFACE_STANDARD); + Stack->Parameters.QueryInterface.InterfaceType = (LPGUID)&GUID_BUS_INTERFACE_STANDARD; + Stack->Parameters.QueryInterface.Version = 1; + Stack->Parameters.QueryInterface.Interface = (PINTERFACE)busInterface; + Stack->Parameters.QueryInterface.InterfaceSpecificData = NULL; + Irp->IoStatus.Status=STATUS_NOT_SUPPORTED ; + + Status=IoCallDriver(DeviceObject, Irp); + + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + Status=IoStatus.Status; + } + + return Status; +} + diff --git a/reactos/drivers/usb/usbehci/purecall.cpp b/reactos/drivers/usb/usbehci/purecall.cpp new file mode 100644 index 00000000000..c168b79e10e --- /dev/null +++ b/reactos/drivers/usb/usbehci/purecall.cpp @@ -0,0 +1,24 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbehci_new/purecall.cpp + * PURPOSE: USB EHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbehci.h" + + +extern "C" { + void + __cxa_pure_virtual() + { + // put error handling here + + DbgBreakPoint(); + + } +} + diff --git a/reactos/drivers/usb/usbehci/usb_device.cpp b/reactos/drivers/usb/usbehci/usb_device.cpp new file mode 100644 index 00000000000..b2db3cf5c58 --- /dev/null +++ b/reactos/drivers/usb/usbehci/usb_device.cpp @@ -0,0 +1,1344 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbehci/usb_device.cpp + * PURPOSE: USB EHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#define INITGUID +#include "usbehci.h" + +class CUSBDevice : public IUSBDevice +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + // IUSBDevice interface functions + virtual NTSTATUS Initialize(IN PHUBCONTROLLER HubController, IN PUSBHARDWAREDEVICE Device, IN PVOID Parent, IN ULONG Port, IN ULONG PortStatus); + virtual BOOLEAN IsHub(); + virtual NTSTATUS GetParent(PVOID * Parent); + virtual UCHAR GetDeviceAddress(); + virtual ULONG GetPort(); + virtual USB_DEVICE_SPEED GetSpeed(); + virtual USB_DEVICE_TYPE GetType(); + virtual ULONG GetState(); + virtual void SetDeviceHandleData(PVOID Data); + virtual NTSTATUS SetDeviceAddress(UCHAR DeviceAddress); + virtual void GetDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor); + virtual UCHAR GetConfigurationValue(); + virtual NTSTATUS SubmitIrp(PIRP Irp); + virtual VOID GetConfigurationDescriptors(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptorBuffer, IN ULONG BufferLength, OUT PULONG OutBufferLength); + virtual ULONG GetConfigurationDescriptorsLength(); + virtual NTSTATUS SubmitSetupPacket(IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, OUT ULONG BufferLength, OUT PVOID Buffer); + virtual NTSTATUS SelectConfiguration(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, IN PUSBD_INTERFACE_INFORMATION Interface, OUT USBD_CONFIGURATION_HANDLE *ConfigurationHandle); + virtual NTSTATUS SelectInterface(IN USBD_CONFIGURATION_HANDLE ConfigurationHandle, IN OUT PUSBD_INTERFACE_INFORMATION Interface); + virtual NTSTATUS AbortPipe(IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor); + + + // local function + virtual NTSTATUS CommitIrp(PIRP Irp); + virtual NTSTATUS CommitSetupPacket(PUSB_DEFAULT_PIPE_SETUP_PACKET Packet, IN OPTIONAL PUSB_ENDPOINT EndpointDescriptor, IN ULONG BufferLength, IN OUT PMDL Mdl); + virtual NTSTATUS CreateConfigurationDescriptor(UCHAR ConfigurationIndex); + virtual NTSTATUS CreateDeviceDescriptor(); + virtual VOID DumpDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor); + virtual VOID DumpConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor); + + // constructor / destructor + CUSBDevice(IUnknown *OuterUnknown){} + virtual ~CUSBDevice(){} + +protected: + LONG m_Ref; + PHUBCONTROLLER m_HubController; + PUSBHARDWAREDEVICE m_Device; + PVOID m_Parent; + ULONG m_Port; + UCHAR m_DeviceAddress; + PVOID m_Data; + UCHAR m_ConfigurationIndex; + KSPIN_LOCK m_Lock; + USB_DEVICE_DESCRIPTOR m_DeviceDescriptor; + ULONG m_PortStatus; + PUSBQUEUE m_Queue; + PDMAMEMORYMANAGER m_DmaManager; + + PUSB_CONFIGURATION m_ConfigurationDescriptors; +}; + +//---------------------------------------------------------------------------------------- +NTSTATUS +STDMETHODCALLTYPE +CUSBDevice::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::Initialize( + IN PHUBCONTROLLER HubController, + IN PUSBHARDWAREDEVICE Device, + IN PVOID Parent, + IN ULONG Port, + IN ULONG PortStatus) +{ + NTSTATUS Status; + + // + // initialize members + // + m_HubController = HubController; + m_Device = Device; + m_Parent = Parent; + m_Port = Port; + m_PortStatus = PortStatus; + + // + // initialize device lock + // + KeInitializeSpinLock(&m_Lock); + + // + // no device address has been set yet + // + m_DeviceAddress = 0; + + // + // get usb request queue + // + Status = m_Device->GetUSBQueue(&m_Queue); + if (!NT_SUCCESS(Status)) + { + // + // failed to get usb queue + // + DPRINT1("CUSBDevice::Initialize GetUsbQueue failed with %x\n", Status); + return Status; + } + + // + // get dma manager + // + Status = m_Device->GetDMA(&m_DmaManager); + if (!NT_SUCCESS(Status)) + { + // + // failed to get dma manager + // + DPRINT1("CUSBDevice::Initialize GetDMA failed with %x\n", Status); + return Status; + } + + // + // sanity check + // + PC_ASSERT(m_DmaManager); + + // + // get device descriptor + // + Status = CreateDeviceDescriptor(); + if (!NT_SUCCESS(Status)) + { + // + // failed to get device descriptor + // + DPRINT1("CUSBDevice::Initialize Failed to get device descriptor with %x\n", Status); + return Status; + } + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +BOOLEAN +CUSBDevice::IsHub() +{ + // + // USB Standard Device Class see http://www.usb.org/developers/defined_class/#BaseClass09h + // for details + // + return (m_DeviceDescriptor.bDeviceClass == 0x09 && m_DeviceDescriptor.bDeviceSubClass == 0x00); +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::GetParent( + PVOID * Parent) +{ + // + // returns parent + // + *Parent = m_Parent; + + // + // done + // + return STATUS_SUCCESS; +} + +//---------------------------------------------------------------------------------------- +UCHAR +CUSBDevice::GetDeviceAddress() +{ + // + // get device address + // + return m_DeviceAddress; +} + +//---------------------------------------------------------------------------------------- +ULONG +CUSBDevice::GetPort() +{ + // + // get port to which this device is connected to + // + return m_Port; +} + +//---------------------------------------------------------------------------------------- +USB_DEVICE_SPEED +CUSBDevice::GetSpeed() +{ + if (m_PortStatus & USB_PORT_STATUS_LOW_SPEED) + { + // + // low speed device + // + return UsbLowSpeed; + } + else if (m_PortStatus & USB_PORT_STATUS_HIGH_SPEED) + { + // + // high speed device + // + return UsbHighSpeed; + } + + // + // default to full speed + // + return UsbFullSpeed; +} + +//---------------------------------------------------------------------------------------- +USB_DEVICE_TYPE +CUSBDevice::GetType() +{ + // + // device is encoded into bcdUSB + // + if (m_DeviceDescriptor.bcdUSB == 0x110) + { + // + // USB 1.1 device + // + return Usb11Device; + } + else if (m_DeviceDescriptor.bcdUSB == 0x200) + { + // + // USB 2.0 device + // + return Usb20Device; + } + + DPRINT1("CUSBDevice::GetType Unknown bcdUSB Type %x\n", m_DeviceDescriptor.bcdUSB); + //PC_ASSERT(FALSE); + + return Usb11Device; +} + +//---------------------------------------------------------------------------------------- +ULONG +CUSBDevice::GetState() +{ + UNIMPLEMENTED + return FALSE; +} + +//---------------------------------------------------------------------------------------- +void +CUSBDevice::SetDeviceHandleData( + PVOID Data) +{ + // + // set device data, for debugging issues + // + m_Data = Data; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::SetDeviceAddress( + UCHAR DeviceAddress) +{ + PUSB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + UCHAR OldAddress; + UCHAR Index; + + DPRINT1("CUSBDevice::SetDeviceAddress Address %d\n", DeviceAddress); + + CtrlSetup = (PUSB_DEFAULT_PIPE_SETUP_PACKET)ExAllocatePoolWithTag(NonPagedPool, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET), TAG_USBEHCI); + if (!CtrlSetup) + return STATUS_INSUFFICIENT_RESOURCES; + + // + // zero request + // + RtlZeroMemory(CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + + // + // initialize request + // + CtrlSetup->bRequest = USB_REQUEST_SET_ADDRESS; + CtrlSetup->wValue.W = (USHORT)DeviceAddress; + + // + // set device address + // + Status = CommitSetupPacket(CtrlSetup, 0, 0, 0); + + // + // free setup packet + // + ExFreePoolWithTag(CtrlSetup, TAG_USBEHCI); + + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // failed to set device address + // + DPRINT1("CUSBDevice::SetDeviceAddress> failed to set device address with %x Address %x\n", Status, DeviceAddress); + return Status; + } + + // + // lets have a short nap + // + KeStallExecutionProcessor(300); + + // + // back up old address + // + OldAddress = m_DeviceAddress; + + // + // store new device address + // + m_DeviceAddress = DeviceAddress; + + // + // check that setting device address succeeded by retrieving the device descriptor + // + Status = CreateDeviceDescriptor(); + if (!NT_SUCCESS(Status)) + { + // + // failed to retrieve device descriptor + // + DPRINT1("CUSBbDevice::SetDeviceAddress> failed to retrieve device descriptor with device address set Error %x\n", Status); + m_DeviceAddress = OldAddress; + + // + // return error status + // + return Status; + } + + // + // sanity checks + // + PC_ASSERT(m_DeviceDescriptor.bNumConfigurations); + + // + // allocate configuration descriptor + // + m_ConfigurationDescriptors = (PUSB_CONFIGURATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(USB_CONFIGURATION) * m_DeviceDescriptor.bNumConfigurations, TAG_USBEHCI); + + // + // zero configuration descriptor + // + RtlZeroMemory(m_ConfigurationDescriptors, sizeof(USB_CONFIGURATION) * m_DeviceDescriptor.bNumConfigurations); + + // + // retrieve the configuration descriptors + // + for(Index = 0; Index < m_DeviceDescriptor.bNumConfigurations; Index++) + { + // + // retrieve configuration descriptors from device + // + Status = CreateConfigurationDescriptor(Index); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CUSBDevice::SetDeviceAddress> failed to retrieve configuration %lu\n", Index); + break; + } + } + + // + // done + // + return Status; + +} + +//---------------------------------------------------------------------------------------- +void +CUSBDevice::GetDeviceDescriptor( + PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) +{ + RtlMoveMemory(DeviceDescriptor, &m_DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); +} + +//---------------------------------------------------------------------------------------- +UCHAR +CUSBDevice::GetConfigurationValue() +{ + // + // return configuration index + // + return m_ConfigurationIndex; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::CommitIrp( + PIRP Irp) +{ + NTSTATUS Status; + PUSBREQUEST Request; + + if (!m_Queue || !m_DmaManager) + { + // + // no queue, wtf? + // + DPRINT1("CUSBDevice::CommitUrb> no queue / dma !!!\n"); + return STATUS_UNSUCCESSFUL; + } + + // + // build usb request + // + Status = m_Queue->CreateUSBRequest(&Request); + if (!NT_SUCCESS(Status)) + { + // + // failed to build request + // + DPRINT1("CUSBDevice::CommitSetupPacket> CreateUSBRequest failed with %x\n", Status); + return Status; + } + + // + // initialize request + // + Status = Request->InitializeWithIrp(m_DmaManager, Irp); + + // + // mark irp as pending + // + IoMarkIrpPending(Irp); + + // + // now add the request + // + Status = m_Queue->AddUSBRequest(Request); + if (!NT_SUCCESS(Status)) + { + // + // failed to add request + // + DPRINT1("CUSBDevice::CommitSetupPacket> failed add request to queue with %x\n", Status); + Request->Release(); + return Status; + } + + // + // done + // + return STATUS_PENDING; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::SubmitIrp( + PIRP Irp) +{ + KIRQL OldLevel; + NTSTATUS Status; + + // + // acquire device lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // commit urb + // + Status = CommitIrp(Irp); + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + return Status; +} +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::CommitSetupPacket( + IN PUSB_DEFAULT_PIPE_SETUP_PACKET Packet, + IN OPTIONAL PUSB_ENDPOINT EndpointDescriptor, + IN ULONG BufferLength, + IN OUT PMDL Mdl) +{ + NTSTATUS Status; + PUSBREQUEST Request; + + if (!m_Queue) + { + // + // no queue, wtf? + // + DPRINT1("CUSBDevice::CommitSetupPacket> no queue!!!\n"); + return STATUS_UNSUCCESSFUL; + } + + // + // build usb request + // + Status = m_Queue->CreateUSBRequest(&Request); + if (!NT_SUCCESS(Status)) + { + // + // failed to build request + // + DPRINT1("CUSBDevice::CommitSetupPacket> CreateUSBRequest failed with %x\n", Status); + return Status; + } + + // + // initialize request + // + Status = Request->InitializeWithSetupPacket(m_DmaManager, Packet, m_DeviceAddress, EndpointDescriptor, BufferLength, Mdl); + if (!NT_SUCCESS(Status)) + { + // + // failed to initialize request + // + DPRINT1("CUSBDevice::CommitSetupPacket> failed to initialize usb request with %x\n", Status); + Request->Release(); + return Status; + } + + // + // now add the request + // + Status = m_Queue->AddUSBRequest(Request); + if (!NT_SUCCESS(Status)) + { + // + // failed to add request + // + DPRINT1("CUSBDevice::CommitSetupPacket> failed add request to queue with %x\n", Status); + Request->Release(); + return Status; + } + + // + // get the result code when the operation has been finished + // + Request->GetResultStatus(&Status, NULL); + + // + // release request + // + Request->Release(); + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::CreateDeviceDescriptor() +{ + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + PMDL Mdl; + NTSTATUS Status; + PVOID Buffer; + + // + // zero descriptor + // + RtlZeroMemory(&m_DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); + RtlZeroMemory(&CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + + // + // setup request + // + CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; + CtrlSetup.wValue.HiByte = USB_DEVICE_DESCRIPTOR_TYPE; + CtrlSetup.wLength = sizeof(USB_DEVICE_DESCRIPTOR); + CtrlSetup.bmRequestType.B = 0x80; + + // + // allocate buffer + // + Buffer = ExAllocatePool(NonPagedPool, PAGE_SIZE); + if (!Buffer) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // zero buffer + // + RtlZeroMemory(Buffer, PAGE_SIZE); + + // + // allocate mdl describing the device descriptor + // + Mdl = IoAllocateMdl(Buffer, sizeof(USB_DEVICE_DESCRIPTOR), FALSE, FALSE, 0); + if (!Mdl) + { + // + // failed to allocate mdl + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build mdl for non paged pool + // + MmBuildMdlForNonPagedPool(Mdl); + + // + // commit setup packet + // + Status = CommitSetupPacket(&CtrlSetup, 0, sizeof(USB_DEVICE_DESCRIPTOR), Mdl); + + // + // now free the mdl + // + IoFreeMdl(Mdl); + + if (NT_SUCCESS(Status)) + { + // + // informal dbg print + // + RtlCopyMemory(&m_DeviceDescriptor, Buffer, sizeof(USB_DEVICE_DESCRIPTOR)); + DumpDeviceDescriptor(&m_DeviceDescriptor); + } + + // + // free buffer + // + ExFreePool(Buffer); + + // + // done + // + return Status; + +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::CreateConfigurationDescriptor( + UCHAR Index) +{ + PVOID Buffer; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PMDL Mdl; + ULONG InterfaceIndex, EndPointIndex; + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + PUSB_ENDPOINT_DESCRIPTOR EndPointDescriptor; + + + // + // sanity checks + // + PC_ASSERT(m_ConfigurationDescriptors); + + // + // first allocate a buffer which should be enough to store all different interfaces and endpoints + // + Buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, TAG_USBEHCI); + if (!Buffer) + { + // + // failed to allocate buffer + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build setup packet + // + CtrlSetup.bmRequestType._BM.Recipient = BMREQUEST_TO_DEVICE; + CtrlSetup.bmRequestType._BM.Type = BMREQUEST_STANDARD; + CtrlSetup.bmRequestType._BM.Reserved = 0; + CtrlSetup.bmRequestType._BM.Dir = BMREQUEST_DEVICE_TO_HOST; + CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; + CtrlSetup.wValue.LowByte = Index; + CtrlSetup.wValue.HiByte = USB_CONFIGURATION_DESCRIPTOR_TYPE; + CtrlSetup.wIndex.W = 0; + CtrlSetup.wLength = PAGE_SIZE; + + // + // now build MDL describing the buffer + // + Mdl = IoAllocateMdl(Buffer, PAGE_SIZE, FALSE, FALSE, 0); + if (!Mdl) + { + // + // failed to allocate mdl + // + ExFreePoolWithTag(Buffer, TAG_USBEHCI); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build mdl for non paged pool + // + MmBuildMdlForNonPagedPool(Mdl); + + // + // commit packet + // + Status = CommitSetupPacket(&CtrlSetup, 0, PAGE_SIZE, Mdl); + if (!NT_SUCCESS(Status)) + { + // + // failed to issue request, cleanup + // + IoFreeMdl(Mdl); + ExFreePool(Buffer); + return Status; + } + + // + // now free the mdl + // + IoFreeMdl(Mdl); + + // + // get configuration descriptor + // + ConfigurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)Buffer; + + // + // informal debug print + // + DumpConfigurationDescriptor(ConfigurationDescriptor); + + // + // sanity check + // + PC_ASSERT(ConfigurationDescriptor->bLength == sizeof(USB_CONFIGURATION_DESCRIPTOR)); + PC_ASSERT(ConfigurationDescriptor->wTotalLength <= PAGE_SIZE); + PC_ASSERT(ConfigurationDescriptor->bNumInterfaces); + + // + // request is complete, initialize configuration descriptor + // + RtlCopyMemory(&m_ConfigurationDescriptors[Index].ConfigurationDescriptor, ConfigurationDescriptor, ConfigurationDescriptor->bLength); + + // + // now allocate interface descriptors + // + m_ConfigurationDescriptors[Index].Interfaces = (PUSB_INTERFACE)ExAllocatePoolWithTag(NonPagedPool, sizeof(USB_INTERFACE) * ConfigurationDescriptor->bNumInterfaces, TAG_USBEHCI); + if (!m_ConfigurationDescriptors[Index].Interfaces) + { + // + // failed to allocate interface descriptors + // + ExFreePool(Buffer); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // zero interface descriptor + // + RtlZeroMemory(m_ConfigurationDescriptors[Index].Interfaces, sizeof(USB_INTERFACE) * ConfigurationDescriptor->bNumInterfaces); + + // + // get first interface descriptor + // + InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)(ConfigurationDescriptor + 1); + + // + // setup interface descriptors + // + for(InterfaceIndex = 0; InterfaceIndex < ConfigurationDescriptor->bNumInterfaces; InterfaceIndex++) + { + // + // sanity check + // + PC_ASSERT(InterfaceDescriptor->bLength == sizeof(USB_INTERFACE_DESCRIPTOR)); + PC_ASSERT(InterfaceDescriptor->bNumEndpoints); + + // + // copy current interface descriptor + // + RtlCopyMemory(&m_ConfigurationDescriptors[Index].Interfaces[InterfaceIndex].InterfaceDescriptor, InterfaceDescriptor, InterfaceDescriptor->bLength); + + // + // allocate end point descriptors + // + m_ConfigurationDescriptors[Index].Interfaces[InterfaceIndex].EndPoints = (PUSB_ENDPOINT)ExAllocatePoolWithTag(NonPagedPool, sizeof(USB_ENDPOINT) * InterfaceDescriptor->bNumEndpoints, TAG_USBEHCI); + if (!m_ConfigurationDescriptors[Index].Interfaces[InterfaceIndex].EndPoints) + { + // + // failed to allocate endpoint + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // zero memory + // + RtlZeroMemory(m_ConfigurationDescriptors[Index].Interfaces[InterfaceIndex].EndPoints, sizeof(USB_ENDPOINT) * InterfaceDescriptor->bNumEndpoints); + + // + // initialize end point descriptors + // + EndPointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)(InterfaceDescriptor + 1); + + for(EndPointIndex = 0; EndPointIndex < InterfaceDescriptor->bNumEndpoints; EndPointIndex++) + { + // + // sanity check + // + PC_ASSERT(EndPointDescriptor->bLength == sizeof(USB_ENDPOINT_DESCRIPTOR)); + + // + // copy endpoint descriptor + // + RtlCopyMemory(&m_ConfigurationDescriptors[Index].Interfaces[InterfaceIndex].EndPoints[EndPointIndex].EndPointDescriptor, EndPointDescriptor, EndPointDescriptor->bLength); + + // + // move to next offset + // + EndPointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)((ULONG_PTR)EndPointDescriptor + EndPointDescriptor->bLength); + } + + // + // update interface descriptor offset + // + InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)EndPointDescriptor; + } + + // + // free buffer + // + ExFreePoolWithTag(Buffer, TAG_USBEHCI); + + // + // done + // + return Status; +} +//---------------------------------------------------------------------------------------- +VOID +CUSBDevice::GetConfigurationDescriptors( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptorBuffer, + IN ULONG BufferLength, + OUT PULONG OutBufferLength) +{ + PVOID Buffer; + ULONG InterfaceIndex, EndpointIndex; + + // + // sanity check + // + PC_ASSERT(BufferLength >= sizeof(USB_CONFIGURATION_DESCRIPTOR)); + PC_ASSERT(ConfigDescriptorBuffer); + PC_ASSERT(OutBufferLength); + + // + // reset copied length + // + *OutBufferLength = 0; + + // + // FIXME: support multiple configurations + // + PC_ASSERT(m_DeviceDescriptor.bNumConfigurations == 1); + + // + // copy first configuration descriptor + // + RtlCopyMemory(ConfigDescriptorBuffer, &m_ConfigurationDescriptors[0].ConfigurationDescriptor, sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + // + // subtract length + // + BufferLength -= sizeof(USB_CONFIGURATION_DESCRIPTOR); + *OutBufferLength += sizeof(USB_CONFIGURATION_DESCRIPTOR); + + // + // increment offset + // + Buffer = (PVOID)(ConfigDescriptorBuffer + 1); + + for(InterfaceIndex = 0; InterfaceIndex < m_ConfigurationDescriptors[0].ConfigurationDescriptor.bNumInterfaces; InterfaceIndex++) + { + if (BufferLength < sizeof(USB_INTERFACE_DESCRIPTOR)) + { + // + // no more room in buffer + // + return; + } + + // + // copy interface descriptor + // + RtlCopyMemory(Buffer, &m_ConfigurationDescriptors[0].Interfaces[InterfaceIndex].InterfaceDescriptor, sizeof(USB_INTERFACE_DESCRIPTOR)); + + // + // increment offset + // + Buffer = (PVOID)((ULONG_PTR)Buffer + sizeof(USB_INTERFACE_DESCRIPTOR)); + BufferLength -= sizeof(USB_INTERFACE_DESCRIPTOR); + *OutBufferLength += sizeof(USB_INTERFACE_DESCRIPTOR); + + // + // does the interface have endpoints + // + if (m_ConfigurationDescriptors[0].Interfaces[InterfaceIndex].InterfaceDescriptor.bNumEndpoints) + { + // + // is enough space available + // + if (BufferLength < sizeof(USB_ENDPOINT_DESCRIPTOR) * m_ConfigurationDescriptors[0].Interfaces[InterfaceIndex].InterfaceDescriptor.bNumEndpoints) + { + // + // no buffer + // + return; + } + + // + // copy end points + // + for(EndpointIndex = 0; EndpointIndex < m_ConfigurationDescriptors[0].Interfaces[InterfaceIndex].InterfaceDescriptor.bNumEndpoints; EndpointIndex++) + { + // + // copy endpoint + // + RtlCopyMemory(Buffer, &m_ConfigurationDescriptors[0].Interfaces[InterfaceIndex].EndPoints[EndpointIndex].EndPointDescriptor, sizeof(USB_ENDPOINT_DESCRIPTOR)); + + // + // increment buffer offset + // + Buffer = (PVOID)((ULONG_PTR)Buffer + sizeof(USB_ENDPOINT_DESCRIPTOR)); + BufferLength -= sizeof(USB_ENDPOINT_DESCRIPTOR); + *OutBufferLength += sizeof(USB_ENDPOINT_DESCRIPTOR); + } + } + } +} + +//---------------------------------------------------------------------------------------- +ULONG +CUSBDevice::GetConfigurationDescriptorsLength() +{ + // + // FIXME: support multiple configurations + // + PC_ASSERT(m_DeviceDescriptor.bNumConfigurations == 1); + + return m_ConfigurationDescriptors[0].ConfigurationDescriptor.wTotalLength; +} +//---------------------------------------------------------------------------------------- +VOID +CUSBDevice::DumpDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) +{ + DPRINT1("Dumping Device Descriptor %x\n", DeviceDescriptor); + DPRINT1("bLength %x\n", DeviceDescriptor->bLength); + DPRINT1("bDescriptorType %x\n", DeviceDescriptor->bDescriptorType); + DPRINT1("bcdUSB %x\n", DeviceDescriptor->bcdUSB); + DPRINT1("bDeviceClass %x\n", DeviceDescriptor->bDeviceClass); + DPRINT1("bDeviceSubClass %x\n", DeviceDescriptor->bDeviceSubClass); + DPRINT1("bDeviceProtocol %x\n", DeviceDescriptor->bDeviceProtocol); + DPRINT1("bMaxPacketSize0 %x\n", DeviceDescriptor->bMaxPacketSize0); + DPRINT1("idVendor %x\n", DeviceDescriptor->idVendor); + DPRINT1("idProduct %x\n", DeviceDescriptor->idProduct); + DPRINT1("bcdDevice %x\n", DeviceDescriptor->bcdDevice); + DPRINT1("iManufacturer %x\n", DeviceDescriptor->iManufacturer); + DPRINT1("iProduct %x\n", DeviceDescriptor->iProduct); + DPRINT1("iSerialNumber %x\n", DeviceDescriptor->iSerialNumber); + DPRINT1("bNumConfigurations %x\n", DeviceDescriptor->bNumConfigurations); +} + +//---------------------------------------------------------------------------------------- +VOID +CUSBDevice::DumpConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) +{ + DPRINT1("Dumping ConfigurationDescriptor %x\n", ConfigurationDescriptor); + DPRINT1("bLength %x\n", ConfigurationDescriptor->bLength); + DPRINT1("bDescriptorType %x\n", ConfigurationDescriptor->bDescriptorType); + DPRINT1("wTotalLength %x\n", ConfigurationDescriptor->wTotalLength); + DPRINT1("bNumInterfaces %x\n", ConfigurationDescriptor->bNumInterfaces); + DPRINT1("bConfigurationValue %x\n", ConfigurationDescriptor->bConfigurationValue); + DPRINT1("iConfiguration %x\n", ConfigurationDescriptor->iConfiguration); + DPRINT1("bmAttributes %x\n", ConfigurationDescriptor->bmAttributes); + DPRINT1("MaxPower %x\n", ConfigurationDescriptor->MaxPower); +} +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::SubmitSetupPacket( + IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, + IN OUT ULONG BufferLength, + OUT PVOID Buffer) +{ + NTSTATUS Status; + PMDL Mdl = NULL; + + if (BufferLength) + { + // + // allocate mdl + // + Mdl = IoAllocateMdl(Buffer, BufferLength, FALSE, FALSE, 0); + if (!Mdl) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // HACK HACK HACK: assume the buffer is build from non paged pool + // + MmBuildMdlForNonPagedPool(Mdl); + } + + // + // commit setup packet + // + Status = CommitSetupPacket(SetupPacket, 0, BufferLength, Mdl); + + if (Mdl != NULL) + { + // + // free mdl + // + IoFreeMdl(Mdl); + } + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::SelectConfiguration( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + IN PUSBD_INTERFACE_INFORMATION InterfaceInfo, + OUT USBD_CONFIGURATION_HANDLE *ConfigurationHandle) +{ + ULONG InterfaceIndex, PipeIndex; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + + // + // sanity checks + // + ASSERT(ConfigurationDescriptor->iConfiguration < m_DeviceDescriptor.bNumConfigurations); + ASSERT(ConfigurationDescriptor->iConfiguration == m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].ConfigurationDescriptor.iConfiguration); + + // + // sanity check + // + ASSERT(ConfigurationDescriptor->bNumInterfaces <= m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].ConfigurationDescriptor.bNumInterfaces); + + // + // now build setup packet + // + RtlZeroMemory(&CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + CtrlSetup.bRequest = USB_REQUEST_SET_CONFIGURATION; + CtrlSetup.wValue.W = ConfigurationDescriptor->bConfigurationValue; + + // + // select configuration + // + Status = CommitSetupPacket(&CtrlSetup, 0, 0, 0); + + // + // informal debug print + // + DPRINT1("CUsbDevice::SelectConfiguration New Configuration %x Old Configuration %x Result %x\n", ConfigurationDescriptor->iConfiguration, m_ConfigurationIndex, Status); + if (!NT_SUCCESS(Status)) + { + // + // failed + // + return Status; + } + + // + // store configuration device index + // + m_ConfigurationIndex = ConfigurationDescriptor->iConfiguration; + + // + // store configuration handle + // + *ConfigurationHandle = &m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration]; + + // + // copy interface info and pipe info + // + for(InterfaceIndex = 0; InterfaceIndex < ConfigurationDescriptor->bNumInterfaces; InterfaceIndex++) + { + // + // sanity check: is the info pre-layed out + // + PC_ASSERT(InterfaceInfo->NumberOfPipes == m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].Interfaces[InterfaceIndex].InterfaceDescriptor.bNumEndpoints); + PC_ASSERT(InterfaceInfo->Length != 0); +#ifdef _MSC_VER + PC_ASSERT(InterfaceInfo->Length == FIELD_OFFSET(USBD_INTERFACE_INFORMATION, Pipes[InterfaceInfo->NumberOfPipes])); +#endif + + // + // copy interface info + // + InterfaceInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)&m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].Interfaces[InterfaceIndex]; + InterfaceInfo->Class = m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].Interfaces[InterfaceIndex].InterfaceDescriptor.bInterfaceClass; + InterfaceInfo->SubClass = m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].Interfaces[InterfaceIndex].InterfaceDescriptor.bInterfaceSubClass; + InterfaceInfo->Protocol = m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].Interfaces[InterfaceIndex].InterfaceDescriptor.bInterfaceProtocol; + InterfaceInfo->Reserved = 0; + + // + // copy endpoint info + // + for(PipeIndex = 0; PipeIndex < InterfaceInfo->NumberOfPipes; PipeIndex++) + { + // + // copy pipe info + // + InterfaceInfo->Pipes[PipeIndex].MaximumPacketSize = m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].Interfaces[InterfaceIndex].EndPoints[PipeIndex].EndPointDescriptor.wMaxPacketSize; + InterfaceInfo->Pipes[PipeIndex].EndpointAddress = m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].Interfaces[InterfaceIndex].EndPoints[PipeIndex].EndPointDescriptor.bEndpointAddress; + InterfaceInfo->Pipes[PipeIndex].Interval = m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].Interfaces[InterfaceIndex].EndPoints[PipeIndex].EndPointDescriptor.bInterval; + InterfaceInfo->Pipes[PipeIndex].PipeType = (USBD_PIPE_TYPE)m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].Interfaces[InterfaceIndex].EndPoints[PipeIndex].EndPointDescriptor.bmAttributes; + InterfaceInfo->Pipes[PipeIndex].PipeHandle = (PVOID)&m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].Interfaces[InterfaceIndex].EndPoints[PipeIndex].EndPointDescriptor; + + // + // data toggle is reset on configuration requests + // + m_ConfigurationDescriptors[ConfigurationDescriptor->iConfiguration].Interfaces[InterfaceIndex].EndPoints[PipeIndex].DataToggle = FALSE; + } + + // + // move offset + // + InterfaceInfo = (PUSBD_INTERFACE_INFORMATION)((ULONG_PTR)PtrToUlong(InterfaceInfo) + InterfaceInfo->Length); + } + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::SelectInterface( + IN USBD_CONFIGURATION_HANDLE ConfigurationHandle, + IN OUT PUSBD_INTERFACE_INFORMATION InterfaceInfo) +{ + PUSB_CONFIGURATION Configuration; + ULONG PipeIndex; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + + // + // get configuration struct + // + Configuration = (PUSB_CONFIGURATION)ConfigurationHandle; + + // + // sanity check + // + ASSERT(Configuration->ConfigurationDescriptor.bDescriptorType == USB_CONFIGURATION_DESCRIPTOR_TYPE); + ASSERT(Configuration->ConfigurationDescriptor.bLength == sizeof(USB_CONFIGURATION_DESCRIPTOR)); + ASSERT(Configuration->ConfigurationDescriptor.iConfiguration < m_DeviceDescriptor.bNumConfigurations); + ASSERT(&m_ConfigurationDescriptors[Configuration->ConfigurationDescriptor.iConfiguration] == Configuration); + + // + // initialize setup packet + // + RtlZeroMemory(&CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + CtrlSetup.bRequest = USB_REQUEST_SET_INTERFACE; + CtrlSetup.wValue.W = Configuration->Interfaces[InterfaceInfo->InterfaceNumber].InterfaceDescriptor.bAlternateSetting; + CtrlSetup.wIndex.W = Configuration->Interfaces[InterfaceInfo->InterfaceNumber].InterfaceDescriptor.bInterfaceNumber; + CtrlSetup.bmRequestType.B = 0x01; + + // + // issue request + // + Status = CommitSetupPacket(&CtrlSetup, 0, 0, 0); + + // + // informal debug print + // + DPRINT1("CUSBDevice::SelectInterface AlternateSetting %x InterfaceNumber %x Status %x\n", InterfaceInfo->AlternateSetting, InterfaceInfo->InterfaceNumber, Status); + if (!NT_SUCCESS(Status)) + { + // + // failed to select interface + // + return Status; + } + + + // + // sanity checks + // + PC_ASSERT(Configuration->ConfigurationDescriptor.bNumInterfaces > InterfaceInfo->InterfaceNumber); + PC_ASSERT(Configuration->Interfaces[InterfaceInfo->InterfaceNumber].InterfaceDescriptor.bNumEndpoints == InterfaceInfo->NumberOfPipes); +#ifdef _MSC_VER + PC_ASSERT(InterfaceInfo->Length == FIELD_OFFSET(USBD_INTERFACE_INFORMATION, Pipes[InterfaceInfo->NumberOfPipes])); +#endif + + // + // copy pipe handles + // + for(PipeIndex = 0; PipeIndex < InterfaceInfo->NumberOfPipes; PipeIndex++) + { + // + // copy pipe handle + // + DPRINT1("PipeIndex %lu\n", PipeIndex); + DPRINT1("EndpointAddress %x\n", InterfaceInfo->Pipes[PipeIndex].EndpointAddress); + DPRINT1("Interval %d\n", InterfaceInfo->Pipes[PipeIndex].Interval); + DPRINT1("MaximumPacketSize %d\n", InterfaceInfo->Pipes[PipeIndex].MaximumPacketSize); + DPRINT1("MaximumTransferSize %d\n", InterfaceInfo->Pipes[PipeIndex].MaximumTransferSize); + DPRINT1("PipeFlags %d\n", InterfaceInfo->Pipes[PipeIndex].PipeFlags); + DPRINT1("PipeType %dd\n", InterfaceInfo->Pipes[PipeIndex].PipeType); + DPRINT1("UsbEndPoint %x\n", Configuration->Interfaces[InterfaceInfo->InterfaceNumber].EndPoints[PipeIndex].EndPointDescriptor.bEndpointAddress); + PC_ASSERT(Configuration->Interfaces[InterfaceInfo->InterfaceNumber].EndPoints[PipeIndex].EndPointDescriptor.bEndpointAddress == InterfaceInfo->Pipes[PipeIndex].EndpointAddress); + + InterfaceInfo->Pipes[PipeIndex].PipeHandle = &Configuration->Interfaces[InterfaceInfo->InterfaceNumber].EndPoints[PipeIndex].EndPointDescriptor; + + // + // data toggle is reset on select interface requests + // + m_ConfigurationDescriptors[Configuration->ConfigurationDescriptor.iConfiguration].Interfaces[InterfaceInfo->InterfaceNumber].EndPoints[PipeIndex].DataToggle = FALSE; + + if (Configuration->Interfaces[InterfaceInfo->InterfaceNumber].EndPoints[PipeIndex].EndPointDescriptor.bmAttributes & (USB_ENDPOINT_TYPE_ISOCHRONOUS | USB_ENDPOINT_TYPE_INTERRUPT)) + { + // + // FIXME: check if enough bandwidth is available + // + } + } + + + // + // done + // + return Status; +} + +NTSTATUS +CUSBDevice::AbortPipe( + IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor) +{ + // + // let it handle usb queue + // + ASSERT(m_Queue); + ASSERT(m_DeviceAddress); + + // + // done + // + return m_Queue->AbortDevicePipe(m_DeviceAddress, EndpointDescriptor); +} + + +//---------------------------------------------------------------------------------------- +NTSTATUS +CreateUSBDevice( + PUSBDEVICE *OutDevice) +{ + CUSBDevice * This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBEHCI) CUSBDevice(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutDevice = (PUSBDEVICE)This; + + // + // done + // + return STATUS_SUCCESS; +} + diff --git a/reactos/drivers/usb/usbehci/usb_queue.cpp b/reactos/drivers/usb/usbehci/usb_queue.cpp new file mode 100644 index 00000000000..3c7d4db58fe --- /dev/null +++ b/reactos/drivers/usb/usbehci/usb_queue.cpp @@ -0,0 +1,978 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbehci/usb_queue.cpp + * PURPOSE: USB EHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbehci.h" +#include "hardware.h" + +class CUSBQueue : public IUSBQueue +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Hardware, PDMA_ADAPTER AdapterObject, IN PDMAMEMORYMANAGER MemManager, IN OPTIONAL PKSPIN_LOCK Lock); + virtual ULONG GetPendingRequestCount(); + virtual NTSTATUS AddUSBRequest(PURB Urb); + virtual NTSTATUS AddUSBRequest(IUSBRequest * Request); + virtual NTSTATUS CancelRequests(); + virtual NTSTATUS CreateUSBRequest(IUSBRequest **OutRequest); + virtual VOID InterruptCallback(IN NTSTATUS Status, OUT PULONG ShouldRingDoorBell); + virtual VOID CompleteAsyncRequests(); + virtual NTSTATUS AbortDevicePipe(UCHAR DeviceAddress, IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor); + + + // constructor / destructor + CUSBQueue(IUnknown *OuterUnknown){} + virtual ~CUSBQueue(){} + +protected: + LONG m_Ref; // reference count + PKSPIN_LOCK m_Lock; // list lock + PDMA_ADAPTER m_Adapter; // dma adapter + PUSBHARDWAREDEVICE m_Hardware; // stores hardware object + PQUEUE_HEAD AsyncListQueueHead; // async queue head + LIST_ENTRY m_CompletedRequestAsyncList; // completed async request list + LIST_ENTRY m_PendingRequestAsyncList; // pending async request list + ULONG m_MaxPeriodicListEntries; // max perdiodic list entries + ULONG m_MaxPollingInterval; // max polling interval + PHYSICAL_ADDRESS m_SyncFrameListAddr; // physical address of sync frame list + PULONG m_SyncFrameList; // virtual address of sync frame list + PQUEUE_HEAD * m_SyncFrameListQueueHeads; // stores the frame list of queue head + + // queue head manipulation functions + VOID LinkQueueHead(PQUEUE_HEAD HeadQueueHead, PQUEUE_HEAD NewQueueHead); + VOID UnlinkQueueHead(PQUEUE_HEAD QueueHead); + VOID LinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead, PQUEUE_HEAD NewQueueHead); + PQUEUE_HEAD UnlinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead, ULONG Count); + + // processes the async list + VOID ProcessAsyncList(IN NTSTATUS Status, OUT PULONG ShouldRingDoorBell); + + // called for each completed queue head + VOID QueueHeadCompletion(PQUEUE_HEAD QueueHead, NTSTATUS Status); + + // called when the completion queue is cleaned up + VOID QueueHeadCleanup(PQUEUE_HEAD QueueHead); + + // intializes the sync schedule + NTSTATUS InitializeSyncSchedule(IN PUSBHARDWAREDEVICE Hardware, IN PDMAMEMORYMANAGER MemManager); +}; + +//================================================================================================= +// COM +// +NTSTATUS +STDMETHODCALLTYPE +CUSBQueue::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + if (IsEqualGUIDAligned(refiid, IID_IUnknown)) + { + *Output = PVOID(PUNKNOWN(this)); + PUNKNOWN(*Output)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS +CUSBQueue::Initialize( + IN PUSBHARDWAREDEVICE Hardware, + IN PDMA_ADAPTER AdapterObject, + IN PDMAMEMORYMANAGER MemManager, + IN OPTIONAL PKSPIN_LOCK Lock) +{ + NTSTATUS Status = STATUS_SUCCESS; + + DPRINT("CUSBQueue::Initialize()\n"); + + ASSERT(Hardware); + + // + // initialize device lock + // + m_Lock = Lock; + + // + // Get the AsyncQueueHead + // + AsyncListQueueHead = (PQUEUE_HEAD)Hardware->GetAsyncListQueueHead(); + + // + // Initialize the List Head + // + InitializeListHead(&AsyncListQueueHead->LinkedQueueHeads); + + // + // Initialize completed async list head + // + InitializeListHead(&m_CompletedRequestAsyncList); + + // + // Initialize pending async list head + // + InitializeListHead(&m_PendingRequestAsyncList); + + // + // now initialize sync schedule + // + Status = InitializeSyncSchedule(Hardware, MemManager); + + // + // store hardware object + // + m_Hardware = Hardware; + + return Status; +} + +NTSTATUS +CUSBQueue::InitializeSyncSchedule( + IN PUSBHARDWAREDEVICE Hardware, + IN PDMAMEMORYMANAGER MemManager) +{ + PHYSICAL_ADDRESS QueueHeadPhysAddr; + NTSTATUS Status; + ULONG Index; + PQUEUE_HEAD QueueHead; + + // + // FIXME: check if smaller list sizes are supported + // + m_MaxPeriodicListEntries = 1024; + + // + // use polling scheme of 32ms + // + m_MaxPollingInterval = 32; + + // + // allocate dummy frame list array + // + m_SyncFrameListQueueHeads = (PQUEUE_HEAD*)ExAllocatePool(NonPagedPool, m_MaxPollingInterval * sizeof(PQUEUE_HEAD)); + if (!m_SyncFrameListQueueHeads) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // first allocate a page to hold the queue array + // + Status = MemManager->Allocate(m_MaxPeriodicListEntries * sizeof(PVOID), (PVOID*)&m_SyncFrameList, &m_SyncFrameListAddr); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate sync frame list array + // + DPRINT1("Failed to allocate sync frame list\n"); + ExFreePool(m_SyncFrameListQueueHeads); + //ASSERT(FALSE); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // now allocate queue head descriptors for the polling interval + // + for(Index = 0; Index < m_MaxPeriodicListEntries; Index++) + { + // + // check if is inside our polling interrupt frequency window + // + if (Index < m_MaxPollingInterval) + { + // + // allocate queue head + // + Status = MemManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&QueueHead, &QueueHeadPhysAddr); + + // + // initialize queue head + // + QueueHead->HorizontalLinkPointer = TERMINATE_POINTER; + QueueHead->AlternateNextPointer = TERMINATE_POINTER; + QueueHead->NextPointer = TERMINATE_POINTER; + + // + // 1 for non high speed, 0 for high speed device + // + QueueHead->EndPointCharacteristics.ControlEndPointFlag = 0; + QueueHead->EndPointCharacteristics.HeadOfReclamation = FALSE; + QueueHead->EndPointCharacteristics.MaximumPacketLength = 64; + + // + // Set NakCountReload to max value possible + // + QueueHead->EndPointCharacteristics.NakCountReload = 0xF; + + // + // Get the Initial Data Toggle from the QEDT + // + QueueHead->EndPointCharacteristics.QEDTDataToggleControl = FALSE; + + // + // FIXME: check if High Speed Device + // + QueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED; + QueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x03; + QueueHead->Token.DWord = 0; + QueueHead->Token.Bits.InterruptOnComplete = FALSE; + QueueHead->PhysicalAddr = QueueHeadPhysAddr.LowPart; + + + // + // store in queue head array + // + m_SyncFrameListQueueHeads[Index] = QueueHead; + } + else + { + // + // get cached entry + // + QueueHead = m_SyncFrameListQueueHeads[m_MaxPeriodicListEntries % m_MaxPollingInterval]; + } + + // + // store entry + // + m_SyncFrameList[Index] = (QueueHead->PhysicalAddr | 0x2); + } + + // + // now set the sync base + // + Hardware->SetPeriodicListRegister(m_SyncFrameListAddr.LowPart); + + // + // sync frame list initialized + // + return STATUS_SUCCESS; +} + +ULONG +CUSBQueue::GetPendingRequestCount() +{ + // + // Loop through the pending list and iterrate one for each QueueHead that + // has a IRP to complete. + // + + return 0; +} + +NTSTATUS +CUSBQueue::AddUSBRequest( + IUSBRequest * Request) +{ + PQUEUE_HEAD QueueHead; + NTSTATUS Status; + ULONG Type; + KIRQL OldLevel; + + // + // sanity check + // + ASSERT(Request != NULL); + + // + // get request type + // + Type = Request->GetTransferType(); + + // + // check if supported + // + switch(Type) + { + case USB_ENDPOINT_TYPE_ISOCHRONOUS: + case USB_ENDPOINT_TYPE_INTERRUPT: + /* NOT IMPLEMENTED IN QUEUE */ + Status = STATUS_NOT_SUPPORTED; + break; + case USB_ENDPOINT_TYPE_BULK: + case USB_ENDPOINT_TYPE_CONTROL: + Status = STATUS_SUCCESS; + break; + default: + /* BUG */ + PC_ASSERT(FALSE); + Status = STATUS_NOT_SUPPORTED; + } + + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // request not supported, please try later + // + return Status; + } + + if (Type == USB_ENDPOINT_TYPE_BULK || Type == USB_ENDPOINT_TYPE_CONTROL) + { + // + // get queue head + // + Status = Request->GetQueueHead(&QueueHead); + + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // failed to get queue head + // + return Status; + } + + DPRINT("Request %p QueueHead %p inserted into AsyncQueue\n", Request, QueueHead); + + // + // Add it to the pending list + // + KeAcquireSpinLock(m_Lock, &OldLevel); + LinkQueueHead(AsyncListQueueHead, QueueHead); + KeReleaseSpinLock(m_Lock, OldLevel); + + } + + + // + // add extra reference which is released when the request is completed + // + Request->AddRef(); + + + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBQueue::AddUSBRequest( + PURB Urb) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CUSBQueue::CancelRequests() +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CUSBQueue::CreateUSBRequest( + IUSBRequest **OutRequest) +{ + PUSBREQUEST UsbRequest; + NTSTATUS Status; + + *OutRequest = NULL; + Status = InternalCreateUSBRequest(&UsbRequest); + + if (NT_SUCCESS(Status)) + { + *OutRequest = UsbRequest; + } + + return Status; +} + +// +// LinkQueueHead - Links one QueueHead to the end of HeadQueueHead list, updating HorizontalLinkPointer. +// +VOID +CUSBQueue::LinkQueueHead( + PQUEUE_HEAD HeadQueueHead, + PQUEUE_HEAD NewQueueHead) +{ + PQUEUE_HEAD LastQueueHead, NextQueueHead; + PLIST_ENTRY Entry; + ASSERT(HeadQueueHead); + ASSERT(NewQueueHead); + + // + // Link the LIST_ENTRYs + // + ASSERT(IsListEmpty(&HeadQueueHead->LinkedQueueHeads)); + InsertTailList(&HeadQueueHead->LinkedQueueHeads, &NewQueueHead->LinkedQueueHeads); + + // + // Update HLP for Previous QueueHead, which should be the last in list. + // + Entry = NewQueueHead->LinkedQueueHeads.Blink; + LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + ASSERT(LastQueueHead == HeadQueueHead); + LastQueueHead->HorizontalLinkPointer = (NewQueueHead->PhysicalAddr | QH_TYPE_QH); + + // + // Update HLP for NewQueueHead to point to next, which should be the HeadQueueHead + // + Entry = NewQueueHead->LinkedQueueHeads.Flink; + NextQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + ASSERT(NextQueueHead == HeadQueueHead); + NewQueueHead->HorizontalLinkPointer = (NextQueueHead->PhysicalAddr | QH_TYPE_QH); + + // + // head queue head must be halted + // + //PC_ASSERT(HeadQueueHead->Token.Bits.Halted == TRUE); +} + +// +// UnlinkQueueHead - Unlinks one QueueHead, updating HorizontalLinkPointer. +// +VOID +CUSBQueue::UnlinkQueueHead( + PQUEUE_HEAD QueueHead) +{ + PQUEUE_HEAD PreviousQH, NextQH; + PLIST_ENTRY Entry; + + // + // sanity check: there must be at least one queue head with halted bit set + // + //PC_ASSERT(QueueHead->Token.Bits.Halted == 0); + + // + // get previous link + // + Entry = QueueHead->LinkedQueueHeads.Blink; + + // + // get queue head structure + // + PreviousQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + + // + // get next link + // + Entry = QueueHead->LinkedQueueHeads.Flink; + + // + // get queue head structure + // + NextQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + + // + // sanity check + // + ASSERT(QueueHead->HorizontalLinkPointer == (NextQH->PhysicalAddr | QH_TYPE_QH)); + + // + // remove queue head from linked list + // + PreviousQH->HorizontalLinkPointer = NextQH->PhysicalAddr | QH_TYPE_QH; + + // + // remove software link + // + RemoveEntryList(&QueueHead->LinkedQueueHeads); +} + +// +// LinkQueueHeadChain - Links a list of QueueHeads to the HeadQueueHead list, updating HorizontalLinkPointer. +// +VOID +CUSBQueue::LinkQueueHeadChain( + PQUEUE_HEAD HeadQueueHead, + PQUEUE_HEAD NewQueueHead) +{ + PQUEUE_HEAD LastQueueHead; + PLIST_ENTRY Entry; + ASSERT(HeadQueueHead); + ASSERT(NewQueueHead); + + // + // Find the last QueueHead in NewQueueHead + // + Entry = NewQueueHead->LinkedQueueHeads.Blink; + ASSERT(Entry != NewQueueHead->LinkedQueueHeads.Flink); + LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + + // + // Set the LinkPointer and Flink + // + LastQueueHead->HorizontalLinkPointer = HeadQueueHead->PhysicalAddr | QH_TYPE_QH; + LastQueueHead->LinkedQueueHeads.Flink = &HeadQueueHead->LinkedQueueHeads; + + // + // Fine the last QueueHead in HeadQueueHead + // + Entry = HeadQueueHead->LinkedQueueHeads.Blink; + HeadQueueHead->LinkedQueueHeads.Blink = &LastQueueHead->LinkedQueueHeads; + LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + LastQueueHead->LinkedQueueHeads.Flink = &NewQueueHead->LinkedQueueHeads; + LastQueueHead->HorizontalLinkPointer = NewQueueHead->PhysicalAddr | QH_TYPE_QH; +} + +// +// UnlinkQueueHeadChain - Unlinks a list number of QueueHeads from HeadQueueHead list, updating HorizontalLinkPointer. +// returns the chain of QueueHeads removed from HeadQueueHead. +// +PQUEUE_HEAD +CUSBQueue::UnlinkQueueHeadChain( + PQUEUE_HEAD HeadQueueHead, + ULONG Count) +{ + PQUEUE_HEAD LastQueueHead, FirstQueueHead; + PLIST_ENTRY Entry; + ULONG Index; + + // + // Find the last QueueHead in NewQueueHead + // + Entry = &HeadQueueHead->LinkedQueueHeads; + FirstQueueHead = CONTAINING_RECORD(Entry->Flink, QUEUE_HEAD, LinkedQueueHeads); + + for (Index = 0; Index < Count; Index++) + { + Entry = Entry->Flink; + + if (Entry == &HeadQueueHead->LinkedQueueHeads) + { + DPRINT1("Warnnig; Only %d QueueHeads in HeadQueueHead\n", Index); + Count = Index + 1; + break; + } + } + + LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + HeadQueueHead->LinkedQueueHeads.Flink = LastQueueHead->LinkedQueueHeads.Flink; + if (Count + 1 == Index) + { + HeadQueueHead->LinkedQueueHeads.Blink = &HeadQueueHead->LinkedQueueHeads; + } + else + HeadQueueHead->LinkedQueueHeads.Blink = LastQueueHead->LinkedQueueHeads.Flink; + + FirstQueueHead->LinkedQueueHeads.Blink = &LastQueueHead->LinkedQueueHeads; + LastQueueHead->LinkedQueueHeads.Flink = &FirstQueueHead->LinkedQueueHeads; + LastQueueHead->HorizontalLinkPointer = TERMINATE_POINTER; + return FirstQueueHead; +} + +VOID +CUSBQueue::QueueHeadCompletion( + PQUEUE_HEAD CurrentQH, + NTSTATUS Status) +{ + // + // now unlink the queue head + // FIXME: implement chained queue heads + // no need to acquire locks, as it is called with locks held + // + + // + // unlink queue head + // + UnlinkQueueHead(CurrentQH); + + // + // insert into completed list + // + InsertTailList(&m_CompletedRequestAsyncList, &CurrentQH->LinkedQueueHeads); +} + +VOID +CUSBQueue::ProcessAsyncList( + IN NTSTATUS Status, + OUT PULONG ShouldRingDoorBell) +{ + KIRQL OldLevel; + PLIST_ENTRY Entry; + PQUEUE_HEAD QueueHead; + IUSBRequest * Request; + BOOLEAN IsQueueHeadComplete; + + // + // lock completed async list + // + KeAcquireSpinLock(m_Lock, &OldLevel); + + // + // walk async list + // + ASSERT(AsyncListQueueHead); + Entry = AsyncListQueueHead->LinkedQueueHeads.Flink; + + while(Entry != &AsyncListQueueHead->LinkedQueueHeads) + { + // + // get queue head structure + // + QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + ASSERT(QueueHead); + + // + // sanity check + // + PC_ASSERT(QueueHead->Request); + + // + // get IUSBRequest interface + // + Request = (IUSBRequest*)QueueHead->Request; + + // + // move to next entry + // + Entry = Entry->Flink; + + // + // check if queue head is complete + // + IsQueueHeadComplete = Request->IsQueueHeadComplete(QueueHead); + + DPRINT("Request %p QueueHead %p Complete %d\n", Request, QueueHead, IsQueueHeadComplete); + + // + // check if queue head is complete + // + if (IsQueueHeadComplete) + { + // + // current queue head is complete + // + QueueHeadCompletion(QueueHead, Status); + + // + // ring door bell is going to be necessary + // + *ShouldRingDoorBell = TRUE; + } + } + + // + // release lock + // + KeReleaseSpinLock(m_Lock, OldLevel); +} + + +VOID +CUSBQueue::InterruptCallback( + IN NTSTATUS Status, + OUT PULONG ShouldRingDoorBell) +{ + + DPRINT("CUSBQueue::InterruptCallback\n"); + + // + // iterate asynchronous list + // + *ShouldRingDoorBell = FALSE; + ProcessAsyncList(Status, ShouldRingDoorBell); + + // + // TODO: implement periodic schedule processing + // +} + +VOID +CUSBQueue::QueueHeadCleanup( + PQUEUE_HEAD CurrentQH) +{ + PQUEUE_HEAD NewQueueHead; + IUSBRequest * Request; + BOOLEAN ShouldReleaseWhenDone; + USBD_STATUS UrbStatus; + KIRQL OldLevel; + + // + // sanity checks + // + PC_ASSERT(CurrentQH->Token.Bits.Active == 0); + PC_ASSERT(CurrentQH->Request); + + + // + // get request + // + Request = (IUSBRequest*)CurrentQH->Request; + + // + // sanity check + // + PC_ASSERT(Request); + + // + // check if the queue head was completed with errors + // + if (CurrentQH->Token.Bits.Halted) + { + if (CurrentQH->Token.Bits.DataBufferError) + { + // + // data buffer error + // + UrbStatus = USBD_STATUS_DATA_BUFFER_ERROR; + } + else if (CurrentQH->Token.Bits.BabbleDetected) + { + // + // babble detected + // + UrbStatus = USBD_STATUS_BABBLE_DETECTED; + } + else + { + // + // stall pid + // + UrbStatus = USBD_STATUS_STALL_PID; + } + } + else + { + // + // well done ;) + // + UrbStatus = USBD_STATUS_SUCCESS; + } + + // + // Check if the transfer was completed and if UrbStatus is ok + // + if ((Request->IsRequestComplete() == FALSE) && (UrbStatus == USBD_STATUS_SUCCESS)) + { + // + // let IUSBRequest free the queue head + // + Request->FreeQueueHead(CurrentQH); + + // + // request is incomplete, get new queue head + // + if (Request->GetQueueHead(&NewQueueHead) == STATUS_SUCCESS) + { + // + // first acquire request lock + // + KeAcquireSpinLock(m_Lock, &OldLevel); + + // + // add to pending list + // + InsertTailList(&m_PendingRequestAsyncList, &NewQueueHead->LinkedQueueHeads); + + // + // release queue head + // + KeReleaseSpinLock(m_Lock, OldLevel); + + // + // Done for now + // + return; + } + DPRINT1("Unable to create a new QueueHead\n"); + //ASSERT(FALSE); + + // + // Else there was a problem + // FIXME: Find better return + UrbStatus = USBD_STATUS_INSUFFICIENT_RESOURCES; + } + + if (UrbStatus != USBD_STATUS_SUCCESS) + { + DPRINT1("URB failed with status 0x%x\n", UrbStatus); + //PC_ASSERT(FALSE); + } + + // + // notify request that a transfer has completed + // + Request->CompletionCallback(UrbStatus != USBD_STATUS_SUCCESS ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS, + UrbStatus, + CurrentQH); + + // + // let IUSBRequest free the queue head + // + Request->FreeQueueHead(CurrentQH); + + // + // check if we should release request when done + // + ShouldReleaseWhenDone = Request->ShouldReleaseRequestAfterCompletion(); + + // + // release reference when the request was added + // + Request->Release(); + + // + // check if the operation was asynchronous + // + if (ShouldReleaseWhenDone) + { + // + // release outstanding reference count + // + Request->Release(); + } + + // + // request is now released + // +} + +VOID +CUSBQueue::CompleteAsyncRequests() +{ + KIRQL OldLevel; + PLIST_ENTRY Entry; + PQUEUE_HEAD CurrentQH; + IUSBRequest *Request; + + DPRINT("CUSBQueue::CompleteAsyncRequests\n"); + + // + // first acquire request lock + // + KeAcquireSpinLock(m_Lock, &OldLevel); + + // + // the list should not be empty + // + PC_ASSERT(!IsListEmpty(&m_CompletedRequestAsyncList)); + + while(!IsListEmpty(&m_CompletedRequestAsyncList)) + { + // + // remove first entry + // + Entry = RemoveHeadList(&m_CompletedRequestAsyncList); + + // + // get queue head structure + // + CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + + // + // Get the Request for this QueueHead + // + Request = (IUSBRequest*) CurrentQH->Request; + + // + // release lock + // + KeReleaseSpinLock(m_Lock, OldLevel); + + // + // complete request now + // + QueueHeadCleanup(CurrentQH); + + // + // first acquire request lock + // + KeAcquireSpinLock(m_Lock, &OldLevel); + } + + // + // is there a pending async entry + // + if (!IsListEmpty(&m_PendingRequestAsyncList)) + { + // + // remove first entry + // + Entry = RemoveHeadList(&m_PendingRequestAsyncList); + + // + // get queue head structure + // + CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads); + + // + // Add it to the AsyncList list + // + LinkQueueHead(AsyncListQueueHead, CurrentQH); + } + + // + // release lock + // + KeReleaseSpinLock(m_Lock, OldLevel); +} + +NTSTATUS +CUSBQueue::AbortDevicePipe( + IN UCHAR DeviceAddress, + IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS +CreateUSBQueue( + PUSBQUEUE *OutUsbQueue) +{ + PUSBQUEUE This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBEHCI) CUSBQueue(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutUsbQueue = (PUSBQUEUE)This; + + // + // done + // + return STATUS_SUCCESS; +} + diff --git a/reactos/drivers/usb/usbehci/usb_request.cpp b/reactos/drivers/usb/usbehci/usb_request.cpp new file mode 100644 index 00000000000..46ed3a4c4dd --- /dev/null +++ b/reactos/drivers/usb/usbehci/usb_request.cpp @@ -0,0 +1,1828 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbehci/usb_request.cpp + * PURPOSE: USB EHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#define INITGUID + +#include "usbehci.h" +#include "hardware.h" + +class CUSBRequest : public IUSBRequest +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + // IUSBRequest interface functions + virtual NTSTATUS InitializeWithSetupPacket(IN PDMAMEMORYMANAGER DmaManager, IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, IN UCHAR DeviceAddress, IN OPTIONAL PUSB_ENDPOINT EndpointDescriptor, IN OUT ULONG TransferBufferLength, IN OUT PMDL TransferBuffer); + virtual NTSTATUS InitializeWithIrp(IN PDMAMEMORYMANAGER DmaManager, IN OUT PIRP Irp); + virtual VOID CompletionCallback(IN NTSTATUS NtStatusCode, IN ULONG UrbStatusCode, IN struct _QUEUE_HEAD *QueueHead); + virtual VOID CancelCallback(IN NTSTATUS NtStatusCode, IN struct _QUEUE_HEAD *QueueHead); + virtual NTSTATUS GetQueueHead(struct _QUEUE_HEAD ** OutHead); + virtual BOOLEAN IsRequestComplete(); + virtual ULONG GetTransferType(); + virtual VOID GetResultStatus(OUT OPTIONAL NTSTATUS *NtStatusCode, OUT OPTIONAL PULONG UrbStatusCode); + virtual BOOLEAN IsRequestInitialized(); + virtual BOOLEAN ShouldReleaseRequestAfterCompletion(); + virtual VOID FreeQueueHead(struct _QUEUE_HEAD * QueueHead); + virtual VOID GetTransferBuffer(OUT PMDL * OutMDL, OUT PULONG TransferLength); + virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead); + + + // local functions + ULONG InternalGetTransferType(); + UCHAR InternalGetPidDirection(); + NTSTATUS BuildControlTransferQueueHead(PQUEUE_HEAD * OutHead); + NTSTATUS BuildBulkTransferQueueHead(PQUEUE_HEAD * OutHead); + NTSTATUS CreateDescriptor(PQUEUE_TRANSFER_DESCRIPTOR *OutDescriptor); + NTSTATUS CreateQueueHead(PQUEUE_HEAD *OutQueueHead); + UCHAR GetDeviceAddress(); + NTSTATUS BuildSetupPacket(); + NTSTATUS BuildSetupPacketFromURB(); + ULONG InternalCalculateTransferLength(); + NTSTATUS BuildTransferDescriptorChain(IN PQUEUE_HEAD QueueHead, IN PVOID TransferBuffer, IN ULONG TransferBufferLength, IN UCHAR PidCode, IN UCHAR InitialDataToggle, OUT PQUEUE_TRANSFER_DESCRIPTOR * OutFirstDescriptor, OUT PQUEUE_TRANSFER_DESCRIPTOR * OutLastDescriptor, OUT PUCHAR OutDataToggle, OUT PULONG OutTransferBufferOffset); + VOID InitDescriptor(IN PQUEUE_TRANSFER_DESCRIPTOR CurrentDescriptor, IN PVOID TransferBuffer, IN ULONG TransferBufferLength, IN UCHAR PidCode, IN UCHAR DataToggle, OUT PULONG OutDescriptorLength); + VOID DumpQueueHead(IN PQUEUE_HEAD QueueHead); + + + // constructor / destructor + CUSBRequest(IUnknown *OuterUnknown){} + virtual ~CUSBRequest(){} + +protected: + LONG m_Ref; + + // + // memory manager for allocating setup packet / queue head / transfer descriptors + // + PDMAMEMORYMANAGER m_DmaManager; + + // + // caller provided irp packet containing URB request + // + PIRP m_Irp; + + // + // transfer buffer length + // + ULONG m_TransferBufferLength; + + // + // current transfer length + // + ULONG m_TransferBufferLengthCompleted; + + // + // Total Transfer Length + // + ULONG m_TotalBytesTransferred; + + // + // transfer buffer MDL + // + PMDL m_TransferBufferMDL; + + // + // caller provided setup packet + // + PUSB_DEFAULT_PIPE_SETUP_PACKET m_SetupPacket; + + // + // completion event for callers who initialized request with setup packet + // + PKEVENT m_CompletionEvent; + + // + // device address for callers who initialized it with device address + // + UCHAR m_DeviceAddress; + + // + // store end point address + // + PUSB_ENDPOINT m_EndpointDescriptor; + + // + // DMA queue head + // + PQUEUE_HEAD m_QueueHead; + + // + // allocated setup packet from the DMA pool + // + PUSB_DEFAULT_PIPE_SETUP_PACKET m_DescriptorPacket; + PHYSICAL_ADDRESS m_DescriptorSetupPacket; + + // + // stores the result of the operation + // + NTSTATUS m_NtStatusCode; + ULONG m_UrbStatusCode; + + PVOID m_Base; + +}; + +//---------------------------------------------------------------------------------------- +NTSTATUS +STDMETHODCALLTYPE +CUSBRequest::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::InitializeWithSetupPacket( + IN PDMAMEMORYMANAGER DmaManager, + IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, + IN UCHAR DeviceAddress, + IN OPTIONAL PUSB_ENDPOINT EndpointDescriptor, + IN OUT ULONG TransferBufferLength, + IN OUT PMDL TransferBuffer) +{ + // + // sanity checks + // + PC_ASSERT(DmaManager); + PC_ASSERT(SetupPacket); + + // + // initialize packet + // + m_DmaManager = DmaManager; + m_SetupPacket = SetupPacket; + m_TransferBufferLength = TransferBufferLength; + m_TransferBufferMDL = TransferBuffer; + m_DeviceAddress = DeviceAddress; + m_EndpointDescriptor = EndpointDescriptor; + m_TotalBytesTransferred = 0; + + // + // Set Length Completed to 0 + // + m_TransferBufferLengthCompleted = 0; + + // + // allocate completion event + // + m_CompletionEvent = (PKEVENT)ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_USBEHCI); + if (!m_CompletionEvent) + { + // + // failed to allocate completion event + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize completion event + // + KeInitializeEvent(m_CompletionEvent, NotificationEvent, FALSE); + + // + // done + // + return STATUS_SUCCESS; +} +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::InitializeWithIrp( + IN PDMAMEMORYMANAGER DmaManager, + IN OUT PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PURB Urb; + + // + // sanity checks + // + PC_ASSERT(DmaManager); + PC_ASSERT(Irp); + + m_DmaManager = DmaManager; + m_TotalBytesTransferred = 0; + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // sanity check + // + PC_ASSERT(IoStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL); + PC_ASSERT(IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_SUBMIT_URB); + PC_ASSERT(IoStack->Parameters.Others.Argument1 != 0); + + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + + // + // store irp + // + m_Irp = Irp; + + // + // check function type + // + switch (Urb->UrbHeader.Function) + { + // + // luckily those request have the same structure layout + // + case URB_FUNCTION_CLASS_INTERFACE: + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + { + // + // bulk interrupt transfer + // + if (Urb->UrbBulkOrInterruptTransfer.TransferBufferLength) + { + // + // Check if there is a MDL + // + if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL) + { + // + // sanity check + // + PC_ASSERT(Urb->UrbBulkOrInterruptTransfer.TransferBuffer); + + // + // Create one using TransferBuffer + // + DPRINT("Creating Mdl from Urb Buffer %p Length %lu\n", Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength); + m_TransferBufferMDL = IoAllocateMdl(Urb->UrbBulkOrInterruptTransfer.TransferBuffer, + Urb->UrbBulkOrInterruptTransfer.TransferBufferLength, + FALSE, + FALSE, + NULL); + + if (!m_TransferBufferMDL) + { + // + // failed to allocate mdl + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build mdl for non paged pool + // FIXME: Does hub driver already do this when passing MDL? + // + MmBuildMdlForNonPagedPool(m_TransferBufferMDL); + + // + // Keep that ehci created the MDL and needs to free it. + // + } + else + { + m_TransferBufferMDL = Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL; + } + + // + // save buffer length + // + m_TransferBufferLength = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength; + + // + // Set Length Completed to 0 + // + m_TransferBufferLengthCompleted = 0; + + // + // get endpoint descriptor + // + m_EndpointDescriptor = (PUSB_ENDPOINT)Urb->UrbBulkOrInterruptTransfer.PipeHandle; + + } + break; + } + default: + DPRINT1("URB Function: not supported %x\n", Urb->UrbHeader.Function); + //ASSERT(FALSE); + } + + // + // done + // + return STATUS_SUCCESS; + +} + +//---------------------------------------------------------------------------------------- +VOID +CUSBRequest::CompletionCallback( + IN NTSTATUS NtStatusCode, + IN ULONG UrbStatusCode, + IN struct _QUEUE_HEAD *QueueHead) +{ + PIO_STACK_LOCATION IoStack; + PURB Urb; + + // + // FIXME: support linked queue heads + // + + // + // store completion code + // + m_NtStatusCode = NtStatusCode; + m_UrbStatusCode = UrbStatusCode; + + if (m_Irp) + { + // + // set irp completion status + // + m_Irp->IoStatus.Status = NtStatusCode; + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(m_Irp); + + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + + // + // store urb status + // + Urb->UrbHeader.Status = UrbStatusCode; + + // + // Check if the MDL was created + // + if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL) + { + // + // Free Mdl + // + IoFreeMdl(m_TransferBufferMDL); + } + + // + // check if the request was successfull + // + if (!NT_SUCCESS(NtStatusCode)) + { + // + // set returned length to zero in case of error + // + Urb->UrbHeader.Length = 0; + } + else + { + // + // calculate transfer length + // + Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = InternalCalculateTransferLength(); + } + + DPRINT("Request %p Completing Irp %p NtStatusCode %x UrbStatusCode %x Transferred Length %lu\n", this, m_Irp, NtStatusCode, UrbStatusCode, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength); + + // + // FIXME: check if the transfer was split + // if yes dont complete irp yet + // + IoCompleteRequest(m_Irp, IO_NO_INCREMENT); + } + else + { + // + // signal completion event + // + PC_ASSERT(m_CompletionEvent); + KeSetEvent(m_CompletionEvent, 0, FALSE); + } +} +//---------------------------------------------------------------------------------------- +VOID +CUSBRequest::CancelCallback( + IN NTSTATUS NtStatusCode, + IN struct _QUEUE_HEAD *QueueHead) +{ + PIO_STACK_LOCATION IoStack; + PURB Urb; + + // + // FIXME: support linked queue heads + // + + // + // store cancelleation code + // + m_NtStatusCode = NtStatusCode; + + if (m_Irp) + { + // + // set irp completion status + // + m_Irp->IoStatus.Status = NtStatusCode; + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(m_Irp); + + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + + // + // store urb status + // + DPRINT1("Request Cancelled\n"); + Urb->UrbHeader.Status = USBD_STATUS_CANCELED; + Urb->UrbHeader.Length = 0; + + // + // FIXME: check if the transfer was split + // if yes dont complete irp yet + // + IoCompleteRequest(m_Irp, IO_NO_INCREMENT); + } + else + { + // + // signal completion event + // + PC_ASSERT(m_CompletionEvent); + KeSetEvent(m_CompletionEvent, 0, FALSE); + } +} +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::GetQueueHead( + struct _QUEUE_HEAD ** OutHead) +{ + ULONG TransferType; + NTSTATUS Status; + + // + // first get transfer type + // + TransferType = InternalGetTransferType(); + + // + // build request depending on type + // + switch(TransferType) + { + case USB_ENDPOINT_TYPE_CONTROL: + Status = BuildControlTransferQueueHead(OutHead); + break; + case USB_ENDPOINT_TYPE_BULK: + Status = BuildBulkTransferQueueHead(OutHead); + break; + case USB_ENDPOINT_TYPE_INTERRUPT: + DPRINT1("USB_ENDPOINT_TYPE_INTERRUPT not implemented\n"); + Status = STATUS_NOT_IMPLEMENTED; + break; + case USB_ENDPOINT_TYPE_ISOCHRONOUS: + DPRINT1("USB_ENDPOINT_TYPE_ISOCHRONOUS not implemented\n"); + Status = STATUS_NOT_IMPLEMENTED; + break; + default: + PC_ASSERT(FALSE); + Status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (NT_SUCCESS(Status)) + { + // + // store queue head + // + m_QueueHead = *OutHead; + + // + // store request object + // + (*OutHead)->Request = PVOID(this); + } + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +BOOLEAN +CUSBRequest::IsRequestComplete() +{ + // + // FIXME: check if request was split + // + + // + // Check if the transfer was completed, only valid for Bulk Transfers + // + if ((m_TransferBufferLengthCompleted < m_TransferBufferLength) + && (GetTransferType() == USB_ENDPOINT_TYPE_BULK)) + { + // + // Transfer not completed + // + return FALSE; + } + return TRUE; +} +//---------------------------------------------------------------------------------------- +ULONG +CUSBRequest::GetTransferType() +{ + // + // call internal implementation + // + return InternalGetTransferType(); +} + +//---------------------------------------------------------------------------------------- +ULONG +CUSBRequest::InternalGetTransferType() +{ + ULONG TransferType; + + // + // check if an irp is provided + // + if (m_Irp) + { + ASSERT(m_EndpointDescriptor); + + // + // end point is defined in the low byte of bmAttributes + // + TransferType = (m_EndpointDescriptor->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK); + } + else + { + // + // initialized with setup packet, must be a control transfer + // + TransferType = USB_ENDPOINT_TYPE_CONTROL; + ASSERT(m_EndpointDescriptor == FALSE); + } + + // + // done + // + return TransferType; +} + +UCHAR +CUSBRequest::InternalGetPidDirection() +{ + if (m_EndpointDescriptor) + { + // + // end point direction is highest bit in bEndpointAddress + // + return (m_EndpointDescriptor->EndPointDescriptor.bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) >> 7; + } + else + { + // + // request arrives on the control pipe, extract direction from setup packet + // + ASSERT(m_DescriptorPacket); + return (m_DescriptorPacket->bmRequestType.B >> 7); + } +} + +VOID +CUSBRequest::InitDescriptor( + IN PQUEUE_TRANSFER_DESCRIPTOR CurrentDescriptor, + IN PVOID TransferBuffer, + IN ULONG TransferBufferLength, + IN UCHAR PidCode, + IN UCHAR DataToggle, + OUT PULONG OutDescriptorLength) +{ + ULONG Index, Length = 0, PageOffset, BufferLength; + + // + // init transfer descriptor + // + CurrentDescriptor->Token.Bits.PIDCode = PidCode; + CurrentDescriptor->Token.Bits.TotalBytesToTransfer = 0; + CurrentDescriptor->Token.Bits.DataToggle = DataToggle; + + // + // sanity check + // + ASSERT(TransferBufferLength); + + // + // store buffers + // + Index = 0; + do + { + // + // use physical address + // + CurrentDescriptor->BufferPointer[Index] = MmGetPhysicalAddress(TransferBuffer).LowPart; + + // + // Get the offset from page size + // + PageOffset = BYTE_OFFSET(CurrentDescriptor->BufferPointer[Index]); + if (PageOffset != 0) + { + // + // move to next page + // + TransferBuffer = (PVOID)ROUND_TO_PAGES(TransferBuffer); + } + else + { + // + // move to next page + // + TransferBuffer = (PVOID)((ULONG_PTR)TransferBuffer + PAGE_SIZE); + } + + // + // calculate buffer length + // + BufferLength = min(TransferBufferLength, PAGE_SIZE - PageOffset); + + // + // increment transfer bytes + // + CurrentDescriptor->Token.Bits.TotalBytesToTransfer += BufferLength; + CurrentDescriptor->TotalBytesToTransfer += BufferLength; + Length += BufferLength; + DPRINT("Index %lu TransferBufferLength %lu PageOffset %x BufferLength %lu Buffer Phy %p TransferBuffer %p\n", Index, TransferBufferLength, PageOffset, BufferLength, CurrentDescriptor->BufferPointer[Index], TransferBuffer); + + // + // decrement available byte count + // + TransferBufferLength -= BufferLength; + if (TransferBufferLength == 0) + { + // + // end reached + // + break; + } + + // + // sanity check + // + if (Index > 1) + { + // + // no equal buffers + // + ASSERT(CurrentDescriptor->BufferPointer[Index] != CurrentDescriptor->BufferPointer[Index-1]); + } + + // + // next descriptor index + // + Index++; + }while(Index < 5); + + // + // store result + // + *OutDescriptorLength = Length; +} + + +NTSTATUS +CUSBRequest::BuildTransferDescriptorChain( + IN PQUEUE_HEAD QueueHead, + IN PVOID TransferBuffer, + IN ULONG TransferBufferLength, + IN UCHAR PidCode, + IN UCHAR InitialDataToggle, + OUT PQUEUE_TRANSFER_DESCRIPTOR * OutFirstDescriptor, + OUT PQUEUE_TRANSFER_DESCRIPTOR * OutLastDescriptor, + OUT PUCHAR OutDataToggle, + OUT PULONG OutTransferBufferOffset) +{ + PQUEUE_TRANSFER_DESCRIPTOR FirstDescriptor = NULL, CurrentDescriptor, LastDescriptor = NULL; + NTSTATUS Status; + ULONG DescriptorLength, TransferBufferOffset = 0; + + do + { + // + // allocate transfer descriptor + // + Status = CreateDescriptor(&CurrentDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate transfer descriptor + // + ASSERT(FALSE); + return Status; + } + + // + // now init the descriptor + // + InitDescriptor(CurrentDescriptor, + (PVOID)((ULONG_PTR)TransferBuffer + TransferBufferOffset), + TransferBufferLength - TransferBufferOffset, + PidCode, + InitialDataToggle, + &DescriptorLength); + + // + // insert into queue head + // + InsertTailList(&QueueHead->TransferDescriptorListHead, &CurrentDescriptor->DescriptorEntry); + + // + // adjust offset + // + TransferBufferOffset += DescriptorLength; + + if (LastDescriptor) + { + // + // link to current descriptor + // + LastDescriptor->AlternateNextPointer = CurrentDescriptor->PhysicalAddr; + LastDescriptor->NextPointer = CurrentDescriptor->PhysicalAddr; + LastDescriptor = CurrentDescriptor; + } + else + { + // + // first descriptor in chain + // + LastDescriptor = FirstDescriptor = CurrentDescriptor; + } + + if(TransferBufferLength == TransferBufferOffset) + { + // + // end reached + // + break; + } + }while(TRUE); + + if (OutFirstDescriptor) + { + // + // store first descriptor + // + *OutFirstDescriptor = FirstDescriptor; + } + + if (OutLastDescriptor) + { + // + // store last descriptor + // + *OutLastDescriptor = CurrentDescriptor; + } + + if (OutDataToggle) + { + // + // flip data toggle + // + InitialDataToggle = !InitialDataToggle; + + // + // store result data toggle + // + *OutDataToggle = InitialDataToggle; + } + + if (OutTransferBufferOffset) + { + // + // store offset + // + *OutTransferBufferOffset = TransferBufferOffset; + } + + // + // done + // + return STATUS_SUCCESS; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::BuildControlTransferQueueHead( + PQUEUE_HEAD * OutHead) +{ + NTSTATUS Status; + ULONG DescriptorChainLength; + PQUEUE_HEAD QueueHead; + PQUEUE_TRANSFER_DESCRIPTOR SetupDescriptor, StatusDescriptor, FirstDescriptor, LastDescriptor; + + // + // first allocate the queue head + // + Status = CreateQueueHead(&QueueHead); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate queue head + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // sanity check + // + PC_ASSERT(QueueHead); + + // + // create setup packet + // + Status = BuildSetupPacket(); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate setup packet + // + ASSERT(FALSE); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // create setup descriptor + // + Status = CreateDescriptor(&SetupDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate transfer descriptor + // + ASSERT(FALSE); + return Status; + } + + // + // create status descriptor + // + Status = CreateDescriptor(&StatusDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate transfer descriptor + // + ASSERT(FALSE); + return Status; + } + + // + // now initialize the queue head + // + QueueHead->EndPointCharacteristics.DeviceAddress = GetDeviceAddress(); + + if (m_EndpointDescriptor) + { + // + // set endpoint address and max packet length + // + QueueHead->EndPointCharacteristics.EndPointNumber = m_EndpointDescriptor->EndPointDescriptor.bEndpointAddress & 0x0F; + QueueHead->EndPointCharacteristics.MaximumPacketLength = m_EndpointDescriptor->EndPointDescriptor.wMaxPacketSize; + } + + // + // init setup descriptor + // + SetupDescriptor->Token.Bits.PIDCode = PID_CODE_SETUP_TOKEN; + SetupDescriptor->Token.Bits.TotalBytesToTransfer = sizeof(USB_DEFAULT_PIPE_SETUP_PACKET); + SetupDescriptor->Token.Bits.DataToggle = FALSE; + SetupDescriptor->BufferPointer[0] = (ULONG)PtrToUlong(m_DescriptorSetupPacket.LowPart); + InsertTailList(&QueueHead->TransferDescriptorListHead, &SetupDescriptor->DescriptorEntry); + + + // + // init status descriptor + // + StatusDescriptor->Token.Bits.TotalBytesToTransfer = 0; + StatusDescriptor->Token.Bits.DataToggle = TRUE; + StatusDescriptor->Token.Bits.InterruptOnComplete = TRUE; + + // + // is there data + // + if (m_TransferBufferLength) + { + Status = BuildTransferDescriptorChain(QueueHead, + MmGetMdlVirtualAddress(m_TransferBufferMDL), + m_TransferBufferLength, + InternalGetPidDirection(), + TRUE, + &FirstDescriptor, + &LastDescriptor, + NULL, + &DescriptorChainLength); + + // + // FIXME handle errors + // + ASSERT(Status == STATUS_SUCCESS); + ASSERT(DescriptorChainLength == m_TransferBufferLength); + + // + // now link the descriptors + // + SetupDescriptor->NextPointer = FirstDescriptor->PhysicalAddr; + SetupDescriptor->AlternateNextPointer = FirstDescriptor->PhysicalAddr; + LastDescriptor->NextPointer = StatusDescriptor->PhysicalAddr; + LastDescriptor->AlternateNextPointer = StatusDescriptor->PhysicalAddr; + + + // + // pid code is flipped for ops with data stage + // + StatusDescriptor->Token.Bits.PIDCode = !InternalGetPidDirection(); + } + else + { + // + // direct link + // + SetupDescriptor->NextPointer = StatusDescriptor->PhysicalAddr; + SetupDescriptor->AlternateNextPointer = StatusDescriptor->PhysicalAddr; + + // + // retrieve result of operation + // + StatusDescriptor->Token.Bits.PIDCode = PID_CODE_IN_TOKEN; + } + + // + // insert status descriptor + // + InsertTailList(&QueueHead->TransferDescriptorListHead, &StatusDescriptor->DescriptorEntry); + + + // + // link transfer descriptors to queue head + // + QueueHead->NextPointer = SetupDescriptor->PhysicalAddr; + + // + // store result + // + *OutHead = QueueHead; + + // + // displays the current request + // + //DumpQueueHead(QueueHead); + + DPRINT("BuildControlTransferQueueHead done\n"); + // + // done + // + return STATUS_SUCCESS; +} + +VOID +CUSBRequest::DumpQueueHead( + IN PQUEUE_HEAD QueueHead) +{ + PLIST_ENTRY Entry; + PQUEUE_TRANSFER_DESCRIPTOR Descriptor; + ULONG Index = 0; + + DPRINT1("QueueHead %p Addr %x\n", QueueHead, QueueHead->PhysicalAddr); + DPRINT1("QueueHead AlternateNextPointer %x\n", QueueHead->AlternateNextPointer); + DPRINT1("QueueHead NextPointer %x\n", QueueHead->NextPointer); + + DPRINT1("QueueHead HubAddr %x\n", QueueHead->EndPointCharacteristics.ControlEndPointFlag); + DPRINT1("QueueHead DeviceAddress %x\n", QueueHead->EndPointCharacteristics.DeviceAddress); + DPRINT1("QueueHead EndPointNumber %x\n", QueueHead->EndPointCharacteristics.EndPointNumber); + DPRINT1("QueueHead EndPointSpeed %x\n", QueueHead->EndPointCharacteristics.EndPointSpeed); + DPRINT1("QueueHead HeadOfReclamation %x\n", QueueHead->EndPointCharacteristics.HeadOfReclamation); + DPRINT1("QueueHead InactiveOnNextTransaction %x\n", QueueHead->EndPointCharacteristics.InactiveOnNextTransaction); + DPRINT1("QueueHead MaximumPacketLength %x\n", QueueHead->EndPointCharacteristics.MaximumPacketLength); + DPRINT1("QueueHead NakCountReload %x\n", QueueHead->EndPointCharacteristics.NakCountReload); + DPRINT1("QueueHead QEDTDataToggleControl %x\n", QueueHead->EndPointCharacteristics.QEDTDataToggleControl); + DPRINT1("QueueHead HubAddr %x\n", QueueHead->EndPointCapabilities.HubAddr); + DPRINT1("QueueHead InterruptScheduleMask %x\n", QueueHead->EndPointCapabilities.InterruptScheduleMask); + DPRINT1("QueueHead NumberOfTransactionPerFrame %x\n", QueueHead->EndPointCapabilities.NumberOfTransactionPerFrame); + DPRINT1("QueueHead PortNumber %x\n", QueueHead->EndPointCapabilities.PortNumber); + DPRINT1("QueueHead SplitCompletionMask %x\n", QueueHead->EndPointCapabilities.SplitCompletionMask); + + Entry = QueueHead->TransferDescriptorListHead.Flink; + while(Entry != &QueueHead->TransferDescriptorListHead) + { + // + // get transfer descriptor + // + Descriptor = (PQUEUE_TRANSFER_DESCRIPTOR)CONTAINING_RECORD(Entry, QUEUE_TRANSFER_DESCRIPTOR, DescriptorEntry); + + DPRINT1("TransferDescriptor %lu Addr %x\n", Index, Descriptor->PhysicalAddr); + DPRINT1("TransferDescriptor %lu Next %x\n", Index, Descriptor->NextPointer); + DPRINT1("TransferDescriptor %lu AlternateNextPointer %x\n", Index, Descriptor->AlternateNextPointer); + DPRINT1("TransferDescriptor %lu Active %lu\n", Index, Descriptor->Token.Bits.Active); + DPRINT1("TransferDescriptor %lu BabbleDetected %lu\n", Index, Descriptor->Token.Bits.BabbleDetected); + DPRINT1("TransferDescriptor %lu CurrentPage %lu\n", Index, Descriptor->Token.Bits.CurrentPage); + DPRINT1("TransferDescriptor %lu DataBufferError %lu\n", Index, Descriptor->Token.Bits.DataBufferError); + DPRINT1("TransferDescriptor %lu DataToggle %lu\n", Index, Descriptor->Token.Bits.DataToggle); + DPRINT1("TransferDescriptor %lu ErrorCounter %lu\n", Index, Descriptor->Token.Bits.ErrorCounter); + DPRINT1("TransferDescriptor %lu Halted %lu\n", Index, Descriptor->Token.Bits.Halted); + DPRINT1("TransferDescriptor %lu InterruptOnComplete %x\n", Index, Descriptor->Token.Bits.InterruptOnComplete); + DPRINT1("TransferDescriptor %lu MissedMicroFrame %lu\n", Index, Descriptor->Token.Bits.MissedMicroFrame); + DPRINT1("TransferDescriptor %lu PIDCode %lu\n", Index, Descriptor->Token.Bits.PIDCode); + DPRINT1("TransferDescriptor %lu PingState %lu\n", Index, Descriptor->Token.Bits.PingState); + DPRINT1("TransferDescriptor %lu SplitTransactionState %lu\n", Index, Descriptor->Token.Bits.SplitTransactionState); + DPRINT1("TransferDescriptor %lu TotalBytesToTransfer %lu\n", Index, Descriptor->Token.Bits.TotalBytesToTransfer); + DPRINT1("TransferDescriptor %lu TransactionError %lu\n", Index, Descriptor->Token.Bits.TransactionError); + + DPRINT1("TransferDescriptor %lu Buffer Pointer 0 %x\n", Index, Descriptor->BufferPointer[0]); + DPRINT1("TransferDescriptor %lu Buffer Pointer 1 %x\n", Index, Descriptor->BufferPointer[1]); + DPRINT1("TransferDescriptor %lu Buffer Pointer 2 %x\n", Index, Descriptor->BufferPointer[2]); + DPRINT1("TransferDescriptor %lu Buffer Pointer 3 %x\n", Index, Descriptor->BufferPointer[3]); + DPRINT1("TransferDescriptor %lu Buffer Pointer 4 %x\n", Index, Descriptor->BufferPointer[4]); + Entry = Entry->Flink; + Index++; + } +} + + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::BuildBulkTransferQueueHead( + PQUEUE_HEAD * OutHead) +{ + NTSTATUS Status; + PQUEUE_HEAD QueueHead; + PVOID Base; + ULONG ChainDescriptorLength; + PQUEUE_TRANSFER_DESCRIPTOR FirstDescriptor, LastDescriptor; + + // + // Allocate the queue head + // + Status = CreateQueueHead(&QueueHead); + + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate queue heads + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // sanity checks + // + PC_ASSERT(QueueHead); + PC_ASSERT(m_TransferBufferLength); + + if (!m_Base) + { + // + // get virtual base of mdl + // + m_Base = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority); + } + + // + // Increase the size of last transfer, 0 in case this is the first + // + Base = (PVOID)((ULONG_PTR)m_Base + m_TransferBufferLengthCompleted); + + PC_ASSERT(m_EndpointDescriptor); + PC_ASSERT(Base); + + // + // sanity check + // + ASSERT(m_EndpointDescriptor); + + // + // build bulk transfer descriptor chain + // + Status = BuildTransferDescriptorChain(QueueHead, + Base, + m_TransferBufferLength - m_TransferBufferLengthCompleted, + InternalGetPidDirection(), + m_EndpointDescriptor->DataToggle, + &FirstDescriptor, + &LastDescriptor, + &m_EndpointDescriptor->DataToggle, + &ChainDescriptorLength); + + // + // FIXME: handle errors + // + ASSERT(ChainDescriptorLength == m_TransferBufferLength); + + // + // move to next offset + // + m_TransferBufferLengthCompleted += ChainDescriptorLength; + + ASSERT(Status == STATUS_SUCCESS); + + // + // init queue head + // + QueueHead->EndPointCharacteristics.DeviceAddress = GetDeviceAddress(); + QueueHead->EndPointCharacteristics.EndPointNumber = m_EndpointDescriptor->EndPointDescriptor.bEndpointAddress & 0x0F; + QueueHead->EndPointCharacteristics.MaximumPacketLength = m_EndpointDescriptor->EndPointDescriptor.wMaxPacketSize; + QueueHead->NextPointer = FirstDescriptor->PhysicalAddr; + QueueHead->CurrentLinkPointer = FirstDescriptor->PhysicalAddr; + QueueHead->AlternateNextPointer = TERMINATE_POINTER; + + ASSERT(QueueHead->EndPointCharacteristics.DeviceAddress); + ASSERT(QueueHead->EndPointCharacteristics.EndPointNumber); + ASSERT(QueueHead->EndPointCharacteristics.MaximumPacketLength); + ASSERT(QueueHead->NextPointer); + + // + // interrupt on last descriptor + // + LastDescriptor->Token.Bits.InterruptOnComplete = TRUE; + + // + // store result + // + *OutHead = QueueHead; + + // + // dump status + // + //DumpQueueHead(QueueHead); + + // + // done + // + return STATUS_SUCCESS; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::CreateDescriptor( + PQUEUE_TRANSFER_DESCRIPTOR *OutDescriptor) +{ + PQUEUE_TRANSFER_DESCRIPTOR Descriptor; + NTSTATUS Status; + PHYSICAL_ADDRESS TransferDescriptorPhysicalAddress; + + // + // allocate descriptor + // + Status = m_DmaManager->Allocate(sizeof(QUEUE_TRANSFER_DESCRIPTOR), (PVOID*)&Descriptor, &TransferDescriptorPhysicalAddress); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate transfer descriptor + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize transfer descriptor + // + Descriptor->NextPointer = TERMINATE_POINTER; + Descriptor->AlternateNextPointer = TERMINATE_POINTER; + Descriptor->Token.Bits.DataToggle = TRUE; + Descriptor->Token.Bits.ErrorCounter = 0x03; + Descriptor->Token.Bits.Active = TRUE; + Descriptor->PhysicalAddr = TransferDescriptorPhysicalAddress.LowPart; + + // + // store result + // + *OutDescriptor = Descriptor; + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::CreateQueueHead( + PQUEUE_HEAD *OutQueueHead) +{ + PQUEUE_HEAD QueueHead; + PHYSICAL_ADDRESS QueueHeadPhysicalAddress; + NTSTATUS Status; + + // + // allocate queue head + // + Status = m_DmaManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&QueueHead, &QueueHeadPhysicalAddress); + + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate queue head + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize queue head + // + QueueHead->HorizontalLinkPointer = TERMINATE_POINTER; + QueueHead->AlternateNextPointer = TERMINATE_POINTER; + QueueHead->NextPointer = TERMINATE_POINTER; + InitializeListHead(&QueueHead->TransferDescriptorListHead); + + // + // 1 for non high speed, 0 for high speed device + // + QueueHead->EndPointCharacteristics.ControlEndPointFlag = 0; + QueueHead->EndPointCharacteristics.HeadOfReclamation = FALSE; + QueueHead->EndPointCharacteristics.MaximumPacketLength = 64; + + // + // Set NakCountReload to max value possible + // + QueueHead->EndPointCharacteristics.NakCountReload = 0xF; + + // + // Get the Initial Data Toggle from the QEDT + // + QueueHead->EndPointCharacteristics.QEDTDataToggleControl = TRUE; + + // + // FIXME: check if High Speed Device + // + QueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED; + QueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x03; + QueueHead->Token.DWord = 0; + QueueHead->Token.Bits.InterruptOnComplete = FALSE; + + // + // FIXME check if that is really needed + // + QueueHead->PhysicalAddr = QueueHeadPhysicalAddress.LowPart; + + // + // output queue head + // + *OutQueueHead = QueueHead; + + // + // done + // + return STATUS_SUCCESS; +} + +//---------------------------------------------------------------------------------------- +UCHAR +CUSBRequest::GetDeviceAddress() +{ + PIO_STACK_LOCATION IoStack; + PURB Urb; + PUSBDEVICE UsbDevice; + + // + // check if there is an irp provided + // + if (!m_Irp) + { + // + // used provided address + // + return m_DeviceAddress; + } + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(m_Irp); + + // + // get contained urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + + // + // check if there is a pipe handle provided + // + if (Urb->UrbHeader.UsbdDeviceHandle) + { + // + // there is a device handle provided + // + UsbDevice = (PUSBDEVICE)Urb->UrbHeader.UsbdDeviceHandle; + + // + // return device address + // + return UsbDevice->GetDeviceAddress(); + } + + // + // no device handle provided, it is the host root bus + // + return 0; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::BuildSetupPacket() +{ + NTSTATUS Status; + + // + // allocate common buffer setup packet + // + Status = m_DmaManager->Allocate(sizeof(USB_DEFAULT_PIPE_SETUP_PACKET), (PVOID*)&m_DescriptorPacket, &m_DescriptorSetupPacket); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + if (m_SetupPacket) + { + // + // copy setup packet + // + RtlCopyMemory(m_DescriptorPacket, m_SetupPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + } + else + { + // + // build setup packet from urb + // + Status = BuildSetupPacketFromURB(); + } + + // + // done + // + return Status; +} + + +NTSTATUS +CUSBRequest::BuildSetupPacketFromURB() +{ + PIO_STACK_LOCATION IoStack; + PURB Urb; + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + + // + // sanity checks + // + PC_ASSERT(m_Irp); + PC_ASSERT(m_DescriptorPacket); + + // + // get stack location + // + IoStack = IoGetCurrentIrpStackLocation(m_Irp); + + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + + // + // zero descriptor packet + // + RtlZeroMemory(m_DescriptorPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + + + switch (Urb->UrbHeader.Function) + { + /* CLEAR FEATURE */ + case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE: + case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE: + case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT: + UNIMPLEMENTED + break; + + /* GET CONFIG */ + case URB_FUNCTION_GET_CONFIGURATION: + m_DescriptorPacket->bRequest = USB_REQUEST_GET_CONFIGURATION; + m_DescriptorPacket->bmRequestType.B = 0x80; + m_DescriptorPacket->wLength = 1; + break; + + /* GET DESCRIPTOR */ + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + m_DescriptorPacket->bRequest = USB_REQUEST_GET_DESCRIPTOR; + m_DescriptorPacket->wValue.LowByte = Urb->UrbControlDescriptorRequest.Index; + m_DescriptorPacket->wValue.HiByte = Urb->UrbControlDescriptorRequest.DescriptorType; + m_DescriptorPacket->wIndex.W = Urb->UrbControlDescriptorRequest.LanguageId; + m_DescriptorPacket->wLength = Urb->UrbControlDescriptorRequest.TransferBufferLength; + m_DescriptorPacket->bmRequestType.B = 0x80; + break; + + /* GET INTERFACE */ + case URB_FUNCTION_GET_INTERFACE: + m_DescriptorPacket->bRequest = USB_REQUEST_GET_CONFIGURATION; + m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index; + m_DescriptorPacket->bmRequestType.B = 0x80; + m_DescriptorPacket->wLength = 1; + break; + + /* GET STATUS */ + case URB_FUNCTION_GET_STATUS_FROM_DEVICE: + m_DescriptorPacket->bRequest = USB_REQUEST_GET_STATUS; + ASSERT(Urb->UrbControlGetStatusRequest.Index == 0); + m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index; + m_DescriptorPacket->bmRequestType.B = 0x80; + m_DescriptorPacket->wLength = 2; + break; + + case URB_FUNCTION_GET_STATUS_FROM_INTERFACE: + m_DescriptorPacket->bRequest = USB_REQUEST_GET_STATUS; + ASSERT(Urb->UrbControlGetStatusRequest.Index != 0); + m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index; + m_DescriptorPacket->bmRequestType.B = 0x81; + m_DescriptorPacket->wLength = 2; + break; + + case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT: + m_DescriptorPacket->bRequest = USB_REQUEST_GET_STATUS; + ASSERT(Urb->UrbControlGetStatusRequest.Index != 0); + m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index; + m_DescriptorPacket->bmRequestType.B = 0x82; + m_DescriptorPacket->wLength = 2; + break; + + /* SET ADDRESS */ + + /* SET CONFIG */ + case URB_FUNCTION_SELECT_CONFIGURATION: + m_DescriptorPacket->bRequest = USB_REQUEST_SET_CONFIGURATION; + m_DescriptorPacket->wValue.W = Urb->UrbSelectConfiguration.ConfigurationDescriptor->bConfigurationValue; + m_DescriptorPacket->wIndex.W = 0; + m_DescriptorPacket->wLength = 0; + m_DescriptorPacket->bmRequestType.B = 0x00; + break; + + /* SET DESCRIPTOR */ + case URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE: + case URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE: + case URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT: + UNIMPLEMENTED + break; + + /* SET FEATURE */ + case URB_FUNCTION_SET_FEATURE_TO_DEVICE: + m_DescriptorPacket->bRequest = USB_REQUEST_SET_FEATURE; + ASSERT(Urb->UrbControlGetStatusRequest.Index == 0); + m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index; + m_DescriptorPacket->bmRequestType.B = 0x80; + break; + + case URB_FUNCTION_SET_FEATURE_TO_INTERFACE: + m_DescriptorPacket->bRequest = USB_REQUEST_SET_FEATURE; + ASSERT(Urb->UrbControlGetStatusRequest.Index == 0); + m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index; + m_DescriptorPacket->bmRequestType.B = 0x81; + break; + + case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT: + m_DescriptorPacket->bRequest = USB_REQUEST_SET_FEATURE; + ASSERT(Urb->UrbControlGetStatusRequest.Index == 0); + m_DescriptorPacket->wIndex.W = Urb->UrbControlGetStatusRequest.Index; + m_DescriptorPacket->bmRequestType.B = 0x82; + break; + + /* SET INTERFACE*/ + case URB_FUNCTION_SELECT_INTERFACE: + m_DescriptorPacket->bRequest = USB_REQUEST_SET_INTERFACE; + m_DescriptorPacket->wValue.W = Urb->UrbSelectInterface.Interface.AlternateSetting; + m_DescriptorPacket->wIndex.W = Urb->UrbSelectInterface.Interface.InterfaceNumber; + m_DescriptorPacket->wLength = 0; + m_DescriptorPacket->bmRequestType.B = 0x01; + break; + + /* SYNC FRAME */ + case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL: + UNIMPLEMENTED + break; + default: + UNIMPLEMENTED + break; + } + + return Status; +} + +//---------------------------------------------------------------------------------------- +VOID +CUSBRequest::GetResultStatus( + OUT OPTIONAL NTSTATUS * NtStatusCode, + OUT OPTIONAL PULONG UrbStatusCode) +{ + // + // sanity check + // + PC_ASSERT(m_CompletionEvent); + + // + // wait for the operation to complete + // + KeWaitForSingleObject(m_CompletionEvent, Executive, KernelMode, FALSE, NULL); + + // + // copy status + // + if (NtStatusCode) + { + *NtStatusCode = m_NtStatusCode; + } + + // + // copy urb status + // + if (UrbStatusCode) + { + *UrbStatusCode = m_UrbStatusCode; + } + +} + + +//----------------------------------------------------------------------------------------- +BOOLEAN +CUSBRequest::IsRequestInitialized() +{ + if (m_Irp || m_SetupPacket) + { + // + // request is initialized + // + return TRUE; + } + + // + // request is not initialized + // + return FALSE; +} + +//----------------------------------------------------------------------------------------- +BOOLEAN +CUSBRequest::ShouldReleaseRequestAfterCompletion() +{ + if (m_Irp) + { + // + // the request is completed, release it + // + return TRUE; + } + else + { + // + // created with an setup packet, don't release + // + return FALSE; + } +} + +//----------------------------------------------------------------------------------------- +VOID +CUSBRequest::FreeQueueHead( + IN struct _QUEUE_HEAD * QueueHead) +{ + PLIST_ENTRY Entry; + PQUEUE_TRANSFER_DESCRIPTOR Descriptor; + + // + // sanity checks + // + ASSERT(m_DmaManager); + ASSERT(QueueHead); + ASSERT(!IsListEmpty(&QueueHead->TransferDescriptorListHead)); + + do + { + // + // get transfer descriptors + // + Entry = RemoveHeadList(&QueueHead->TransferDescriptorListHead); + ASSERT(Entry); + + // + // obtain descriptor from entry + // + Descriptor = (PQUEUE_TRANSFER_DESCRIPTOR)CONTAINING_RECORD(Entry, QUEUE_TRANSFER_DESCRIPTOR, DescriptorEntry); + ASSERT(Descriptor); + + // + // add transfer count + // + m_TotalBytesTransferred += (Descriptor->TotalBytesToTransfer - Descriptor->Token.Bits.TotalBytesToTransfer); + DPRINT("TotalBytes Transferred in Descriptor %p Phys Addr %x TotalBytesSoftware %lu Length %lu\n", Descriptor, Descriptor->PhysicalAddr, Descriptor->TotalBytesToTransfer, Descriptor->TotalBytesToTransfer - Descriptor->Token.Bits.TotalBytesToTransfer); + + // + // release transfer descriptors + // + m_DmaManager->Release(Descriptor, sizeof(QUEUE_TRANSFER_DESCRIPTOR)); + + }while(!IsListEmpty(&QueueHead->TransferDescriptorListHead)); + + if (m_DescriptorPacket) + { + // + // release packet descriptor + // + m_DmaManager->Release(m_DescriptorPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + } + + // + // release queue head + // + m_DmaManager->Release(QueueHead, sizeof(QUEUE_HEAD)); + + // + // nullify pointers + // + m_QueueHead = 0; + m_DescriptorPacket = 0; +} + +//----------------------------------------------------------------------------------------- +BOOLEAN +CUSBRequest::IsQueueHeadComplete( + struct _QUEUE_HEAD * QueueHead) +{ + PLIST_ENTRY Entry; + PQUEUE_TRANSFER_DESCRIPTOR Descriptor; + + // + // first check - is the queue head currently active + // + if (QueueHead->Token.Bits.Active) + { + // + // queue head is active (currently processed) + // + return FALSE; + } + + if (QueueHead->Token.Bits.Halted) + { + // + // error occured + // + DPRINT1("Found halted queue head %p\n", QueueHead); + DumpQueueHead(QueueHead); + //ASSERT(FALSE); + return TRUE; + } + + // + // loop list and see if there are any active descriptors + // + Entry = QueueHead->TransferDescriptorListHead.Flink; + while(Entry != &QueueHead->TransferDescriptorListHead) + { + // + // obtain descriptor from entry + // + Descriptor = (PQUEUE_TRANSFER_DESCRIPTOR)CONTAINING_RECORD(Entry, QUEUE_TRANSFER_DESCRIPTOR, DescriptorEntry); + ASSERT(Descriptor); + if (Descriptor->Token.Bits.Active) + { + // + // descriptor is still active + // + return FALSE; + } + + // + // move to next entry + // + Entry = Entry->Flink; + } + + DPRINT("QueueHead %p Addr %x is complete\n", QueueHead, QueueHead->PhysicalAddr); + + // + // no active descriptors found, queue head is finished + // + return TRUE; +} + +//----------------------------------------------------------------------------------------- +VOID +CUSBRequest::GetTransferBuffer( + OUT PMDL * OutMDL, + OUT PULONG TransferLength) +{ + // sanity checks + PC_ASSERT(OutMDL); + PC_ASSERT(TransferLength); + + *OutMDL = m_TransferBufferMDL; + *TransferLength = m_TransferBufferLength; +} +//----------------------------------------------------------------------------------------- +ULONG +CUSBRequest::InternalCalculateTransferLength() +{ + if (!m_Irp) + { + // + // FIXME: get length for control request + // + return m_TransferBufferLength; + } + + // + // sanity check + // + ASSERT(m_EndpointDescriptor); + if (USB_ENDPOINT_DIRECTION_IN(m_EndpointDescriptor->EndPointDescriptor.bEndpointAddress)) + { + // + // bulk in request + // HACK: Properly determine transfer length + // + return m_TransferBufferLength;//m_TotalBytesTransferred; + } + + // + // bulk out transfer + // + return m_TransferBufferLength; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +InternalCreateUSBRequest( + PUSBREQUEST *OutRequest) +{ + PUSBREQUEST This; + + // + // allocate requests + // + This = new(NonPagedPool, TAG_USBEHCI) CUSBRequest(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutRequest = (PUSBREQUEST)This; + + // + // done + // + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/usb/usbehci/usbehci.cpp b/reactos/drivers/usb/usbehci/usbehci.cpp new file mode 100644 index 00000000000..2793eee8927 --- /dev/null +++ b/reactos/drivers/usb/usbehci/usbehci.cpp @@ -0,0 +1,144 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbehci/usbehci.cpp + * PURPOSE: USB EHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbehci.h" + +// +// driver verifier +// +DRIVER_ADD_DEVICE EHCI_AddDevice; + +NTSTATUS +NTAPI +EHCI_AddDevice( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT PhysicalDeviceObject) +{ + NTSTATUS Status; + PHCDCONTROLLER HcdController; + + DPRINT1("EHCI_AddDevice\n"); + + /* first create the controller object */ + Status = CreateHCDController(&HcdController); + if (!NT_SUCCESS(Status)) + { + /* failed to create hcd */ + DPRINT1("AddDevice: Failed to create hcd with %x\n", Status); + return Status; + } + + /* initialize the hcd */ + Status = HcdController->Initialize(NULL, // FIXME + DriverObject, + PhysicalDeviceObject); + + /* check for success */ + if (!NT_SUCCESS(Status)) + { + /* failed to initialize device */ + DPRINT1("AddDevice: failed to initialize\n"); + + /* release object */ + HcdController->Release(); + } + + return Status; + +} + +NTSTATUS +NTAPI +EHCI_Dispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + + // + // get common device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // sanity checks + // + PC_ASSERT(DeviceExtension->Dispatcher); + + switch(IoStack->MajorFunction) + { + case IRP_MJ_PNP: + { + // + // dispatch pnp + // + return DeviceExtension->Dispatcher->HandlePnp(DeviceObject, Irp); + } + + case IRP_MJ_POWER: + { + // + // dispatch pnp + // + return DeviceExtension->Dispatcher->HandlePower(DeviceObject, Irp); + } + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + case IRP_MJ_DEVICE_CONTROL: + { + // + // dispatch pnp + // + return DeviceExtension->Dispatcher->HandleDeviceControl(DeviceObject, Irp); + } + default: + { + DPRINT1("EHCI_Dispatch> Major %lu Minor %lu unhandeled\n", IoStack->MajorFunction, IoStack->MinorFunction); + Status = STATUS_SUCCESS; + } + } + + // + // complete request + // + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} + +extern +"C" +NTSTATUS +NTAPI +DriverEntry( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath) +{ + + /* initialize driver object*/ + DriverObject->DriverExtension->AddDevice = EHCI_AddDevice; + + DriverObject->MajorFunction[IRP_MJ_CREATE] = EHCI_Dispatch; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = EHCI_Dispatch; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = EHCI_Dispatch; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = EHCI_Dispatch; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = EHCI_Dispatch; + DriverObject->MajorFunction[IRP_MJ_PNP] = EHCI_Dispatch; + + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/usb/usbehci/usbehci.h b/reactos/drivers/usb/usbehci/usbehci.h index 09e29479111..8b05ef37f19 100644 --- a/reactos/drivers/usb/usbehci/usbehci.h +++ b/reactos/drivers/usb/usbehci/usbehci.h @@ -1,239 +1,125 @@ -#pragma once +#ifndef USBEHCI_H__ +#define USBEHCI_H__ -#include "hardware.h" -#include #include -#include #define NDEBUG #include #include +#include #include +// +// FIXME: +// #include +// #include +#include +#include -#define USB_POOL_TAG (ULONG)'ebsu' +// +// FIXME: +// the following includes are required to get kcom to compile +// +#include +#include +#include -#define DEVICEINTIALIZED 0x01 -#define DEVICESTARTED 0x02 -#define DEVICEBUSY 0x04 -#define DEVICESTOPPED 0x08 -#define DEVICESTALLED 0x10 -#define DEVICEREMOVED 0x20 +#include "interfaces.h" - -#define MAX_USB_DEVICES 127 -#define EHCI_MAX_SIZE_TRANSFER 0x100000 - -#define C_HUB_LOCAL_POWER 0 -#define C_HUB_OVER_CURRENT 1 -#define PORT_CONNECTION 0 +// +// flags for handling USB_REQUEST_SET_FEATURE / USB_REQUEST_GET_FEATURE +// #define PORT_ENABLE 1 #define PORT_SUSPEND 2 #define PORT_OVER_CURRENT 3 #define PORT_RESET 4 #define PORT_POWER 8 -#define PORT_LOW_SPEED 9 -#define PORT_HIGH_SPEED 9 #define C_PORT_CONNECTION 16 #define C_PORT_ENABLE 17 #define C_PORT_SUSPEND 18 #define C_PORT_OVER_CURRENT 19 #define C_PORT_RESET 20 -#define PORT_TEST 21 -#define PORT_INDICATOR 22 -#define USB_PORT_STATUS_CHANGE 0x4000 typedef struct _USB_ENDPOINT { - ULONG Flags; - LIST_ENTRY UrbList; - struct _USB_INTERFACE *Interface; USB_ENDPOINT_DESCRIPTOR EndPointDescriptor; + UCHAR HubAddress; + UCHAR HubPort; + UCHAR DataToggle; } USB_ENDPOINT, *PUSB_ENDPOINT; typedef struct _USB_INTERFACE { - struct _USB_CONFIGURATION *Config; USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; - USB_ENDPOINT *EndPoints[]; + USB_ENDPOINT *EndPoints; } USB_INTERFACE, *PUSB_INTERFACE; typedef struct _USB_CONFIGURATION { - struct _USB_DEVICE *Device; USB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; - USB_INTERFACE *Interfaces[]; + USB_INTERFACE *Interfaces; } USB_CONFIGURATION, *PUSB_CONFIGURATION; -typedef struct _USB_DEVICE + + +typedef struct { - UCHAR Address; - ULONG Port; - PVOID ParentDevice; - BOOLEAN IsHub; - USB_DEVICE_SPEED DeviceSpeed; - USB_DEVICE_TYPE DeviceType; - ULONG DeviceState; - PDEVICE_OBJECT UsbDevicePdo; - USB_DEVICE_DESCRIPTOR DeviceDescriptor; - UNICODE_STRING LanguageIDs; - UNICODE_STRING iManufacturer; - UNICODE_STRING iProduct; - UNICODE_STRING iSerialNumber; - USB_CONFIGURATION *ActiveConfig; - USB_INTERFACE *ActiveInterface; - USB_CONFIGURATION **Configs; -} USB_DEVICE, *PUSB_DEVICE; + BOOLEAN IsFDO; // is device a FDO or PDO + BOOLEAN IsHub; // is device a hub / child - not yet used + PDISPATCHIRP Dispatcher; // dispatches the code +}COMMON_DEVICE_EXTENSION, *PCOMMON_DEVICE_EXTENSION; + +// +// tag for allocations +// +#define TAG_USBEHCI 'ICHE' + +// +// assert for c++ - taken from portcls +// +#define PC_ASSERT(exp) \ + (VOID)((!(exp)) ? \ + RtlAssert((PVOID) #exp, (PVOID)__FILE__, __LINE__, NULL ), FALSE : TRUE) + +// +// hcd_controller.cpp +// +NTSTATUS CreateHCDController(PHCDCONTROLLER *HcdController); + +// +// hardware.cpp +// +NTSTATUS CreateUSBHardware(PUSBHARDWAREDEVICE *OutHardware); + +// +// misc.cpp +// +NTSTATUS NTAPI SyncForwardIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS NTAPI GetBusInterface(PDEVICE_OBJECT DeviceObject, PBUS_INTERFACE_STANDARD busInterface); + +// +// root_hub_controller.cpp +// +NTSTATUS CreateHubController(PHUBCONTROLLER * OutHubController); + +// +// memory_manager.cpp +// +NTSTATUS CreateDMAMemoryManager(PDMAMEMORYMANAGER *OutMemoryManager); +// +// usb_device.cpp +// +NTSTATUS CreateUSBDevice(PUSBDEVICE *OutDevice); -typedef struct _COMMON_DEVICE_EXTENSION -{ - BOOLEAN IsFdo; - PDRIVER_OBJECT DriverObject; - PDEVICE_OBJECT DeviceObject; -} COMMON_DEVICE_EXTENSION, *PCOMMON_DEVICE_EXTENSION; +// +// usb_queue.cpp +// +NTSTATUS CreateUSBQueue(PUSBQUEUE *OutUsbQueue); -typedef struct _FDO_DEVICE_EXTENSION -{ - COMMON_DEVICE_EXTENSION Common; - PDRIVER_OBJECT DriverObject; - PDEVICE_OBJECT DeviceObject; - PDEVICE_OBJECT LowerDevice; - PDEVICE_OBJECT Pdo; - ULONG DeviceState; - PVOID RootHubDeviceHandle; - ULONG Vector; - KIRQL Irql; - - KTIMER UpdateTimer; - KINTERRUPT_MODE Mode; - BOOLEAN IrqShared; - PKINTERRUPT EhciInterrupt; - KDPC DpcObject; - KDPC TimerDpcObject; - - KAFFINITY Affinity; - - ULONG BusNumber; - ULONG BusAddress; - ULONG PCIAddress; - USHORT VendorId; - USHORT DeviceId; - - BUS_INTERFACE_STANDARD BusInterface; - - union - { - ULONG ResourcePort; - ULONG ResourceMemory; - }; - - EHCI_HOST_CONTROLLER hcd; - PERIODICFRAMELIST PeriodicFrameList; - - FAST_MUTEX FrameListMutex; - - BOOLEAN AsyncComplete; - -} FDO_DEVICE_EXTENSION, *PFDO_DEVICE_EXTENSION; - -typedef struct _PDO_DEVICE_EXTENSION -{ - COMMON_DEVICE_EXTENSION Common; - PDEVICE_OBJECT DeviceObject; - PDEVICE_OBJECT ControllerFdo; - PUSB_DEVICE UsbDevices[127]; - LIST_ENTRY IrpQueue; - KSPIN_LOCK IrpQueueLock; - PIRP CurrentIrp; - HANDLE ThreadHandle; - ULONG ChildDeviceCount; - BOOLEAN HaltQueue; - PVOID CallbackContext; - RH_INIT_CALLBACK *CallbackRoutine; - USB_IDLE_CALLBACK IdleCallback; - PVOID IdleContext; - KTIMER Timer; - KEVENT QueueDrainedEvent; - FAST_MUTEX ListLock; -} PDO_DEVICE_EXTENSION, *PPDO_DEVICE_EXTENSION; - -typedef struct _WORKITEMDATA -{ - WORK_QUEUE_ITEM WorkItem; - PVOID Context; -} WORKITEMDATA, *PWORKITEMDATA; - -VOID NTAPI -UrbWorkerThread(PVOID Context); - -NTSTATUS NTAPI -GetBusInterface(PDEVICE_OBJECT pcifido, PBUS_INTERFACE_STANDARD busInterface); - -NTSTATUS NTAPI -ForwardAndWaitCompletionRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp, PKEVENT Event); - -NTSTATUS NTAPI -ForwardAndWait(PDEVICE_OBJECT DeviceObject, PIRP Irp); - -NTSTATUS NTAPI -ForwardIrpAndForget(PDEVICE_OBJECT DeviceObject,PIRP Irp); - -NTSTATUS NTAPI -FdoDispatchPnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); - -NTSTATUS NTAPI -PdoDispatchPnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); - -NTSTATUS NTAPI -AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT Pdo); - -NTSTATUS -DuplicateUnicodeString(ULONG Flags, PCUNICODE_STRING SourceString, PUNICODE_STRING DestinationString); - -PWSTR -GetSymbolicName(PDEVICE_OBJECT DeviceObject); - -PWSTR -GetPhysicalDeviceObjectName(PDEVICE_OBJECT DeviceObject); - -NTSTATUS NTAPI -PdoDispatchInternalDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp); - -NTSTATUS NTAPI -FdoDispatchInternalDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp); - -USBD_STATUS -ExecuteControlRequest(PFDO_DEVICE_EXTENSION DeviceExtension, PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, UCHAR Address, ULONG Port, PVOID Buffer, ULONG BufferLength); - -VOID -RequestURBCancel (PPDO_DEVICE_EXTENSION DeviceExtension, PIRP Irp); - -VOID -RemoveUrbRequest(PPDO_DEVICE_EXTENSION PdoDeviceExtension, PIRP Irp); - -VOID -QueueURBRequest(PPDO_DEVICE_EXTENSION DeviceExtension, PIRP Irp); - -VOID -CompletePendingURBRequest(PPDO_DEVICE_EXTENSION DeviceExtension); - -NTSTATUS -HandleUrbRequest(PPDO_DEVICE_EXTENSION DeviceExtension, PIRP Irp); - -PUSB_DEVICE -DeviceHandleToUsbDevice(PPDO_DEVICE_EXTENSION PdoDeviceExtension, PUSB_DEVICE_HANDLE DeviceHandle); - -VOID -DumpDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor); - -VOID -DumpFullConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor); - - -VOID -DumpTransferDescriptor(PQUEUE_TRANSFER_DESCRIPTOR TransferDescriptor); - -VOID -DumpQueueHead(PQUEUE_HEAD QueueHead); +// +// usb_request.cpp +// +NTSTATUS InternalCreateUSBRequest(PUSBREQUEST *OutRequest); +#endif diff --git a/reactos/drivers/usb/usbehci/usbehci.rbuild b/reactos/drivers/usb/usbehci/usbehci.rbuild index f30b683d498..63d39433c95 100644 --- a/reactos/drivers/usb/usbehci/usbehci.rbuild +++ b/reactos/drivers/usb/usbehci/usbehci.rbuild @@ -1,18 +1,22 @@ + + 0x600 ntoskrnl hal - usbehci.c - hardware.c - physmem.c - hwiface.c - transfer.c - fdo.c - pdo.c - common.c - misc.c - irp.c - usbiffn.c + pseh + usbehci.cpp + usb_device.cpp + usb_request.cpp + usb_queue.cpp + hcd_controller.cpp + hardware.cpp + misc.cpp + purecall.cpp + hub_controller.cpp + memory_manager.cpp usbehci.rc + -fno-rtti + -fno-exceptions diff --git a/reactos/drivers/usb/usbehci/usbehci.rc b/reactos/drivers/usb/usbehci/usbehci.rc index 1211ed481ea..7ddce2e638f 100644 --- a/reactos/drivers/usb/usbehci/usbehci.rc +++ b/reactos/drivers/usb/usbehci/usbehci.rc @@ -1,5 +1,5 @@ #define REACTOS_VERSION_DLL -#define REACTOS_STR_FILE_DESCRIPTION "USB Ehci Driver\0" +#define REACTOS_STR_FILE_DESCRIPTION "USBEHCI Driver API\0" #define REACTOS_STR_INTERNAL_NAME "usbehci\0" #define REACTOS_STR_ORIGINAL_FILENAME "usbehci.sys\0" #include diff --git a/reactos/drivers/usb/usbhub/CMakeLists.txt b/reactos/drivers/usb/usbhub/CMakeLists.txt index 60c165f4e35..a33f9dbdc8d 100644 --- a/reactos/drivers/usb/usbhub/CMakeLists.txt +++ b/reactos/drivers/usb/usbhub/CMakeLists.txt @@ -1,15 +1,13 @@ -list(APPEND SOURCE - createclose.c - fdo.c - misc.c - pdo.c - usbhub.c - usbhub.rc) +add_definitions(-DDEBUG_MODE) -add_library(usbhub SHARED ${SOURCE}) +include_directories(${REACTOS_SOURCE_DIR}/ntoskrnl/include) + +add_library(usbhub SHARED fdo.c misc.c pdo.c usbhub.c usbhub.rc) + +target_link_libraries(usbhub ${PSEH_LIB}) set_module_type(usbhub kernelmodedriver) add_importlibs(usbhub ntoskrnl hal usbd) -add_cab_target(usbhub 2) +add_cd_file(TARGET usbhub DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/reactos/drivers/usb/usbhub/fdo.c b/reactos/drivers/usb/usbhub/fdo.c index bea0e09e65f..6333d1e19c4 100644 --- a/reactos/drivers/usb/usbhub/fdo.c +++ b/reactos/drivers/usb/usbhub/fdo.c @@ -1,557 +1,38 @@ -/* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: USB hub driver - * FILE: drivers/usb/cromwell/hub/fdo.c - * PURPOSE: IRP_MJ_PNP operations for FDOs - * - * PROGRAMMERS: Herv� Poussineau (hpoussin@reactos.com) +/* + * PROJECT: ReactOS Universal Serial Bus Hub Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbhub/fdo.c + * PURPOSE: Handle FDO + * PROGRAMMERS: * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) */ #define INITGUID -#include -#define NDEBUG #include "usbhub.h" -#include "usbdlib.h" - -#define IO_METHOD_FROM_CTL_CODE(ctlCode) (ctlCode&0x00000003) - -typedef struct _PORTSTATUSANDCHANGE -{ - USHORT Status; - USHORT Change; -} PORTSTATUSANDCHANGE, *PPORTSTATUSANDCHANGE; NTSTATUS -QueryRootHub(IN PDEVICE_OBJECT Pdo, IN ULONG IoControlCode, OUT PVOID OutParameter1, OUT PVOID OutParameter2); -NTSTATUS -WaitForUsbDeviceArrivalNotification(PDEVICE_OBJECT DeviceObject); -NTSTATUS -SubmitUrbToRootHub(IN PDEVICE_OBJECT Pdo, IN ULONG IoControlCode, IN PURB Urb); - -VOID DumpDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) -{ - DPRINT1("Dumping Device Descriptor %x\n", DeviceDescriptor); - DPRINT1("bLength %x\n", DeviceDescriptor->bLength); - DPRINT1("bDescriptorType %x\n", DeviceDescriptor->bDescriptorType); - DPRINT1("bcdUSB %x\n", DeviceDescriptor->bcdUSB); - DPRINT1("bDeviceClass %x\n", DeviceDescriptor->bDeviceClass); - DPRINT1("bDeviceSubClass %x\n", DeviceDescriptor->bDeviceSubClass); - DPRINT1("bDeviceProtocol %x\n", DeviceDescriptor->bDeviceProtocol); - DPRINT1("bMaxPacketSize0 %x\n", DeviceDescriptor->bMaxPacketSize0); - DPRINT1("idVendor %x\n", DeviceDescriptor->idVendor); - DPRINT1("idProduct %x\n", DeviceDescriptor->idProduct); - DPRINT1("bcdDevice %x\n", DeviceDescriptor->bcdDevice); - DPRINT1("iManufacturer %x\n", DeviceDescriptor->iManufacturer); - DPRINT1("iProduct %x\n", DeviceDescriptor->iProduct); - DPRINT1("iSerialNumber %x\n", DeviceDescriptor->iSerialNumber); - DPRINT1("bNumConfigurations %x\n", DeviceDescriptor->bNumConfigurations); -} - -VOID DumpFullConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) -{ - PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; - PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; - LONG i, j; - - DPRINT1("Duming ConfigurationDescriptor %x\n", ConfigurationDescriptor); - DPRINT1("bLength %x\n", ConfigurationDescriptor->bLength); - DPRINT1("bDescriptorType %x\n", ConfigurationDescriptor->bDescriptorType); - DPRINT1("wTotalLength %x\n", ConfigurationDescriptor->wTotalLength); - DPRINT1("bNumInterfaces %x\n", ConfigurationDescriptor->bNumInterfaces); - DPRINT1("bConfigurationValue %x\n", ConfigurationDescriptor->bConfigurationValue); - DPRINT1("iConfiguration %x\n", ConfigurationDescriptor->iConfiguration); - DPRINT1("bmAttributes %x\n", ConfigurationDescriptor->bmAttributes); - DPRINT1("MaxPower %x\n", ConfigurationDescriptor->MaxPower); - - InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR) ((ULONG_PTR)ConfigurationDescriptor + sizeof(USB_CONFIGURATION_DESCRIPTOR)); - - for (i=0; i < ConfigurationDescriptor->bNumInterfaces; i++) - { - DPRINT1("- Dumping InterfaceDescriptor %x\n", InterfaceDescriptor); - DPRINT1(" bLength %x\n", InterfaceDescriptor->bLength); - DPRINT1(" bDescriptorType %x\n", InterfaceDescriptor->bDescriptorType); - DPRINT1(" bInterfaceNumber %x\n", InterfaceDescriptor->bInterfaceNumber); - DPRINT1(" bAlternateSetting %x\n", InterfaceDescriptor->bAlternateSetting); - DPRINT1(" bNumEndpoints %x\n", InterfaceDescriptor->bNumEndpoints); - DPRINT1(" bInterfaceClass %x\n", InterfaceDescriptor->bInterfaceClass); - DPRINT1(" bInterfaceSubClass %x\n", InterfaceDescriptor->bInterfaceSubClass); - DPRINT1(" bInterfaceProtocol %x\n", InterfaceDescriptor->bInterfaceProtocol); - DPRINT1(" iInterface %x\n", InterfaceDescriptor->iInterface); - - EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) ((ULONG_PTR)InterfaceDescriptor + sizeof(USB_INTERFACE_DESCRIPTOR)); - - for (j=0; j < InterfaceDescriptor->bNumEndpoints; j++) - { - DPRINT1(" bLength %x\n", EndpointDescriptor->bLength); - DPRINT1(" bDescriptorType %x\n", EndpointDescriptor->bDescriptorType); - DPRINT1(" bEndpointAddress %x\n", EndpointDescriptor->bEndpointAddress); - DPRINT1(" bmAttributes %x\n", EndpointDescriptor->bmAttributes); - DPRINT1(" wMaxPacketSize %x\n", EndpointDescriptor->wMaxPacketSize); - DPRINT1(" bInterval %x\n", EndpointDescriptor->bInterval); - EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) ((ULONG_PTR)EndpointDescriptor + sizeof(USB_ENDPOINT_DESCRIPTOR)); - } - InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)(ULONG_PTR)EndpointDescriptor; - } - -} - -VOID DumpInterfaceInfo(PUSBD_INTERFACE_INFORMATION InterfaceInformation) -{ - PUSBD_PIPE_INFORMATION PipeInformation; - ULONG i; - - DPRINT1("IntefaceLenth %x\n",InterfaceInformation->Length); - DPRINT1("InterfaceNumber %x\n",InterfaceInformation->InterfaceNumber); - DPRINT1("AlternateSetting %x\n",InterfaceInformation->AlternateSetting); - DPRINT1("Class %x\n",InterfaceInformation->Class); - DPRINT1("SubClass %x\n",InterfaceInformation->SubClass); - DPRINT1("Protocol %x\n",InterfaceInformation->Protocol); - DPRINT1("Reserved %x\n",InterfaceInformation->Reserved); - DPRINT1("InterfaceHandle %x\n",InterfaceInformation->InterfaceHandle); - DPRINT1("NumberOfPipes %x\n", InterfaceInformation->NumberOfPipes); - - PipeInformation = &InterfaceInformation->Pipes[0]; - - for (i = 0; i < InterfaceInformation->NumberOfPipes; i++) - { - - DPRINT1("MaximumPacketSize %x\n", PipeInformation->MaximumPacketSize); - DPRINT1("EndpointAddress %x\n", PipeInformation->EndpointAddress); - DPRINT1("Interval %x\n", PipeInformation->Interval); - DPRINT1("PipeType %x\n", PipeInformation->PipeType); - DPRINT1("PipeHandle %x\n", PipeInformation->PipeHandle); - DPRINT1("PipeFlags %x\n", PipeInformation->PipeFlags); - DPRINT1("MaximumTransferSize %x\n", PipeInformation->MaximumTransferSize); - PipeInformation = (PUSBD_PIPE_INFORMATION)((ULONG_PTR)PipeInformation + sizeof(USBD_PIPE_INFORMATION)); - } -} - - -VOID NTAPI -WorkerThread(IN PVOID Context) -{ - PHUB_DEVICE_EXTENSION DeviceExtension; - PDEVICE_OBJECT DeviceObject, Pdo; - PHUB_CHILDDEVICE_EXTENSION PdoExtension; - PURB Urb = NULL; - PORTSTATUSANDCHANGE PortStatusAndChange; - int PortLoop, DeviceCount; - NTSTATUS Status; - USB_DEVICE_DESCRIPTOR DevDesc; - USB_CONFIGURATION_DESCRIPTOR ConfigDesc; - ULONG DevDescSize, ConfigDescSize; - PUSB_STRING_DESCRIPTOR StringDesc; - USB_STRING_DESCRIPTOR LanguageIdDescriptor; - PWORKITEMDATA WorkItemData = (PWORKITEMDATA)Context; - - DeviceObject = (PDEVICE_OBJECT)WorkItemData->Context; - - DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; - - /* Determine where in the children array to store this device info */ - for (DeviceCount = 0; DeviceCount < USB_MAXCHILDREN; DeviceCount++) - { - if (DeviceExtension->UsbChildren[DeviceCount] == NULL) - { - break; - } - } - - Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG); - if (!Urb) - { - DPRINT1("Failed to allocate memory for URB!\n"); - ASSERT(FALSE); - } - - RtlZeroMemory(Urb, sizeof(URB)); - - for (PortLoop = 0; PortLoop < DeviceExtension->UsbExtHubInfo.NumberOfPorts; PortLoop++) - { - /* Get the port status */ - UsbBuildVendorRequest(Urb, - URB_FUNCTION_CLASS_OTHER, - sizeof(Urb->UrbControlVendorClassRequest), - USBD_TRANSFER_DIRECTION_OUT, - 0, - USB_REQUEST_GET_STATUS, - 0, - PortLoop + 1, - &PortStatusAndChange, - 0, - sizeof(PORTSTATUSANDCHANGE), - 0); - - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to get PortStatus!\n"); - goto CleanUp; - } - - DPRINT("Notification Port %x:\n", PortLoop + 1); - DPRINT("Status %x\n", PortStatusAndChange.Status); - DPRINT("Change %x\n", PortStatusAndChange.Change); - - if (PortStatusAndChange.Change == USB_PORT_STATUS_RESET) - { - /* Clear the Reset */ - UsbBuildVendorRequest(Urb, - URB_FUNCTION_CLASS_OTHER, - sizeof(Urb->UrbControlVendorClassRequest), - USBD_TRANSFER_DIRECTION_IN, - 0, - USB_REQUEST_CLEAR_FEATURE, - C_PORT_RESET, - PortLoop + 1, - &PortStatusAndChange, - 0, - sizeof(PORTSTATUSANDCHANGE), - 0); - - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to Clear the Port Reset with Status %x!\n", Status); - goto CleanUp; - } - - UsbBuildVendorRequest(Urb, URB_FUNCTION_CLASS_OTHER, - sizeof(Urb->UrbControlVendorClassRequest), - USBD_TRANSFER_DIRECTION_OUT, - 0, - USB_REQUEST_GET_STATUS, - 0, - PortLoop + 1, - &PortStatusAndChange, - 0, - sizeof(PORTSTATUSANDCHANGE), - 0); - - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); - - DPRINT("Status %x\n", PortStatusAndChange.Status); - DPRINT("Change %x\n", PortStatusAndChange.Change); - - /* Create the UsbDevice */ - Status = DeviceExtension->HubInterface.CreateUsbDevice(DeviceExtension->RootHubPdo, - (PVOID)&DeviceExtension->UsbChildren[DeviceCount], - DeviceExtension->RootHubUsbDevice, - PortStatusAndChange.Status, - PortLoop + 1); - DPRINT1("CreateUsbDevice Status %x\n", Status); - - Status = DeviceExtension->HubInterface.InitializeUsbDevice(DeviceExtension->RootHubPdo, DeviceExtension->UsbChildren[DeviceCount]); - DPRINT1("InitializeUsbDevice Status %x\n", Status); - - DevDescSize = sizeof(USB_DEVICE_DESCRIPTOR); - ConfigDescSize = sizeof(USB_CONFIGURATION_DESCRIPTOR); - Status = DeviceExtension->HubInterface.GetUsbDescriptors(DeviceExtension->RootHubPdo, - DeviceExtension->UsbChildren[DeviceCount], - (PUCHAR)&DevDesc, - &DevDescSize, - (PUCHAR)&ConfigDesc, - &ConfigDescSize); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to Get Usb Deccriptors %x!\n", Status); - } - - DumpDeviceDescriptor(&DevDesc); - - Status = IoCreateDevice(DeviceObject->DriverObject, - sizeof(HUB_CHILDDEVICE_EXTENSION), - NULL, - FILE_DEVICE_CONTROLLER, - FILE_AUTOGENERATED_DEVICE_NAME, - FALSE, - &DeviceExtension->Children[DeviceCount]); - - if (!NT_SUCCESS(Status)) - { - DPRINT1("UsbHub; IoCreateDevice failed with status %x\n",Status); - goto CleanUp; - } - - Pdo = DeviceExtension->Children[DeviceCount]; - DPRINT1("Created New Device with %x\n", Pdo); - Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE; - - PdoExtension = Pdo->DeviceExtension; - - RtlZeroMemory(PdoExtension, sizeof(HUB_CHILDDEVICE_EXTENSION)); - - PdoExtension->DeviceId = ExAllocatePoolWithTag(NonPagedPool, 32 * sizeof(WCHAR), USB_HUB_TAG); - RtlZeroMemory(PdoExtension->DeviceId, 32 * sizeof(WCHAR)); - swprintf(PdoExtension->DeviceId, L"USB\\Vid_%04x&Pid_%04x", DevDesc.idVendor, DevDesc.idProduct); - - - /* Get the LANGids */ - RtlZeroMemory(&LanguageIdDescriptor, sizeof(USB_STRING_DESCRIPTOR)); - UsbBuildGetDescriptorRequest(Urb, - sizeof(Urb->UrbControlDescriptorRequest), - USB_STRING_DESCRIPTOR_TYPE, - 0, - 0, - &LanguageIdDescriptor, - NULL, - sizeof(USB_STRING_DESCRIPTOR), - NULL); - - Urb->UrbHeader.UsbdDeviceHandle = DeviceExtension->UsbChildren[DeviceCount]; - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); - - /* Get the length of the SerialNumber */ - StringDesc = ExAllocatePoolWithTag(PagedPool, 64, USB_HUB_TAG); - RtlZeroMemory(StringDesc, 64); - StringDesc->bLength = 0; - StringDesc->bDescriptorType = 0; - - UsbBuildGetDescriptorRequest(Urb, - sizeof(Urb->UrbControlDescriptorRequest), - USB_STRING_DESCRIPTOR_TYPE, - DevDesc.iSerialNumber, - LanguageIdDescriptor.bString[0], - StringDesc, - NULL, - 64, - NULL); - - Urb->UrbHeader.UsbdDeviceHandle = DeviceExtension->UsbChildren[DeviceCount]; - - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); - - PdoExtension->InstanceId = ExAllocatePoolWithTag(NonPagedPool, (StringDesc->bLength + 1) * sizeof(WCHAR), USB_HUB_TAG); - DPRINT1("PdoExtension->InstanceId %x\n",PdoExtension->InstanceId); - - RtlZeroMemory(PdoExtension->InstanceId, (StringDesc->bLength + 1) * sizeof(WCHAR)); - RtlCopyMemory(PdoExtension->InstanceId, &StringDesc->bString[0], StringDesc->bLength); - DPRINT1("------>SerialNumber %S\n", PdoExtension->InstanceId); - - - - RtlZeroMemory(StringDesc, 64); - StringDesc->bLength = 0; - StringDesc->bDescriptorType = 0; - - UsbBuildGetDescriptorRequest(Urb, - sizeof(Urb->UrbControlDescriptorRequest), - USB_STRING_DESCRIPTOR_TYPE, - DevDesc.iProduct, - LanguageIdDescriptor.bString[0], - StringDesc, - NULL, - 64, - NULL); - - Urb->UrbHeader.UsbdDeviceHandle = DeviceExtension->UsbChildren[DeviceCount]; - - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); - - PdoExtension->TextDescription = ExAllocatePoolWithTag(NonPagedPool, (StringDesc->bLength + 1) * sizeof(WCHAR), USB_HUB_TAG); - - RtlZeroMemory(PdoExtension->TextDescription, (StringDesc->bLength + 1) * sizeof(WCHAR)); - RtlCopyMemory(PdoExtension->TextDescription, &StringDesc->bString[0], StringDesc->bLength); - ExFreePool(StringDesc); - DPRINT1("------>TextDescription %S\n", PdoExtension->TextDescription); - - PdoExtension->IsFDO = FALSE; - PdoExtension->Parent = DeviceObject; - Pdo->Flags &= ~DO_DEVICE_INITIALIZING; - - ExFreePool(WorkItemData); - ExFreePool(Urb); - - IoInvalidateDeviceRelations(DeviceExtension->RootHubPdo, BusRelations); - return; - } - - /* Is a device connected to this port */ - if (PortStatusAndChange.Change == USB_PORT_STATUS_CONNECT) - { - /* Clear the Connect from ProtChange */ - UsbBuildVendorRequest(Urb, - URB_FUNCTION_CLASS_OTHER, - sizeof(Urb->UrbControlVendorClassRequest), - USBD_TRANSFER_DIRECTION_IN, - 0, - USB_REQUEST_CLEAR_FEATURE, - C_PORT_CONNECTION, - PortLoop + 1, - &PortStatusAndChange, - 0, - sizeof(PORTSTATUSANDCHANGE), - 0); - - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to Clear the Port Connect!\n"); - goto CleanUp; - } - - /* Send the miniport controller a SCE request so when the port resets we can be informed */ - WaitForUsbDeviceArrivalNotification(DeviceObject); - - UsbBuildVendorRequest(Urb, - URB_FUNCTION_CLASS_OTHER, - sizeof(Urb->UrbControlVendorClassRequest), - USBD_TRANSFER_DIRECTION_IN, - 0, - USB_REQUEST_SET_FEATURE, - PORT_RESET, - PortLoop + 1, - &PortStatusAndChange, - 0, - sizeof(PORTSTATUSANDCHANGE), - 0); - - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); - - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to Reset the port!\n"); - goto CleanUp; - } - /* At this point the miniport will complete another SCE to inform of Reset completed */ - } - } - -CleanUp: - ExFreePool(WorkItemData); - ExFreePool(Urb); -} +QueryStatusChangeEndpoint( + IN PDEVICE_OBJECT DeviceObject); NTSTATUS -DeviceArrivalCompletion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context) -{ - PHUB_DEVICE_EXTENSION DeviceExtension; - LONG i; - PWORKITEMDATA WorkItemData; - - DeviceExtension = (PHUB_DEVICE_EXTENSION)((PDEVICE_OBJECT)Context)->DeviceExtension; - - for (i=0; i < DeviceExtension->UsbExtHubInfo.NumberOfPorts; i++) - DPRINT1("Port %x DeviceExtension->PortStatus %x\n",i+1, DeviceExtension->PortStatus[i]); - - IoFreeIrp(Irp); - - WorkItemData = ExAllocatePool(NonPagedPool, sizeof(WORKITEMDATA)); - if (!WorkItemData) - { - DPRINT1("Failed to allocate memory\n"); - return STATUS_NO_MEMORY; - } - - - RtlZeroMemory(WorkItemData, sizeof(WORKITEMDATA)); - WorkItemData->Context = Context; - - ExInitializeWorkItem(&WorkItemData->WorkItem, (PWORKER_THREAD_ROUTINE)WorkerThread, (PVOID)WorkItemData); - ExQueueWorkItem(&WorkItemData->WorkItem, DelayedWorkQueue); - return STATUS_MORE_PROCESSING_REQUIRED; -} - +CreateUsbChildDeviceObject( + IN PDEVICE_OBJECT UsbHubDeviceObject, + IN LONG PortId, + OUT PDEVICE_OBJECT *UsbChildDeviceObject, + IN ULONG PortStatus); NTSTATUS -WaitForUsbDeviceArrivalNotification(PDEVICE_OBJECT DeviceObject) -{ - PURB Urb; - PIRP Irp; - NTSTATUS Status; - PIO_STACK_LOCATION Stack = NULL; - PHUB_DEVICE_EXTENSION DeviceExtension; - - DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; - - Urb = &DeviceExtension->Urb; - - RtlZeroMemory(Urb, sizeof(URB)); - - /* Send URB to the miniports Status Change Endpoint SCE */ - UsbBuildInterruptOrBulkTransferRequest(Urb, - sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), - DeviceExtension->PipeHandle, - &DeviceExtension->PortStatus, - NULL, - sizeof(ULONG) * 2, - USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK, - NULL); - - Urb->UrbHeader.UsbdDeviceHandle = DeviceExtension->RootHubUsbDevice; - - Irp = IoAllocateIrp(DeviceExtension->RootHubPdo->StackSize, FALSE); - - if (Irp == NULL) - { - DPRINT("Usbhub: IoBuildDeviceIoControlRequest() failed\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - - Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; - Irp->IoStatus.Information = 0; - Irp->Flags = 0; - Irp->UserBuffer = NULL; - - Stack = IoGetCurrentIrpStackLocation(Irp); - Stack->DeviceObject = DeviceExtension->RootHubPdo; - - Stack = IoGetNextIrpStackLocation(Irp); - Stack->DeviceObject = DeviceExtension->RootHubPdo; - Stack->Parameters.Others.Argument1 = Urb; - Stack->Parameters.Others.Argument2 = NULL; - Stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; - Stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; - - //IoSetCompletionRoutineEx(DeviceExtension->RootHubPdo, Irp, (PIO_COMPLETION_ROUTINE)DeviceArrivalCompletion, DeviceObject, TRUE, TRUE, TRUE); - IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE)DeviceArrivalCompletion, DeviceObject, TRUE, TRUE, TRUE); - - Status = IoCallDriver(DeviceExtension->RootHubPdo, Irp); - DPRINT1("SCE request status %x\n", Status); - - return STATUS_PENDING; -} +DestroyUsbChildDeviceObject( + IN PDEVICE_OBJECT UsbHubDeviceObject, + IN LONG PortId); NTSTATUS -SubmitUrbToRootHub(IN PDEVICE_OBJECT Pdo, IN ULONG IoControlCode, IN PURB Urb) -{ - PIRP Irp; - IO_STATUS_BLOCK IoStatus; - NTSTATUS Status; - PIO_STACK_LOCATION Stack = NULL; - - Irp = IoBuildDeviceIoControlRequest(IoControlCode, - Pdo, - NULL, 0, - NULL, 0, - TRUE, - NULL, - &IoStatus); - - if (Irp == NULL) - { - DPRINT("Usbhub: IoBuildDeviceIoControlRequest() failed\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - /* Initialize the status block before sending the IRP */ - IoStatus.Status = STATUS_NOT_SUPPORTED; - IoStatus.Information = 0; - - Stack = IoGetNextIrpStackLocation(Irp); - - Stack->Parameters.Others.Argument1 = Urb; - Stack->Parameters.Others.Argument2 = NULL; - - Status = IoCallDriver(Pdo, Irp); - - return Status; -} - -NTSTATUS -QueryRootHub(IN PDEVICE_OBJECT Pdo, IN ULONG IoControlCode, OUT PVOID OutParameter1, OUT PVOID OutParameter2) +SubmitRequestToRootHub( + IN PDEVICE_OBJECT RootHubDeviceObject, + IN ULONG IoControlCode, + OUT PVOID OutParameter1, + OUT PVOID OutParameter2) { KEVENT Event; PIRP Irp; @@ -561,8 +42,11 @@ QueryRootHub(IN PDEVICE_OBJECT Pdo, IN ULONG IoControlCode, OUT PVOID OutParamet KeInitializeEvent(&Event, NotificationEvent, FALSE); + // + // Build Control Request + // Irp = IoBuildDeviceIoControlRequest(IoControlCode, - Pdo, + RootHubDeviceObject, NULL, 0, NULL, 0, TRUE, @@ -575,28 +59,528 @@ QueryRootHub(IN PDEVICE_OBJECT Pdo, IN ULONG IoControlCode, OUT PVOID OutParamet return STATUS_INSUFFICIENT_RESOURCES; } - /* Initialize the status block before sending the IRP */ + // + // Initialize the status block before sending the IRP + // IoStatus.Status = STATUS_NOT_SUPPORTED; IoStatus.Information = 0; + // + // Get Next Stack Location and Initialize it + // Stack = IoGetNextIrpStackLocation(Irp); - Stack->Parameters.Others.Argument1 = OutParameter1; Stack->Parameters.Others.Argument2 = OutParameter2; - Status = IoCallDriver(Pdo, Irp); + // + // Call RootHub + // + Status = IoCallDriver(RootHubDeviceObject, Irp); - if (Status == STATUS_PENDING) + // + // Its ok to block here as this function is called in an nonarbitrary thread + // + if (Status == STATUS_PENDING) { - DPRINT1("Usbhub: Operation pending\n"); KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL); Status = IoStatus.Status; } + // + // The IO Manager will free the IRP + // + return Status; } -NTSTATUS QueryInterface(IN PDEVICE_OBJECT Pdo, IN CONST GUID InterfaceType, IN LONG Size, IN LONG Version, OUT PVOID Interface) +NTSTATUS +GetPortStatusAndChange( + IN PDEVICE_OBJECT RootHubDeviceObject, + IN ULONG PortId, + OUT PPORT_STATUS_CHANGE StatusChange) +{ + NTSTATUS Status; + PURB Urb; + + // + // Allocate URB + // + Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG); + if (!Urb) + { + DPRINT1("Failed to allocate memory for URB!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Zero it + // + RtlZeroMemory(Urb, sizeof(URB)); + + // + // Initialize URB for getting Port Status + // + UsbBuildVendorRequest(Urb, + URB_FUNCTION_CLASS_OTHER, + sizeof(Urb->UrbControlVendorClassRequest), + USBD_TRANSFER_DIRECTION_OUT, + 0, + USB_REQUEST_GET_STATUS, + 0, + PortId, + StatusChange, + 0, + sizeof(PORT_STATUS_CHANGE), + 0); + + // + // Query the Root Hub + // + Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); + + // + // Free URB + // + ExFreePool(Urb); + + return Status; +} + +NTSTATUS +SetPortFeature( + IN PDEVICE_OBJECT RootHubDeviceObject, + IN ULONG PortId, + IN ULONG Feature) +{ + NTSTATUS Status; + PURB Urb; + + // + // Allocate URB + // + Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG); + if (!Urb) + { + DPRINT1("Failed to allocate memory for URB!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Zero it + // + RtlZeroMemory(Urb, sizeof(URB)); + + // + // Initialize URB for Clearing Port Reset + // + UsbBuildVendorRequest(Urb, + URB_FUNCTION_CLASS_OTHER, + sizeof(Urb->UrbControlVendorClassRequest), + USBD_TRANSFER_DIRECTION_IN, + 0, + USB_REQUEST_SET_FEATURE, + Feature, + PortId, + NULL, + 0, + 0, + 0); + // + // Query the Root Hub + // + Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); + + // + // Free URB + // + ExFreePool(Urb); + + return Status; +} + +NTSTATUS +ClearPortFeature( + IN PDEVICE_OBJECT RootHubDeviceObject, + IN ULONG PortId, + IN ULONG Feature) +{ + NTSTATUS Status; + PURB Urb; + + // + // Allocate a URB + // + Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG); + if (!Urb) + { + DPRINT1("Failed to allocate memory for URB!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Zero it + // + RtlZeroMemory(Urb, sizeof(URB)); + + // + // Initialize URB for Clearing Port Reset + // + UsbBuildVendorRequest(Urb, + URB_FUNCTION_CLASS_OTHER, + sizeof(Urb->UrbControlVendorClassRequest), + USBD_TRANSFER_DIRECTION_IN, + 0, + USB_REQUEST_CLEAR_FEATURE, + Feature, + PortId, + NULL, + 0, + 0, + 0); + // + // Query the Root Hub + // + Status = SubmitRequestToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); + + // + // Free URB + // + ExFreePool(Urb); + + return Status; +} + +VOID NTAPI +DeviceStatusChangeThread( + IN PVOID Context) +{ + NTSTATUS Status; + PDEVICE_OBJECT DeviceObject, RootHubDeviceObject; + PHUB_DEVICE_EXTENSION HubDeviceExtension; + PWORK_ITEM_DATA WorkItemData; + PORT_STATUS_CHANGE PortStatus; + LONG PortId; + BOOLEAN SignalResetComplete = FALSE; + + DPRINT("Entered DeviceStatusChangeThread, Context %x\n", Context); + + WorkItemData = (PWORK_ITEM_DATA)Context; + DeviceObject = (PDEVICE_OBJECT)WorkItemData->Context; + HubDeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject; + // + // Loop all ports + // + for (PortId = 1; PortId <= HubDeviceExtension->UsbExtHubInfo.NumberOfPorts; PortId++) + { + // + // Get Port Status + // + Status = GetPortStatusAndChange(RootHubDeviceObject, PortId, &PortStatus); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to get port status for port %d, Status %x\n", PortId, Status); + // FIXME: Do we really want to halt further SCE requests? + return; + } + + DPRINT1("Port %d Status %x\n", PortId, PortStatus.Status); + DPRINT1("Port %d Change %x\n", PortId, PortStatus.Change); + + + // + // Check for new device connection + // + if (PortStatus.Change & USB_PORT_STATUS_CONNECT) + { + // + // Clear Port Connect + // + Status = ClearPortFeature(RootHubDeviceObject, PortId, C_PORT_CONNECTION); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to clear connection change for port %d\n", PortId); + } + + // + // Is this a connect or disconnect? + // + if (!(PortStatus.Status & USB_PORT_STATUS_CONNECT)) + { + DPRINT1("Device disconnected from port %d\n", PortId); + + Status = DestroyUsbChildDeviceObject(DeviceObject, PortId); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to delete child device object after disconnect\n"); + } + } + else + { + DPRINT1("Device connected from port %d\n", PortId); + + // No SCE completion done for clearing C_PORT_CONNECT + + // + // Reset Port + // + Status = SetPortFeature(RootHubDeviceObject, PortId, PORT_RESET); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to reset port %d\n", PortId); + } + } + } + else if (PortStatus.Change & USB_PORT_STATUS_ENABLE) + { + // + // Clear Enable + // + Status = ClearPortFeature(RootHubDeviceObject, PortId, C_PORT_ENABLE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to clear enable change on port %d\n", PortId); + } + } + else if (PortStatus.Change & USB_PORT_STATUS_RESET) + { + // + // Clear Reset + // + Status = ClearPortFeature(RootHubDeviceObject, PortId, C_PORT_RESET); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to clear reset change on port %d\n", PortId); + } + + // + // Get Port Status + // + Status = GetPortStatusAndChange(RootHubDeviceObject, PortId, &PortStatus); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to get port status for port %d, Status %x\n", PortId, Status); + // FIXME: Do we really want to halt further SCE requests? + return; + } + + DPRINT1("Port %d Status %x\n", PortId, PortStatus.Status); + DPRINT1("Port %d Change %x\n", PortId, PortStatus.Change); + + // + // Check that reset was cleared + // + if(PortStatus.Change & USB_PORT_STATUS_RESET) + { + DPRINT1("Port did not clear reset! Possible Hardware problem!\n"); + } + + // + // Check if the device is still connected + // + if (!(PortStatus.Status & USB_PORT_STATUS_CONNECT)) + { + DPRINT1("Device has been disconnected\n"); + continue; + } + + // + // Make sure its Connected and Enabled + // + if (!(PortStatus.Status & (USB_PORT_STATUS_CONNECT | USB_PORT_STATUS_ENABLE))) + { + DPRINT1("Usb Device is not connected and enabled!\n"); + // + // Attempt another reset + // + Status = SetPortFeature(RootHubDeviceObject, PortId, PORT_RESET); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to reset port %d\n", PortId); + } + continue; + } + + // + // This is a new device + // + Status = CreateUsbChildDeviceObject(DeviceObject, PortId, NULL, PortStatus.Status); + + // + // Request event signalling later + // + SignalResetComplete = TRUE; + } + } + + ExFreePool(WorkItemData); + + // + // Send another SCE Request + // + DPRINT("Sending another SCE!\n"); + QueryStatusChangeEndpoint(DeviceObject); + + // + // Check if a reset event was satisfied + // + if (SignalResetComplete) + { + // + // Signal anyone waiting on it + // + KeSetEvent(&HubDeviceExtension->ResetComplete, IO_NO_INCREMENT, FALSE); + } +} + +NTSTATUS +NTAPI +StatusChangeEndpointCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context) +{ + PDEVICE_OBJECT RealDeviceObject; + PHUB_DEVICE_EXTENSION HubDeviceExtension; + PWORK_ITEM_DATA WorkItemData; + + RealDeviceObject = (PDEVICE_OBJECT)Context; + HubDeviceExtension = (PHUB_DEVICE_EXTENSION)RealDeviceObject->DeviceExtension; + + // + // NOTE: USBPORT frees this IRP + // + DPRINT("Received Irp %x, HubDeviceExtension->PendingSCEIrp %x\n", Irp, HubDeviceExtension->PendingSCEIrp); + //IoFreeIrp(Irp); + + // + // Create and initialize work item data + // + WorkItemData = ExAllocatePoolWithTag(NonPagedPool, sizeof(WORK_ITEM_DATA), USB_HUB_TAG); + if (!WorkItemData) + { + DPRINT1("Failed to allocate memory!n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + WorkItemData->Context = RealDeviceObject; + + DPRINT("Queuing work item\n"); + + // + // Queue the work item to handle initializing the device + // + ExInitializeWorkItem(&WorkItemData->WorkItem, DeviceStatusChangeThread, (PVOID)WorkItemData); + ExQueueWorkItem(&WorkItemData->WorkItem, DelayedWorkQueue); + + // + // Return more processing required so the IO Manger doesn’t try to mess with IRP just freed + // + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +QueryStatusChangeEndpoint( + IN PDEVICE_OBJECT DeviceObject) +{ + NTSTATUS Status; + PDEVICE_OBJECT RootHubDeviceObject; + PIO_STACK_LOCATION Stack; + PHUB_DEVICE_EXTENSION HubDeviceExtension; + PURB PendingSCEUrb; + + HubDeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject; + + // + // Allocate a URB + // + PendingSCEUrb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG); + + // + // Initialize URB for Status Change Endpoint request + // + UsbBuildInterruptOrBulkTransferRequest(PendingSCEUrb, + sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), + HubDeviceExtension->PipeHandle, + HubDeviceExtension->PortStatusChange, + NULL, + sizeof(USHORT) * 2 * HubDeviceExtension->UsbExtHubInfo.NumberOfPorts, + USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK, + NULL); + + // + // Set the device handle to null for roothub + // + PendingSCEUrb->UrbHeader.UsbdDeviceHandle = NULL;//HubDeviceExtension->RootHubHandle; + + // + // Allocate an Irp + // + + HubDeviceExtension->PendingSCEIrp = ExAllocatePoolWithTag(NonPagedPool, + IoSizeOfIrp(RootHubDeviceObject->StackSize), + USB_HUB_TAG); +/* + HubDeviceExtension->PendingSCEIrp = IoAllocateIrp(RootHubDeviceObject->StackSize, + FALSE); +*/ + DPRINT("Allocated IRP %x\n", HubDeviceExtension->PendingSCEIrp); + + if (!HubDeviceExtension->PendingSCEIrp) + { + DPRINT1("USBHUB: Failed to allocate IRP for SCE request!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the IRP + // + IoInitializeIrp(HubDeviceExtension->PendingSCEIrp, + IoSizeOfIrp(RootHubDeviceObject->StackSize), + RootHubDeviceObject->StackSize); + + HubDeviceExtension->PendingSCEIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; + HubDeviceExtension->PendingSCEIrp->IoStatus.Information = 0; + HubDeviceExtension->PendingSCEIrp->Flags = 0; + HubDeviceExtension->PendingSCEIrp->UserBuffer = NULL; + + // + // Get the Next Stack Location and Initialize it + // + Stack = IoGetNextIrpStackLocation(HubDeviceExtension->PendingSCEIrp); + Stack->DeviceObject = DeviceObject; + Stack->Parameters.Others.Argument1 = PendingSCEUrb; + Stack->Parameters.Others.Argument2 = NULL; + Stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + Stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; + + // + // Set the completion routine for when device is connected to root hub + // + IoSetCompletionRoutine(HubDeviceExtension->PendingSCEIrp, + StatusChangeEndpointCompletion, + DeviceObject, + TRUE, + TRUE, + TRUE); + + // + // Send to RootHub + // + DPRINT("DeviceObject is %x\n", DeviceObject); + DPRINT("Iocalldriver %x with irp %x\n", RootHubDeviceObject, HubDeviceExtension->PendingSCEIrp); + Status = IoCallDriver(RootHubDeviceObject, HubDeviceExtension->PendingSCEIrp); + + return STATUS_PENDING; +} + +NTSTATUS +QueryInterface( + IN PDEVICE_OBJECT DeviceObject, + IN CONST GUID InterfaceType, + IN LONG Size, + IN LONG Version, + OUT PVOID Interface) { KEVENT Event; PIRP Irp; @@ -604,16 +588,25 @@ NTSTATUS QueryInterface(IN PDEVICE_OBJECT Pdo, IN CONST GUID InterfaceType, IN L NTSTATUS Status; PIO_STACK_LOCATION Stack = NULL; + // + // Initialize the Event used to wait for Irp completion + // KeInitializeEvent(&Event, NotificationEvent, FALSE); + // + // Build Control Request + // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, - Pdo, + DeviceObject, NULL, 0, NULL, &Event, &IoStatus); + // + // Get Next Stack Location and Initialize it. + // Stack = IoGetNextIrpStackLocation(Irp); Stack->MinorFunction = IRP_MN_QUERY_INTERFACE; Stack->Parameters.QueryInterface.InterfaceType= &InterfaceType;//USB_BUS_INTERFACE_HUB_GUID; @@ -622,11 +615,11 @@ NTSTATUS QueryInterface(IN PDEVICE_OBJECT Pdo, IN CONST GUID InterfaceType, IN L Stack->Parameters.QueryInterface.Interface = Interface; Stack->Parameters.QueryInterface.InterfaceSpecificData = NULL; - Status = IoCallDriver(Pdo, Irp); + Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { - DPRINT("Usbhub: Operation pending\n"); + DPRINT("Operation pending\n"); KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL); Status = IoStatus.Status; } @@ -634,63 +627,789 @@ NTSTATUS QueryInterface(IN PDEVICE_OBJECT Pdo, IN CONST GUID InterfaceType, IN L return Status; } -static VOID -UsbhubGetUserBuffers(IN PIRP Irp, IN ULONG IoControlCode, OUT PVOID* BufferIn, OUT PVOID* BufferOut) +NTSTATUS +GetUsbDeviceDescriptor( + IN PDEVICE_OBJECT ChildDeviceObject, + IN UCHAR DescriptorType, + IN UCHAR Index, + IN USHORT LangId, + OUT PVOID TransferBuffer, + IN ULONG TransferBufferLength) { - ASSERT(Irp); - ASSERT(BufferIn); - ASSERT(BufferOut); + NTSTATUS Status; + PDEVICE_OBJECT RootHubDeviceObject; + PURB Urb; + PHUB_DEVICE_EXTENSION HubDeviceExtension; + PHUB_CHILDDEVICE_EXTENSION ChildDeviceExtension; - switch (IO_METHOD_FROM_CTL_CODE(IoControlCode)) + // + // Get the Hubs Device Extension + // + ChildDeviceExtension = (PHUB_CHILDDEVICE_EXTENSION)ChildDeviceObject->DeviceExtension; + HubDeviceExtension = (PHUB_DEVICE_EXTENSION) ChildDeviceExtension->ParentDeviceObject->DeviceExtension; + RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject; + + // + // Allocate a URB + // + Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB), USB_HUB_TAG); + if (!Urb) { - case METHOD_BUFFERED: - *BufferIn = *BufferOut = Irp->AssociatedIrp.SystemBuffer; - break; - case METHOD_IN_DIRECT: - case METHOD_OUT_DIRECT: - *BufferIn = Irp->AssociatedIrp.SystemBuffer; - *BufferOut = MmGetSystemAddressForMdl(Irp->MdlAddress); - break; - case METHOD_NEITHER: - *BufferIn = IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.Type3InputBuffer; - *BufferOut = Irp->UserBuffer; - break; - default: - /* Should never happen */ - *BufferIn = NULL; - *BufferOut = NULL; - break; + DPRINT1("Failed to allocate memory for URB!\n"); + return STATUS_INSUFFICIENT_RESOURCES; } + + // + // Zero it + // + RtlZeroMemory(Urb, sizeof(URB)); + + // + // Initialize URB for getting device descriptor + // + UsbBuildGetDescriptorRequest(Urb, + sizeof(Urb->UrbControlDescriptorRequest), + DescriptorType, + Index, + LangId, + TransferBuffer, + NULL, + TransferBufferLength, + NULL); + + // + // Set the device handle + // + Urb->UrbHeader.UsbdDeviceHandle = (PVOID)ChildDeviceExtension->UsbDeviceHandle; + + // + // Query the Root Hub + // + Status = SubmitRequestToRootHub(RootHubDeviceObject, + IOCTL_INTERNAL_USB_SUBMIT_URB, + Urb, + NULL); + + return Status; } NTSTATUS -UsbhubFdoQueryBusRelations(IN PDEVICE_OBJECT DeviceObject, OUT PDEVICE_RELATIONS* pDeviceRelations) +GetUsbStringDescriptor( + IN PDEVICE_OBJECT ChildDeviceObject, + IN UCHAR Index, + IN USHORT LangId, + OUT PVOID *TransferBuffer, + OUT USHORT *Size) { - PHUB_DEVICE_EXTENSION DeviceExtension; + NTSTATUS Status; + PUSB_STRING_DESCRIPTOR StringDesc = NULL; + ULONG SizeNeeded; + LPWSTR Buffer; + + StringDesc = ExAllocatePoolWithTag(NonPagedPool, + sizeof(USB_STRING_DESCRIPTOR), + USB_HUB_TAG); + if (!StringDesc) + { + DPRINT1("Failed to allocate buffer for string!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Get the index string descriptor length + // FIXME: Implement LangIds + // + Status = GetUsbDeviceDescriptor(ChildDeviceObject, + USB_STRING_DESCRIPTOR_TYPE, + Index, + 0x0409, + StringDesc, + sizeof(USB_STRING_DESCRIPTOR)); + if (!NT_SUCCESS(Status)) + { + DPRINT1("GetUsbDeviceDescriptor failed with status %x\n", Status); + ExFreePool(StringDesc); + return Status; + } + DPRINT1("StringDesc->bLength %d\n", StringDesc->bLength); + + // + // Did we get something more than the length of the first two fields of structure? + // + if (StringDesc->bLength == 2) + { + DPRINT1("USB Device Error!\n"); + ExFreePool(StringDesc); + return STATUS_DEVICE_DATA_ERROR; + } + SizeNeeded = StringDesc->bLength + sizeof(WCHAR); + + // + // Free String + // + ExFreePool(StringDesc); + + // + // Recreate with appropriate size + // + StringDesc = ExAllocatePoolWithTag(NonPagedPool, + SizeNeeded, + USB_HUB_TAG); + if (!StringDesc) + { + DPRINT1("Failed to allocate buffer for string!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(StringDesc, SizeNeeded); + + // + // Get the string + // + Status = GetUsbDeviceDescriptor(ChildDeviceObject, + USB_STRING_DESCRIPTOR_TYPE, + Index, + 0x0409, + StringDesc, + SizeNeeded); + if (!NT_SUCCESS(Status)) + { + DPRINT1("GetUsbDeviceDescriptor failed with status %x\n", Status); + ExFreePool(StringDesc); + return Status; + } + + // + // Allocate Buffer to return + // + Buffer = ExAllocatePoolWithTag(NonPagedPool, + SizeNeeded, + USB_HUB_TAG); + if (!Buffer) + { + DPRINT1("Failed to allocate buffer for string!\n"); + ExFreePool(StringDesc); + return STATUS_INSUFFICIENT_RESOURCES; + } + DPRINT("Buffer %p\n", Buffer); + RtlZeroMemory(Buffer, SizeNeeded); + + DPRINT("SizeNeeded %lu\n", SizeNeeded); + DPRINT("Offset %lu\n", FIELD_OFFSET(USB_STRING_DESCRIPTOR, bLength)); + DPRINT("Length %lu\n", SizeNeeded - FIELD_OFFSET(USB_STRING_DESCRIPTOR, bLength)); + + // + // Copy the string to destination + // + RtlCopyMemory(Buffer, StringDesc->bString, SizeNeeded - FIELD_OFFSET(USB_STRING_DESCRIPTOR, bString)); + *Size = SizeNeeded; + *TransferBuffer = Buffer; + + ExFreePool(StringDesc); + + return STATUS_SUCCESS; +} + +ULONG +IsCompositeDevice( + IN PUSB_DEVICE_DESCRIPTOR DeviceDescriptor, + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) +{ + if (DeviceDescriptor->bNumConfigurations != 1) + { + // + // composite device must have only one configuration + // + DPRINT1("IsCompositeDevice bNumConfigurations %x\n", DeviceDescriptor->bNumConfigurations); + return FALSE; + } + + if (ConfigurationDescriptor->bNumInterfaces < 2) + { + // + // composite device must have multiple interfaces + // + DPRINT1("IsCompositeDevice bNumInterfaces %x\n", ConfigurationDescriptor->bNumInterfaces); + return FALSE; + } + + if (DeviceDescriptor->bDeviceClass == 0) + { + // + // composite device + // + ASSERT(DeviceDescriptor->bDeviceSubClass == 0); + ASSERT(DeviceDescriptor->bDeviceProtocol == 0); + DPRINT1("IsCompositeDevice: TRUE\n"); + return TRUE; + } + + if (DeviceDescriptor->bDeviceClass == 0xEF && + DeviceDescriptor->bDeviceSubClass == 0x02 && + DeviceDescriptor->bDeviceProtocol == 0x01) + { + // + // USB-IF association descriptor + // + DPRINT1("IsCompositeDevice: TRUE\n"); + return TRUE; + } + + DPRINT1("DeviceDescriptor bDeviceClass %x bDeviceSubClass %x bDeviceProtocol %x\n", DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass, DeviceDescriptor->bDeviceProtocol); + + // + // not a composite device + // + return FALSE; +} + +NTSTATUS +CreateDeviceIds( + PDEVICE_OBJECT UsbChildDeviceObject) +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG Index = 0; + LPWSTR DeviceString; + WCHAR Buffer[200]; + PHUB_CHILDDEVICE_EXTENSION UsbChildExtension; + PUSB_DEVICE_DESCRIPTOR DeviceDescriptor; + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + + // + // get child device extension + // + UsbChildExtension = (PHUB_CHILDDEVICE_EXTENSION)UsbChildDeviceObject->DeviceExtension; + + // + // get device descriptor + // + DeviceDescriptor = &UsbChildExtension->DeviceDesc; + + // + // get configuration descriptor + // + ConfigurationDescriptor = UsbChildExtension->FullConfigDesc; + + // + // use first interface descriptor available + // + InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor, ConfigurationDescriptor, 0, -1, -1, -1, -1); + ASSERT(InterfaceDescriptor); + + // + // Construct the CompatibleIds + // + if (IsCompositeDevice(DeviceDescriptor, ConfigurationDescriptor)) + { + // + // sanity checks + // + ASSERT(DeviceDescriptor->bNumConfigurations == 1); + ASSERT(ConfigurationDescriptor->bNumInterfaces > 1); + Index += swprintf(&Buffer[Index], + L"USB\\DevClass_%02x&SubClass_%02x&Prot_%02x", + DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass, DeviceDescriptor->bDeviceProtocol) + 1; + Index += swprintf(&Buffer[Index], + L"USB\\DevClass_%02x&SubClass_%02x", + DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass) + 1; + Index += swprintf(&Buffer[Index], + L"USB\\DevClass_%02x", + DeviceDescriptor->bDeviceClass) + 1; + Index += swprintf(&Buffer[Index], + L"USB\\COMPOSITE") + 1; + } + else + { + // + // sanity checks for simple usb device + // + ASSERT(ConfigurationDescriptor->bNumInterfaces == 1); + + // + // FIXME: support multiple configurations + // + ASSERT(DeviceDescriptor->bNumConfigurations == 1); + + if (DeviceDescriptor->bDeviceClass == 0) + { + Index += swprintf(&Buffer[Index], + L"USB\\Class_%02x&SubClass_%02x&Prot_%02x", + InterfaceDescriptor->bInterfaceClass, InterfaceDescriptor->bInterfaceSubClass, InterfaceDescriptor->bInterfaceProtocol) + 1; + Index += swprintf(&Buffer[Index], + L"USB\\Class_%02x&SubClass_%02x", + InterfaceDescriptor->bInterfaceClass, InterfaceDescriptor->bInterfaceSubClass) + 1; + Index += swprintf(&Buffer[Index], + L"USB\\Class_%02x", + InterfaceDescriptor->bInterfaceClass) + 1; + } + else + { + Index += swprintf(&Buffer[Index], + L"USB\\Class_%02x&SubClass_%02x&Prot_%02x", + DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass, DeviceDescriptor->bDeviceProtocol) + 1; + Index += swprintf(&Buffer[Index], + L"USB\\Class_%02x&SubClass_%02x", + DeviceDescriptor->bDeviceClass, DeviceDescriptor->bDeviceSubClass) + 1; + Index += swprintf(&Buffer[Index], + L"USB\\Class_%02x", + DeviceDescriptor->bDeviceClass) + 1; + } + } + + // + // now allocate the buffer + // + DeviceString = ExAllocatePool(NonPagedPool, (Index + 1) * sizeof(WCHAR)); + if (!DeviceString) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy buffer + // + RtlCopyMemory(DeviceString, Buffer, Index * sizeof(WCHAR)); + DeviceString[Index] = UNICODE_NULL; + UsbChildExtension->usCompatibleIds.Buffer = DeviceString; + UsbChildExtension->usCompatibleIds.Length = Index * sizeof(WCHAR); + UsbChildExtension->usCompatibleIds.MaximumLength = (Index + 1) * sizeof(WCHAR); + DPRINT("usCompatibleIds %wZ\n", &UsbChildExtension->usCompatibleIds); + + // + // Construct DeviceId string + // + Index = swprintf(Buffer, L"USB\\Vid_%04x&Pid_%04x", UsbChildExtension->DeviceDesc.idVendor, UsbChildExtension->DeviceDesc.idProduct) + 1; + + // + // now allocate the buffer + // + DeviceString = ExAllocatePool(NonPagedPool, Index * sizeof(WCHAR)); + if (!DeviceString) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy buffer + // + RtlCopyMemory(DeviceString, Buffer, Index * sizeof(WCHAR)); + UsbChildExtension->usDeviceId.Buffer = DeviceString; + UsbChildExtension->usDeviceId.Length = (Index-1) * sizeof(WCHAR); + UsbChildExtension->usDeviceId.MaximumLength = Index * sizeof(WCHAR); + DPRINT("usDeviceId %wZ\n", &UsbChildExtension->usDeviceId); + + // + // Construct HardwareIds + // + Index = 0; + Index += swprintf(&Buffer[Index], + L"USB\\Vid_%04x&Pid_%04x&Rev_%04x", + UsbChildExtension->DeviceDesc.idVendor, UsbChildExtension->DeviceDesc.idProduct, UsbChildExtension->DeviceDesc.bcdDevice) + 1; + Index += swprintf(&Buffer[Index], + L"USB\\Vid_%04x&Pid_%04x", + UsbChildExtension->DeviceDesc.idVendor, UsbChildExtension->DeviceDesc.idProduct) + 1; + + // + // now allocate the buffer + // + DeviceString = ExAllocatePool(NonPagedPool, (Index + 1) * sizeof(WCHAR)); + if (!DeviceString) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy buffer + // + RtlCopyMemory(DeviceString, Buffer, Index * sizeof(WCHAR)); + DeviceString[Index] = UNICODE_NULL; + UsbChildExtension->usHardwareIds.Buffer = DeviceString; + UsbChildExtension->usHardwareIds.Length = (Index + 1) * sizeof(WCHAR); + UsbChildExtension->usHardwareIds.MaximumLength = (Index + 1) * sizeof(WCHAR); + DPRINT("usHardWareIds %wZ\n", &UsbChildExtension->usHardwareIds); + + // + // FIXME: Handle Lang ids + // + + // + // Get the product string if obe provided + // + if (UsbChildExtension->DeviceDesc.iProduct) + { + Status = GetUsbStringDescriptor(UsbChildDeviceObject, + UsbChildExtension->DeviceDesc.iProduct, + 0, + (PVOID*)&UsbChildExtension->usTextDescription.Buffer, + &UsbChildExtension->usTextDescription.Length); + if (!NT_SUCCESS(Status)) + { + DPRINT1("USBHUB: GetUsbStringDescriptor failed with status %x\n", Status); + RtlInitUnicodeString(&UsbChildExtension->usTextDescription, L""); + } + else + { + UsbChildExtension->usTextDescription.MaximumLength = UsbChildExtension->usTextDescription.Length; + DPRINT("Usb TextDescription %wZ\n", &UsbChildExtension->usTextDescription); + } + } + + // + // Get the Serial Number string if obe provided + // + if (UsbChildExtension->DeviceDesc.iSerialNumber) + { + Status = GetUsbStringDescriptor(UsbChildDeviceObject, + UsbChildExtension->DeviceDesc.iSerialNumber, + 0, + (PVOID*)&UsbChildExtension->usInstanceId.Buffer, + &UsbChildExtension->usInstanceId.Length); + if (!NT_SUCCESS(Status)) + { + DPRINT1("USBHUB: GetUsbStringDescriptor failed with status %x\n", Status); + return Status; + } + + UsbChildExtension->usInstanceId.MaximumLength = UsbChildExtension->usInstanceId.Length; + DPRINT("Usb InstanceId %wZ\n", &UsbChildExtension->usInstanceId); + } + else + { + // + // the device did not provide a serial number, lets create a pseudo instance id + // + Index = swprintf(Buffer, L"0&%04d", UsbChildExtension->PortNumber) + 1; + UsbChildExtension->usInstanceId.Buffer = (LPWSTR)ExAllocatePool(NonPagedPool, Index * sizeof(WCHAR)); + if (UsbChildExtension->usInstanceId.Buffer == NULL) + { + DPRINT1("Error: failed to allocate %lu bytes\n", Index * sizeof(WCHAR)); + Status = STATUS_INSUFFICIENT_RESOURCES; + return Status; + } + + // + // copy instance id + // + RtlCopyMemory(UsbChildExtension->usInstanceId.Buffer, Buffer, Index * sizeof(WCHAR)); + UsbChildExtension->usInstanceId.Length = UsbChildExtension->usInstanceId.MaximumLength = Index * sizeof(WCHAR); + + DPRINT("usDeviceId %wZ\n", &UsbChildExtension->usInstanceId); + } + + return Status; +} + +NTSTATUS +DestroyUsbChildDeviceObject( + IN PDEVICE_OBJECT UsbHubDeviceObject, + IN LONG PortId) +{ + PHUB_DEVICE_EXTENSION HubDeviceExtension = (PHUB_DEVICE_EXTENSION)UsbHubDeviceObject->DeviceExtension; + PHUB_CHILDDEVICE_EXTENSION UsbChildExtension = NULL; + PDEVICE_OBJECT ChildDeviceObject = NULL; + ULONG Index = 0; + + DPRINT("Removing device on port %d (Child index: %d)\n", PortId, Index); + + for (Index = 0; Index < USB_MAXCHILDREN; Index++) + { + if (HubDeviceExtension->ChildDeviceObject[Index]) + { + UsbChildExtension = (PHUB_CHILDDEVICE_EXTENSION)HubDeviceExtension->ChildDeviceObject[Index]->DeviceExtension; + + /* Check if it matches the port ID */ + if (UsbChildExtension->PortNumber == PortId) + { + /* We found it */ + ChildDeviceObject = HubDeviceExtension->ChildDeviceObject[Index]; + break; + } + } + } + + /* Fail the request if the device doesn't exist */ + if (!ChildDeviceObject) + { + DPRINT1("Removal request for non-existant device!\n"); + return STATUS_UNSUCCESSFUL; + } + + /* Remove the device from the table */ + HubDeviceExtension->ChildDeviceObject[Index] = NULL; + + /* Invalidate device relations for the root hub */ + IoInvalidateDeviceRelations(HubDeviceExtension->RootHubPhysicalDeviceObject, BusRelations); + + /* The rest of the removal process takes place in IRP_MN_REMOVE_DEVICE handling for the PDO */ + return STATUS_SUCCESS; +} + +NTSTATUS +CreateUsbChildDeviceObject( + IN PDEVICE_OBJECT UsbHubDeviceObject, + IN LONG PortId, + OUT PDEVICE_OBJECT *UsbChildDeviceObject, + IN ULONG PortStatus) +{ + NTSTATUS Status; + PDEVICE_OBJECT RootHubDeviceObject, NewChildDeviceObject; + PHUB_DEVICE_EXTENSION HubDeviceExtension; + PHUB_CHILDDEVICE_EXTENSION UsbChildExtension; + PUSB_BUS_INTERFACE_HUB_V5 HubInterface; + ULONG ChildDeviceCount, UsbDeviceNumber = 0; + WCHAR CharDeviceName[64]; + UNICODE_STRING DeviceName; + ULONG ConfigDescSize, DeviceDescSize; + PVOID HubInterfaceBusContext; + USB_CONFIGURATION_DESCRIPTOR ConfigDesc; + + HubDeviceExtension = (PHUB_DEVICE_EXTENSION) UsbHubDeviceObject->DeviceExtension; + HubInterface = &HubDeviceExtension->HubInterface; + RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject; + HubInterfaceBusContext = HubDeviceExtension->UsbDInterface.BusContext; + // + // Find an empty slot in the child device array + // + for (ChildDeviceCount = 0; ChildDeviceCount < USB_MAXCHILDREN; ChildDeviceCount++) + { + if (HubDeviceExtension->ChildDeviceObject[ChildDeviceCount] == NULL) + { + DPRINT("Found unused entry at %d\n", ChildDeviceCount); + break; + } + } + + // + // Check if the limit has been reached for maximum usb devices + // + if (ChildDeviceCount == USB_MAXCHILDREN) + { + DPRINT1("USBHUB: Too many child devices!\n"); + return STATUS_UNSUCCESSFUL; + } + + while (TRUE) + { + // + // Create a Device Name + // + swprintf(CharDeviceName, L"\\Device\\USBPDO-%d", UsbDeviceNumber); + + // + // Initialize UnicodeString + // + RtlInitUnicodeString(&DeviceName, CharDeviceName); + + // + // Create a DeviceObject + // + Status = IoCreateDevice(UsbHubDeviceObject->DriverObject, + sizeof(HUB_CHILDDEVICE_EXTENSION), + NULL, + FILE_DEVICE_CONTROLLER, + FILE_AUTOGENERATED_DEVICE_NAME, + FALSE, + &NewChildDeviceObject); + + // + // Check if the name is already in use + // + if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION)) + { + // + // Try next name + // + UsbDeviceNumber++; + continue; + } + + // + // Check for other errors + // + if (!NT_SUCCESS(Status)) + { + DPRINT1("USBHUB: IoCreateDevice failed with status %x\n", Status); + return Status; + } + + DPRINT("USBHUB: Created Device %x\n", NewChildDeviceObject); + break; + } + + NewChildDeviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE; + + // + // Assign the device extensions + // + UsbChildExtension = (PHUB_CHILDDEVICE_EXTENSION)NewChildDeviceObject->DeviceExtension; + RtlZeroMemory(UsbChildExtension, sizeof(HUB_CHILDDEVICE_EXTENSION)); + UsbChildExtension->ParentDeviceObject = UsbHubDeviceObject; + UsbChildExtension->PortNumber = PortId; + + // + // Create the UsbDeviceObject + // + Status = HubInterface->CreateUsbDevice(HubInterfaceBusContext, + (PVOID)&UsbChildExtension->UsbDeviceHandle, + HubDeviceExtension->RootHubHandle, + PortStatus, + PortId); + if (!NT_SUCCESS(Status)) + { + DPRINT1("USBHUB: CreateUsbDevice failed with status %x\n", Status); + goto Cleanup; + } + + // + // Initialize UsbDevice + // + Status = HubInterface->InitializeUsbDevice(HubInterfaceBusContext, UsbChildExtension->UsbDeviceHandle); + if (!NT_SUCCESS(Status)) + { + DPRINT1("USBHUB: InitializeUsbDevice failed with status %x\n", Status); + goto Cleanup; + } + + DPRINT("Usb Device Handle %x\n", UsbChildExtension->UsbDeviceHandle); + + ConfigDescSize = sizeof(USB_CONFIGURATION_DESCRIPTOR); + DeviceDescSize = sizeof(USB_DEVICE_DESCRIPTOR); + + // + // Get the descriptors + // + Status = HubInterface->GetUsbDescriptors(HubInterfaceBusContext, + UsbChildExtension->UsbDeviceHandle, + (PUCHAR)&UsbChildExtension->DeviceDesc, + &DeviceDescSize, + (PUCHAR)&ConfigDesc, + &ConfigDescSize); + if (!NT_SUCCESS(Status)) + { + DPRINT1("USBHUB: GetUsbDescriptors failed with status %x\n", Status); + goto Cleanup; + } + + DumpDeviceDescriptor(&UsbChildExtension->DeviceDesc); + DumpConfigurationDescriptor(&ConfigDesc); + + // + // FIXME: Support more than one configuration and one interface? + // + if (UsbChildExtension->DeviceDesc.bNumConfigurations > 1) + { + DPRINT1("Warning: Device has more than one configuration. Only one configuration (the first) is supported!\n"); + } + + if (ConfigDesc.bNumInterfaces > 1) + { + DPRINT1("Warning: Device has more that one interface. Only one interface (the first) is currently supported\n"); + } + + ConfigDescSize = ConfigDesc.wTotalLength; + + // + // Allocate memory for the first full descriptor, including interfaces and endpoints. + // + UsbChildExtension->FullConfigDesc = ExAllocatePoolWithTag(PagedPool, ConfigDescSize, USB_HUB_TAG); + + // + // Retrieve the full configuration descriptor + // + Status = GetUsbDeviceDescriptor(NewChildDeviceObject, + USB_CONFIGURATION_DESCRIPTOR_TYPE, + 0, + 0, + UsbChildExtension->FullConfigDesc, + ConfigDescSize); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("USBHUB: GetUsbDeviceDescriptor failed with status %x\n", Status); + goto Cleanup; + } + + //DumpFullConfigurationDescriptor(UsbChildExtension->FullConfigDesc); + + // + // Construct all the strings that will described the device to PNP + // + Status = CreateDeviceIds(NewChildDeviceObject); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create strings needed to describe device to PNP.\n"); + goto Cleanup; + } + + HubDeviceExtension->ChildDeviceObject[ChildDeviceCount] = NewChildDeviceObject; + + IoInvalidateDeviceRelations(RootHubDeviceObject, BusRelations); + return STATUS_SUCCESS; + +Cleanup: + + // + // Remove the usb device if it was created + // + if (UsbChildExtension->UsbDeviceHandle) + HubInterface->RemoveUsbDevice(HubInterfaceBusContext, UsbChildExtension->UsbDeviceHandle, 0); + + // + // Free full configuration descriptor if one was allocated + // + if (UsbChildExtension->FullConfigDesc) + ExFreePool(UsbChildExtension->FullConfigDesc); + + // + // Delete the device object + // + IoDeleteDevice(NewChildDeviceObject); + return Status; +} + +NTSTATUS +USBHUB_FdoQueryBusRelations( + IN PDEVICE_OBJECT DeviceObject, + OUT PDEVICE_RELATIONS* pDeviceRelations) +{ + PHUB_DEVICE_EXTENSION HubDeviceExtension; PDEVICE_RELATIONS DeviceRelations; ULONG i; ULONG Children = 0; ULONG NeededSize; - DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; - DPRINT1("USBHUB: Query Bus Relations\n"); + HubDeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; - /* Create PDOs that are missing */ + // + // Count the number of children + // for (i = 0; i < USB_MAXCHILDREN; i++) { - if (DeviceExtension->UsbChildren[i] == NULL) + if (HubDeviceExtension->ChildDeviceObject[i] == NULL) { continue; } Children++; } - /* Fill returned structure */ NeededSize = sizeof(DEVICE_RELATIONS); if (Children > 1) NeededSize += (Children - 1) * sizeof(PDEVICE_OBJECT); + // + // Allocate DeviceRelations + // DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, NeededSize); @@ -699,69 +1418,138 @@ UsbhubFdoQueryBusRelations(IN PDEVICE_OBJECT DeviceObject, OUT PDEVICE_RELATIONS DeviceRelations->Count = Children; Children = 0; + // + // Fill in return structure + // for (i = 0; i < USB_MAXCHILDREN; i++) { - if (DeviceExtension->Children[i]) + if (HubDeviceExtension->ChildDeviceObject[i]) { - ObReferenceObject(DeviceExtension->Children[i]); - DeviceRelations->Objects[Children++] = DeviceExtension->Children[i]; + ObReferenceObject(HubDeviceExtension->ChildDeviceObject[i]); + HubDeviceExtension->ChildDeviceObject[i]->Flags &= ~DO_DEVICE_INITIALIZING; + DeviceRelations->Objects[Children++] = HubDeviceExtension->ChildDeviceObject[i]; } } ASSERT(Children == DeviceRelations->Count); *pDeviceRelations = DeviceRelations; - WaitForUsbDeviceArrivalNotification(DeviceObject); - return STATUS_SUCCESS; } -VOID CallBackRoutine(IN PVOID Argument1) +VOID +NTAPI +RootHubInitCallbackFunction( + PVOID Context) { - DPRINT1("RH_INIT_CALLBACK %x\n", Argument1); - ASSERT(FALSE); + PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context; + NTSTATUS Status; + ULONG PortId; + PHUB_DEVICE_EXTENSION HubDeviceExtension; + PORT_STATUS_CHANGE StatusChange; + + HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension; + + DPRINT("RootHubInitCallbackFunction Sending the initial SCE Request %x\n", DeviceObject); + + // + // Send the first SCE Request + // + QueryStatusChangeEndpoint(DeviceObject); + + for (PortId = 1; PortId <= HubDeviceExtension->HubDescriptor.bNumberOfPorts; PortId++) + { + // + // get port status + // + Status = GetPortStatusAndChange(HubDeviceExtension->RootHubPhysicalDeviceObject, PortId, &StatusChange); + if (NT_SUCCESS(Status)) + { + // + // is there a device connected + // + if (StatusChange.Status & USB_PORT_STATUS_CONNECT) + { + // + // reset port + // + Status = SetPortFeature(HubDeviceExtension->RootHubPhysicalDeviceObject, PortId, PORT_RESET); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to reset on port %d\n", PortId); + } + else + { + // + // wait for the reset to be handled since we want to enumerate synchronously + // + KeWaitForSingleObject(&HubDeviceExtension->ResetComplete, + Executive, + KernelMode, + FALSE, + NULL); + KeClearEvent(&HubDeviceExtension->ResetComplete); + } + } + } + } } -NTSTATUS NTAPI -UsbhubPnpFdo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) +NTSTATUS +USBHUB_FdoHandlePnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) { - PIO_STACK_LOCATION IrpSp; + PIO_STACK_LOCATION Stack; NTSTATUS Status = STATUS_SUCCESS; - ULONG MinorFunction; ULONG_PTR Information = 0; - PHUB_DEVICE_EXTENSION DeviceExtension; + PHUB_DEVICE_EXTENSION HubDeviceExtension; + PDEVICE_OBJECT RootHubDeviceObject; + PVOID HubInterfaceBusContext , UsbDInterfaceBusContext; + PORT_STATUS_CHANGE StatusChange; - IrpSp = IoGetCurrentIrpStackLocation(Irp); - MinorFunction = IrpSp->MinorFunction; + HubDeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension; - DeviceExtension = (PHUB_DEVICE_EXTENSION) DeviceObject->DeviceExtension; + Stack = IoGetCurrentIrpStackLocation(Irp); - switch (MinorFunction) + switch (Stack->MinorFunction) { - case IRP_MN_START_DEVICE: /* 0x0 */ + case IRP_MN_START_DEVICE: { PURB Urb; - ULONG Result = 0; PUSB_INTERFACE_DESCRIPTOR Pid; - /* Theres only one descriptor on hub */ + ULONG Result = 0, PortId; USBD_INTERFACE_LIST_ENTRY InterfaceList[2] = {{NULL, NULL}, {NULL, NULL}}; PURB ConfigUrb = NULL; + ULONG HubStatus; - /* We differ from windows on hubpdo because we dont have usbport.sys which manages all usb device objects */ - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_START_DEVICE\n"); + DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n"); - /* Allocating size including the sizeof USBD_INTERFACE_LIST_ENTRY */ + // + // Allocated size including the sizeof USBD_INTERFACE_LIST_ENTRY + // Urb = ExAllocatePoolWithTag(NonPagedPool, sizeof(URB) + sizeof(USBD_INTERFACE_LIST_ENTRY), USB_HUB_TAG); RtlZeroMemory(Urb, sizeof(URB) + sizeof(USBD_INTERFACE_LIST_ENTRY)); - /* Get the hubs PDO */ - QueryRootHub(DeviceExtension->LowerDevice, IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO, &DeviceExtension->RootHubPdo, &DeviceExtension->RootHubFdo); - ASSERT(DeviceExtension->RootHubPdo); - ASSERT(DeviceExtension->RootHubFdo); - DPRINT1("RootPdo %x, RootFdo %x\n", DeviceExtension->RootHubPdo, DeviceExtension->RootHubFdo); + // + // Get the Root Hub Pdo + // + SubmitRequestToRootHub(HubDeviceExtension->LowerDeviceObject, + IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO, + &HubDeviceExtension->RootHubPhysicalDeviceObject, + &HubDeviceExtension->RootHubFunctionalDeviceObject); - /* Send the START_DEVICE irp down to the PDO of RootHub */ - Status = ForwardIrpAndWait(DeviceExtension->RootHubPdo, Irp); + RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject; + ASSERT(HubDeviceExtension->RootHubPhysicalDeviceObject); + ASSERT(HubDeviceExtension->RootHubFunctionalDeviceObject); + DPRINT("RootPdo %x, RootFdo %x\n", + HubDeviceExtension->RootHubPhysicalDeviceObject, + HubDeviceExtension->RootHubFunctionalDeviceObject); + + // + // Send the StartDevice to RootHub + // + Status = ForwardIrpAndWait(RootHubDeviceObject, Irp); if (!NT_SUCCESS(Status)) { @@ -769,195 +1557,335 @@ UsbhubPnpFdo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) ASSERT(FALSE); } - /* Get the current number of hubs */ - QueryRootHub(DeviceExtension->RootHubPdo,IOCTL_INTERNAL_USB_GET_HUB_COUNT, &DeviceExtension->HubCount, NULL); + // + // Get the current number of hubs + // + Status = SubmitRequestToRootHub(RootHubDeviceObject, + IOCTL_INTERNAL_USB_GET_HUB_COUNT, + &HubDeviceExtension->NumberOfHubs, NULL); - /* Get the Direct Call Interfaces */ - Status = QueryInterface(DeviceExtension->RootHubPdo, + // + // Get the Hub Interface + // + Status = QueryInterface(RootHubDeviceObject, USB_BUS_INTERFACE_HUB_GUID, sizeof(USB_BUS_INTERFACE_HUB_V5), 5, - (PVOID)&DeviceExtension->HubInterface); + (PVOID)&HubDeviceExtension->HubInterface); + if (!NT_SUCCESS(Status)) { - DPRINT1("UsbhubM Failed to get HUB_GUID interface with status 0x%08lx\n", Status); + DPRINT1("Failed to get HUB_GUID interface with status 0x%08lx\n", Status); return STATUS_UNSUCCESSFUL; } - Status = QueryInterface(DeviceExtension->RootHubPdo, + HubInterfaceBusContext = HubDeviceExtension->HubInterface.BusContext; + + // + // Get the USBDI Interface + // + Status = QueryInterface(RootHubDeviceObject, USB_BUS_INTERFACE_USBDI_GUID, sizeof(USB_BUS_INTERFACE_USBDI_V2), 2, - (PVOID)&DeviceExtension->UsbDInterface); - if (!NT_SUCCESS(Status)) - { - DPRINT1("UsbhubM Failed to get USBDI_GUID interface with status 0x%08lx\n", Status); - return STATUS_UNSUCCESSFUL; - } + (PVOID)&HubDeviceExtension->UsbDInterface); - /* Get roothub device handle */ - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE, &DeviceExtension->RootHubUsbDevice, NULL); if (!NT_SUCCESS(Status)) { - DPRINT1("Usbhub: GetRootHubDeviceHandle failed with status 0x%08lx\n", Status); + DPRINT1("Failed to get USBDI_GUID interface with status 0x%08lx\n", Status); return Status; } - Status = DeviceExtension->HubInterface.QueryDeviceInformation(DeviceExtension->RootHubPdo, - DeviceExtension->RootHubUsbDevice, - &DeviceExtension->DeviceInformation, - sizeof(USB_DEVICE_INFORMATION_0), - &Result); + UsbDInterfaceBusContext = HubDeviceExtension->UsbDInterface.BusContext; + // + // Get Root Hub Device Handle + // + Status = SubmitRequestToRootHub(RootHubDeviceObject, + IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE, + &HubDeviceExtension->RootHubHandle, + NULL); - DPRINT("Status %x, Result %x\n", Status, Result); - DPRINT("InformationLevel %x\n", DeviceExtension->DeviceInformation.InformationLevel); - DPRINT("ActualLength %x\n", DeviceExtension->DeviceInformation.ActualLength); - DPRINT("PortNumber %x\n", DeviceExtension->DeviceInformation.PortNumber); - DPRINT("DeviceDescriptor %x\n", DeviceExtension->DeviceInformation.DeviceDescriptor); - DPRINT("HubAddress %x\n", DeviceExtension->DeviceInformation.HubAddress); - DPRINT("NumberofPipes %x\n", DeviceExtension->DeviceInformation.NumberOfOpenPipes); + if (!NT_SUCCESS(Status)) + { + DPRINT1("GetRootHubDeviceHandle failed with status 0x%08lx\n", Status); + return Status; + } - /* Get roothubs device descriptor */ + // + // Get Hub Device Information + // + Status = HubDeviceExtension->HubInterface.QueryDeviceInformation(HubInterfaceBusContext, + HubDeviceExtension->RootHubHandle, + &HubDeviceExtension->DeviceInformation, + sizeof(USB_DEVICE_INFORMATION_0), + &Result); + + DPRINT1("Status %x, Result 0x%08lx\n", Status, Result); + DPRINT1("InformationLevel %x\n", HubDeviceExtension->DeviceInformation.InformationLevel); + DPRINT1("ActualLength %x\n", HubDeviceExtension->DeviceInformation.ActualLength); + DPRINT1("PortNumber %x\n", HubDeviceExtension->DeviceInformation.PortNumber); + DPRINT1("DeviceDescriptor %x\n", HubDeviceExtension->DeviceInformation.DeviceDescriptor); + DPRINT1("HubAddress %x\n", HubDeviceExtension->DeviceInformation.HubAddress); + DPRINT1("NumberofPipes %x\n", HubDeviceExtension->DeviceInformation.NumberOfOpenPipes); + + // + // Get Root Hubs Device Descriptor + // UsbBuildGetDescriptorRequest(Urb, sizeof(Urb->UrbControlDescriptorRequest), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, - &DeviceExtension->HubDeviceDescriptor, + &HubDeviceExtension->HubDeviceDescriptor, NULL, sizeof(USB_DEVICE_DESCRIPTOR), NULL); - Urb->UrbHeader.UsbdDeviceHandle = DeviceExtension->RootHubUsbDevice; + Urb->UrbHeader.UsbdDeviceHandle = NULL;//HubDeviceExtension->RootHubHandle; + + Status = SubmitRequestToRootHub(RootHubDeviceObject, + IOCTL_INTERNAL_USB_SUBMIT_URB, + Urb, + NULL); - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); if (!NT_SUCCESS(Status)) { - DPRINT1("Usbhub: Failed to get HubDeviceDescriptor!\n"); + DPRINT1("Failed to get HubDeviceDescriptor!\n"); } - DumpDeviceDescriptor(&DeviceExtension->HubDeviceDescriptor); + DumpDeviceDescriptor(&HubDeviceExtension->HubDeviceDescriptor); - /* Get roothubs configuration descriptor */ + // + // Get Root Hubs Configuration Descriptor + // UsbBuildGetDescriptorRequest(Urb, sizeof(Urb->UrbControlDescriptorRequest), USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, - &DeviceExtension->HubConfigDescriptor, + &HubDeviceExtension->HubConfigDescriptor, NULL, sizeof(USB_CONFIGURATION_DESCRIPTOR) + sizeof(USB_INTERFACE_DESCRIPTOR) + sizeof(USB_ENDPOINT_DESCRIPTOR), NULL); - Urb->UrbHeader.UsbdDeviceHandle = DeviceExtension->RootHubUsbDevice; - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); + DPRINT("RootHub Handle %x\n", HubDeviceExtension->RootHubHandle); + Urb->UrbHeader.UsbdDeviceHandle = NULL;//HubDeviceExtension->RootHubHandle; + + Status = SubmitRequestToRootHub(RootHubDeviceObject, + IOCTL_INTERNAL_USB_SUBMIT_URB, + Urb, + NULL); if (!NT_SUCCESS(Status)) { - DPRINT1("Usbhub: Failed to get RootHub Configuration with status %x\n", Status); + DPRINT1("Failed to get RootHub Configuration with status %x\n", Status); ASSERT(FALSE); } - ASSERT(DeviceExtension->HubConfigDescriptor.wTotalLength); + ASSERT(HubDeviceExtension->HubConfigDescriptor.wTotalLength); - DumpFullConfigurationDescriptor(&DeviceExtension->HubConfigDescriptor); - //DPRINT1("DeviceExtension->HubConfigDescriptor.wTotalLength %x\n", DeviceExtension->HubConfigDescriptor.wTotalLength); + DumpConfigurationDescriptor(&HubDeviceExtension->HubConfigDescriptor); - Status = DeviceExtension->HubInterface.GetExtendedHubInformation(DeviceExtension->RootHubPdo, - DeviceExtension->RootHubPdo, - &DeviceExtension->UsbExtHubInfo, - sizeof(USB_EXTHUB_INFORMATION_0), - &Result); + Status = HubDeviceExtension->HubInterface.GetExtendedHubInformation(HubInterfaceBusContext, + RootHubDeviceObject, + &HubDeviceExtension->UsbExtHubInfo, + sizeof(USB_EXTHUB_INFORMATION_0), + &Result); if (!NT_SUCCESS(Status)) { - DPRINT1("Usbhub: Failed to extended hub information. Unable to determine the number of ports!\n"); + DPRINT1("Failed to extended hub information. Unable to determine the number of ports!\n"); ASSERT(FALSE); } - DPRINT1("DeviceExtension->UsbExtHubInfo.NumberOfPorts %x\n", DeviceExtension->UsbExtHubInfo.NumberOfPorts); + DPRINT1("HubDeviceExtension->UsbExtHubInfo.NumberOfPorts %x\n", HubDeviceExtension->UsbExtHubInfo.NumberOfPorts); + // + // Get the Hub Descriptor + // UsbBuildVendorRequest(Urb, URB_FUNCTION_CLASS_DEVICE, sizeof(Urb->UrbControlVendorClassRequest), - USBD_TRANSFER_DIRECTION_IN, + USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK, 0, + USB_REQUEST_GET_DESCRIPTOR, USB_DEVICE_CLASS_RESERVED, 0, - 0, - &DeviceExtension->HubDescriptor, + &HubDeviceExtension->HubDescriptor, NULL, sizeof(USB_HUB_DESCRIPTOR), NULL); - Urb->UrbHeader.UsbdDeviceHandle = DeviceExtension->RootHubUsbDevice; + Urb->UrbHeader.UsbdDeviceHandle = NULL;//HubDeviceExtension->RootHubHandle; - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); + Status = SubmitRequestToRootHub(RootHubDeviceObject, + IOCTL_INTERNAL_USB_SUBMIT_URB, + Urb, + NULL); - DPRINT1("bDescriptorType %x\n", DeviceExtension->HubDescriptor.bDescriptorType); + DPRINT1("bDescriptorType %x\n", HubDeviceExtension->HubDescriptor.bDescriptorType); - /* Select the configuration */ + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to get Hub Descriptor!\n"); + ExFreePool(Urb); + return STATUS_UNSUCCESSFUL; + } + + HubStatus = 0; + UsbBuildGetStatusRequest(Urb, + URB_FUNCTION_GET_STATUS_FROM_DEVICE, + 0, + &HubStatus, + 0, + NULL); + Urb->UrbHeader.UsbdDeviceHandle = NULL;//HubDeviceExtension->RootHubHandle; + + Status = SubmitRequestToRootHub(RootHubDeviceObject, + IOCTL_INTERNAL_USB_SUBMIT_URB, + Urb, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to get Hub Status!\n"); + ExFreePool(Urb); + return STATUS_UNSUCCESSFUL; + } + + DPRINT1("HubStatus %x\n", HubStatus); + + // + // Allocate memory for PortStatusChange to hold 2 USHORTs for each port on hub + // + HubDeviceExtension->PortStatusChange = ExAllocatePoolWithTag(NonPagedPool, + sizeof(ULONG) * HubDeviceExtension->UsbExtHubInfo.NumberOfPorts, + USB_HUB_TAG); + + // + // Get the first Configuration Descriptor + // + Pid = USBD_ParseConfigurationDescriptorEx(&HubDeviceExtension->HubConfigDescriptor, + &HubDeviceExtension->HubConfigDescriptor, + -1, -1, -1, -1, -1); - /* Get the first one */ - Pid = USBD_ParseConfigurationDescriptorEx(&DeviceExtension->HubConfigDescriptor, - &DeviceExtension->HubConfigDescriptor, - -1, -1, -1, -1, -1); ASSERT(Pid != NULL); - InterfaceList[0].InterfaceDescriptor = Pid; - ConfigUrb = USBD_CreateConfigurationRequestEx(&DeviceExtension->HubConfigDescriptor, (PUSBD_INTERFACE_LIST_ENTRY)&InterfaceList); - ASSERT(ConfigUrb != NULL); - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, ConfigUrb, NULL); - DeviceExtension->ConfigurationHandle = ConfigUrb->UrbSelectConfiguration.ConfigurationHandle; - DeviceExtension->PipeHandle = ConfigUrb->UrbSelectConfiguration.Interface.Pipes[0].PipeHandle; - DPRINT1("Configuration Handle %x\n", DeviceExtension->ConfigurationHandle); + InterfaceList[0].InterfaceDescriptor = Pid; + ConfigUrb = USBD_CreateConfigurationRequestEx(&HubDeviceExtension->HubConfigDescriptor, + (PUSBD_INTERFACE_LIST_ENTRY)&InterfaceList); + ASSERT(ConfigUrb != NULL); + + Status = SubmitRequestToRootHub(RootHubDeviceObject, + IOCTL_INTERNAL_USB_SUBMIT_URB, + ConfigUrb, + NULL); + + HubDeviceExtension->ConfigurationHandle = ConfigUrb->UrbSelectConfiguration.ConfigurationHandle; + HubDeviceExtension->PipeHandle = ConfigUrb->UrbSelectConfiguration.Interface.Pipes[0].PipeHandle; + DPRINT("Configuration Handle %x\n", HubDeviceExtension->ConfigurationHandle); + + // + // check if function is available + // + if (HubDeviceExtension->UsbDInterface.IsDeviceHighSpeed) + { + // + // is it high speed bus + // + if (HubDeviceExtension->UsbDInterface.IsDeviceHighSpeed(HubInterfaceBusContext)) + { + // + // initialize usb 2.0 hub + // + Status = HubDeviceExtension->HubInterface.Initialize20Hub(HubInterfaceBusContext, + HubDeviceExtension->RootHubHandle, 1); + DPRINT("Status %x\n", Status); + + // + // FIXME handle error + // + ASSERT(Status == STATUS_SUCCESS); + } + } ExFreePool(ConfigUrb); - Status = DeviceExtension->HubInterface.Initialize20Hub(DeviceExtension->RootHubPdo, DeviceExtension->RootHubUsbDevice, 1); - DPRINT1("Status %x\n", Status); + // + // Enable power on all ports + // + DPRINT("Enabling PortPower on all ports!\n"); + + for (PortId = 1; PortId <= HubDeviceExtension->HubDescriptor.bNumberOfPorts; PortId++) { - int PortLoop; - USHORT PortStatusAndChange[2]; + Status = SetPortFeature(RootHubDeviceObject, PortId, PORT_POWER); + if (!NT_SUCCESS(Status)) + DPRINT1("Failed to power on port %d\n", PortId); - for (PortLoop=0; PortLoop< DeviceExtension->UsbExtHubInfo.NumberOfPorts; PortLoop++) + Status = ClearPortFeature(RootHubDeviceObject, PortId, C_PORT_CONNECTION); + if (!NT_SUCCESS(Status)) + DPRINT1("Failed to power on port %d\n", PortId); + } + + DPRINT("RootHubInitNotification %x\n", HubDeviceExtension->HubInterface.RootHubInitNotification); + + // + // init root hub notification + // + if (HubDeviceExtension->HubInterface.RootHubInitNotification) + { + Status = HubDeviceExtension->HubInterface.RootHubInitNotification(HubInterfaceBusContext, + DeviceObject, + RootHubInitCallbackFunction); + if (!NT_SUCCESS(Status)) { - DPRINT1("Port %x\n", PortLoop); - UsbBuildVendorRequest(Urb, - URB_FUNCTION_CLASS_OTHER, - sizeof(Urb->UrbControlVendorClassRequest), - USBD_TRANSFER_DIRECTION_IN, - 0, - USB_REQUEST_SET_FEATURE, - PORT_POWER, - 1, - 0, - 0, - 0, - 0); + DPRINT1("Failed to set callback\n"); + } + } + else + { + // + // Send the first SCE Request + // + QueryStatusChangeEndpoint(DeviceObject); - Urb->UrbOSFeatureDescriptorRequest.MS_FeatureDescriptorIndex = PortLoop + 1; - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); - - DPRINT1("Status %x\n", Status); - - UsbBuildVendorRequest(Urb, - URB_FUNCTION_CLASS_OTHER, - sizeof(Urb->UrbControlVendorClassRequest), - USBD_TRANSFER_DIRECTION_OUT, - 0, - USB_REQUEST_GET_STATUS, - 0, - PortLoop + 1, - &PortStatusAndChange, - 0, - sizeof(PortStatusAndChange), - 0); - Status = QueryRootHub(DeviceExtension->RootHubPdo, IOCTL_INTERNAL_USB_SUBMIT_URB, Urb, NULL); - - DPRINT1("Status %x\n", Status); - DPRINT1("PortStatus = %x\n", PortStatusAndChange[0]); - DPRINT1("PortChange = %x\n", PortStatusAndChange[1]); + // + // reset ports + // + for (PortId = 1; PortId <= HubDeviceExtension->HubDescriptor.bNumberOfPorts; PortId++) + { + // + // get port status + // + Status = GetPortStatusAndChange(HubDeviceExtension->RootHubPhysicalDeviceObject, PortId, &StatusChange); + if (NT_SUCCESS(Status)) + { + // + // is there a device connected + // + if (StatusChange.Status & USB_PORT_STATUS_CONNECT) + { + // + // reset port + // + Status = SetPortFeature(HubDeviceExtension->RootHubPhysicalDeviceObject, PortId, PORT_RESET); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to reset on port %d\n", PortId); + } + else + { + // + // wait for the reset to be handled since we want to enumerate synchronously + // + KeWaitForSingleObject(&HubDeviceExtension->ResetComplete, + Executive, + KernelMode, + FALSE, + NULL); + KeClearEvent(&HubDeviceExtension->ResetComplete); + } + } + } } } @@ -965,53 +1893,70 @@ UsbhubPnpFdo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) break; } - case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x7 */ + case IRP_MN_QUERY_DEVICE_RELATIONS: { - switch (IrpSp->Parameters.QueryDeviceRelations.Type) + switch (Stack->Parameters.QueryDeviceRelations.Type) { case BusRelations: { PDEVICE_RELATIONS DeviceRelations = NULL; - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n"); + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n"); - Status = UsbhubFdoQueryBusRelations(DeviceObject, &DeviceRelations); + Status = USBHUB_FdoQueryBusRelations(DeviceObject, &DeviceRelations); Information = (ULONG_PTR)DeviceRelations; break; } case RemovalRelations: { - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n"); + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n"); return ForwardIrpAndForget(DeviceObject, Irp); } default: - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n", - IrpSp->Parameters.QueryDeviceRelations.Type); + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n", + Stack->Parameters.QueryDeviceRelations.Type); return ForwardIrpAndForget(DeviceObject, Irp); } break; } + case IRP_MN_QUERY_REMOVE_DEVICE: + case IRP_MN_QUERY_STOP_DEVICE: + { + Irp->IoStatus.Status = STATUS_SUCCESS; + return ForwardIrpAndForget(DeviceObject, Irp); + } + case IRP_MN_REMOVE_DEVICE: + { + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + IoDetachDevice(HubDeviceExtension->LowerDeviceObject); + IoDeleteDevice(DeviceObject); + + return STATUS_SUCCESS; + } case IRP_MN_QUERY_BUS_INFORMATION: { - DPRINT1("IRP_MN_QUERY_BUS_INFORMATION\n"); + DPRINT("IRP_MN_QUERY_BUS_INFORMATION\n"); break; } case IRP_MN_QUERY_ID: { - DPRINT1("IRP_MN_QUERY_ID\n"); + DPRINT("IRP_MN_QUERY_ID\n"); break; } case IRP_MN_QUERY_CAPABILITIES: { - DPRINT1("IRP_MN_QUERY_CAPABILITIES\n"); + DPRINT("IRP_MN_QUERY_CAPABILITIES\n"); break; } default: { - DPRINT1("Usbhub: IRP_MJ_PNP / unknown minor function 0x%lx\n", MinorFunction); + DPRINT(" IRP_MJ_PNP / unknown minor function 0x%lx\n", Stack->MinorFunction); return ForwardIrpAndForget(DeviceObject, Irp); } } + Irp->IoStatus.Information = Information; Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); @@ -1019,172 +1964,12 @@ UsbhubPnpFdo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) } NTSTATUS -UsbhubDeviceControlFdo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) +USBHUB_FdoHandleDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) { - PIO_STACK_LOCATION Stack; - ULONG IoControlCode; - PHUB_DEVICE_EXTENSION DeviceExtension; - ULONG LengthIn, LengthOut; - ULONG_PTR Information = 0; - PVOID BufferIn, BufferOut; - NTSTATUS Status = STATUS_UNSUCCESSFUL; - - Stack = IoGetCurrentIrpStackLocation(Irp); - LengthIn = Stack->Parameters.DeviceIoControl.InputBufferLength; - LengthOut = Stack->Parameters.DeviceIoControl.OutputBufferLength; - DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; - IoControlCode = Stack->Parameters.DeviceIoControl.IoControlCode; - UsbhubGetUserBuffers(Irp, IoControlCode, &BufferIn, &BufferOut); - - switch (IoControlCode) - { - case IOCTL_USB_GET_NODE_INFORMATION: - { - //PUSB_NODE_INFORMATION NodeInformation; - - DPRINT1("Usbhub: IOCTL_USB_GET_NODE_INFORMATION\n"); - if (LengthOut < sizeof(USB_NODE_INFORMATION)) - Status = STATUS_BUFFER_TOO_SMALL; - else if (BufferOut == NULL) - Status = STATUS_INVALID_PARAMETER; - else - { - /*NodeInformation = (PUSB_NODE_INFORMATION)BufferOut; - dev = ((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->dev; - NodeInformation->NodeType = UsbHub; - RtlCopyMemory( - &NodeInformation->u.HubInformation.HubDescriptor, - ((struct usb_hub *)usb_get_intfdata(to_usb_interface(&dev->actconfig->interface[0].dev)))->descriptor, - sizeof(USB_HUB_DESCRIPTOR)); - NodeInformation->u.HubInformation.HubIsBusPowered = dev->actconfig->desc.bmAttributes & 0x80; - Information = sizeof(USB_NODE_INFORMATION);*/ - Status = STATUS_SUCCESS; - } - break; - } - case IOCTL_USB_GET_NODE_CONNECTION_NAME: - { - PHUB_DEVICE_EXTENSION DeviceExtension; - PUSB_NODE_CONNECTION_NAME ConnectionName; - DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; - ConnectionName = (PUSB_NODE_CONNECTION_NAME)BufferOut; - - DPRINT1("Usbhub: IOCTL_USB_GET_NODE_CONNECTION_NAME\n"); - if (LengthOut < sizeof(USB_NODE_CONNECTION_NAME)) - Status = STATUS_BUFFER_TOO_SMALL; - else if (BufferOut == NULL) - Status = STATUS_INVALID_PARAMETER; - else if (ConnectionName->ConnectionIndex < 1 - || ConnectionName->ConnectionIndex > USB_MAXCHILDREN) - Status = STATUS_INVALID_PARAMETER; - else if (DeviceExtension->Children[ConnectionName->ConnectionIndex - 1] == NULL) - Status = STATUS_INVALID_PARAMETER; - else - { - ULONG NeededStructureSize; - DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceExtension->Children[ConnectionName->ConnectionIndex - 1]->DeviceExtension; - NeededStructureSize = DeviceExtension->SymbolicLinkName.Length + sizeof(UNICODE_NULL) + FIELD_OFFSET(USB_NODE_CONNECTION_NAME, NodeName); - if (ConnectionName->ActualLength < NeededStructureSize / sizeof(WCHAR) - || LengthOut < NeededStructureSize) - { - /* Buffer too small */ - ConnectionName->ActualLength = NeededStructureSize / sizeof(WCHAR); - Information = sizeof(USB_NODE_CONNECTION_NAME); - Status = STATUS_BUFFER_TOO_SMALL; - } - else - { - RtlCopyMemory( - ConnectionName->NodeName, - DeviceExtension->SymbolicLinkName.Buffer, - DeviceExtension->SymbolicLinkName.Length); - ConnectionName->NodeName[DeviceExtension->SymbolicLinkName.Length / sizeof(WCHAR)] = UNICODE_NULL; - DPRINT1("Usbhub: IOCTL_USB_GET_NODE_CONNECTION_NAME returns '%S'\n", ConnectionName->NodeName); - ConnectionName->ActualLength = NeededStructureSize / sizeof(WCHAR); - Information = NeededStructureSize; - Status = STATUS_SUCCESS; - } - Information = LengthOut; - } - break; - } - case IOCTL_USB_GET_NODE_CONNECTION_INFORMATION: - { - PUSB_NODE_CONNECTION_INFORMATION ConnectionInformation; -/* - ULONG i, j, k; - struct usb_device* dev; - ULONG NumberOfOpenPipes = 0; - ULONG SizeOfOpenPipesArray; -*/ - ConnectionInformation = (PUSB_NODE_CONNECTION_INFORMATION)BufferOut; - - DPRINT1("Usbhub: IOCTL_USB_GET_NODE_CONNECTION_INFORMATION\n"); - if (LengthOut < sizeof(USB_NODE_CONNECTION_INFORMATION)) - Status = STATUS_BUFFER_TOO_SMALL; - else if (BufferOut == NULL) - Status = STATUS_INVALID_PARAMETER; - else if (ConnectionInformation->ConnectionIndex < 1 - || ConnectionInformation->ConnectionIndex > USB_MAXCHILDREN) - Status = STATUS_INVALID_PARAMETER; - else - { - DPRINT1("Usbhub: We should succeed\n"); - } - break; - } - case IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION: - { - //PUSB_DESCRIPTOR_REQUEST Descriptor; - DPRINT1("Usbhub: IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION\n"); - Information = 0; - Status = STATUS_NOT_IMPLEMENTED; - break; - } - case IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME: - { - PHUB_DEVICE_EXTENSION DeviceExtension; - PUSB_NODE_CONNECTION_DRIVERKEY_NAME StringDescriptor; - DPRINT1("Usbhub: IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME\n"); - DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; - StringDescriptor = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)BufferOut; - if (LengthOut < sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME)) - Status = STATUS_BUFFER_TOO_SMALL; - else if (StringDescriptor == NULL) - Status = STATUS_INVALID_PARAMETER; - else if (StringDescriptor->ConnectionIndex < 1 - || StringDescriptor->ConnectionIndex > USB_MAXCHILDREN) - Status = STATUS_INVALID_PARAMETER; - else if (DeviceExtension->Children[StringDescriptor->ConnectionIndex - 1] == NULL) - Status = STATUS_INVALID_PARAMETER; - else - { - ULONG StringSize; - Status = IoGetDeviceProperty( - DeviceExtension->Children[StringDescriptor->ConnectionIndex - 1], - DevicePropertyDriverKeyName, - LengthOut - FIELD_OFFSET(USB_NODE_CONNECTION_DRIVERKEY_NAME, DriverKeyName), - StringDescriptor->DriverKeyName, - &StringSize); - if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL) - { - StringDescriptor->ActualLength = StringSize + FIELD_OFFSET(USB_NODE_CONNECTION_DRIVERKEY_NAME, DriverKeyName); - Information = LengthOut; - Status = STATUS_SUCCESS; - } - } - break; - } - default: - { - /* Pass Irp to lower driver */ - DPRINT1("Usbhub: Unknown IOCTL code 0x%lx\n", Stack->Parameters.DeviceIoControl.IoControlCode); - return ForwardIrpAndForget(DeviceObject, Irp); - } - } - - Irp->IoStatus.Information = Information; - Irp->IoStatus.Status = Status; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - return Status; + DPRINT1("FdoHandleDeviceControl\n"); + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; } + diff --git a/reactos/drivers/usb/usbhub/misc.c b/reactos/drivers/usb/usbhub/misc.c index a870a7e4f38..9e3d782678c 100644 --- a/reactos/drivers/usb/usbhub/misc.c +++ b/reactos/drivers/usb/usbhub/misc.c @@ -1,17 +1,100 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: USB hub driver - * FILE: drivers/usb/cromwell/hub/misc.c - * PURPOSE: Misceallenous operations - * - * PROGRAMMERS: Herv� Poussineau (hpoussin@reactos.com), + * PROJECT: ReactOS Universal Serial Bus Hub Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbhub/fdo.c + * PURPOSE: Misc helper functions + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) */ - -#define NDEBUG #include "usbhub.h" #include -NTSTATUS NTAPI +VOID +DumpDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) +{ + DPRINT1("Dumping Device Descriptor %x\n", DeviceDescriptor); + DPRINT1("bLength %x\n", DeviceDescriptor->bLength); + DPRINT1("bDescriptorType %x\n", DeviceDescriptor->bDescriptorType); + DPRINT1("bcdUSB %x\n", DeviceDescriptor->bcdUSB); + DPRINT1("bDeviceClass %x\n", DeviceDescriptor->bDeviceClass); + DPRINT1("bDeviceSubClass %x\n", DeviceDescriptor->bDeviceSubClass); + DPRINT1("bDeviceProtocol %x\n", DeviceDescriptor->bDeviceProtocol); + DPRINT1("bMaxPacketSize0 %x\n", DeviceDescriptor->bMaxPacketSize0); + DPRINT1("idVendor %x\n", DeviceDescriptor->idVendor); + DPRINT1("idProduct %x\n", DeviceDescriptor->idProduct); + DPRINT1("bcdDevice %x\n", DeviceDescriptor->bcdDevice); + DPRINT1("iManufacturer %x\n", DeviceDescriptor->iManufacturer); + DPRINT1("iProduct %x\n", DeviceDescriptor->iProduct); + DPRINT1("iSerialNumber %x\n", DeviceDescriptor->iSerialNumber); + DPRINT1("bNumConfigurations %x\n", DeviceDescriptor->bNumConfigurations); +} + +//---------------------------------------------------------------------------------------- +VOID +DumpConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) +{ + DPRINT1("Dumping ConfigurationDescriptor %x\n", ConfigurationDescriptor); + DPRINT1("bLength %x\n", ConfigurationDescriptor->bLength); + DPRINT1("bDescriptorType %x\n", ConfigurationDescriptor->bDescriptorType); + DPRINT1("wTotalLength %x\n", ConfigurationDescriptor->wTotalLength); + DPRINT1("bNumInterfaces %x\n", ConfigurationDescriptor->bNumInterfaces); + DPRINT1("bConfigurationValue %x\n", ConfigurationDescriptor->bConfigurationValue); + DPRINT1("iConfiguration %x\n", ConfigurationDescriptor->iConfiguration); + DPRINT1("bmAttributes %x\n", ConfigurationDescriptor->bmAttributes); + DPRINT1("MaxPower %x\n", ConfigurationDescriptor->MaxPower); +} + +VOID +DumpFullConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) +{ + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + LONG i, j; + + DPRINT1("Dumping ConfigurationDescriptor %x\n", ConfigurationDescriptor); + DPRINT1("bLength %x\n", ConfigurationDescriptor->bLength); + DPRINT1("bDescriptorType %x\n", ConfigurationDescriptor->bDescriptorType); + DPRINT1("wTotalLength %x\n", ConfigurationDescriptor->wTotalLength); + DPRINT1("bNumInterfaces %x\n", ConfigurationDescriptor->bNumInterfaces); + DPRINT1("bConfigurationValue %x\n", ConfigurationDescriptor->bConfigurationValue); + DPRINT1("iConfiguration %x\n", ConfigurationDescriptor->iConfiguration); + DPRINT1("bmAttributes %x\n", ConfigurationDescriptor->bmAttributes); + DPRINT1("MaxPower %x\n", ConfigurationDescriptor->MaxPower); + + InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR) ((ULONG_PTR)ConfigurationDescriptor + sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + for (i=0; i < ConfigurationDescriptor->bNumInterfaces; i++) + { + DPRINT1("- Dumping InterfaceDescriptor %x\n", InterfaceDescriptor); + DPRINT1(" bLength %x\n", InterfaceDescriptor->bLength); + DPRINT1(" bDescriptorType %x\n", InterfaceDescriptor->bDescriptorType); + DPRINT1(" bInterfaceNumber %x\n", InterfaceDescriptor->bInterfaceNumber); + DPRINT1(" bAlternateSetting %x\n", InterfaceDescriptor->bAlternateSetting); + DPRINT1(" bNumEndpoints %x\n", InterfaceDescriptor->bNumEndpoints); + DPRINT1(" bInterfaceClass %x\n", InterfaceDescriptor->bInterfaceClass); + DPRINT1(" bInterfaceSubClass %x\n", InterfaceDescriptor->bInterfaceSubClass); + DPRINT1(" bInterfaceProtocol %x\n", InterfaceDescriptor->bInterfaceProtocol); + DPRINT1(" iInterface %x\n", InterfaceDescriptor->iInterface); + + EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) ((ULONG_PTR)InterfaceDescriptor + sizeof(USB_INTERFACE_DESCRIPTOR)); + + for (j=0; j < InterfaceDescriptor->bNumEndpoints; j++) + { + DPRINT1(" bLength %x\n", EndpointDescriptor->bLength); + DPRINT1(" bDescriptorType %x\n", EndpointDescriptor->bDescriptorType); + DPRINT1(" bEndpointAddress %x\n", EndpointDescriptor->bEndpointAddress); + DPRINT1(" bmAttributes %x\n", EndpointDescriptor->bmAttributes); + DPRINT1(" wMaxPacketSize %x\n", EndpointDescriptor->wMaxPacketSize); + DPRINT1(" bInterval %x\n", EndpointDescriptor->bInterval); + EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) ((ULONG_PTR)EndpointDescriptor + sizeof(USB_ENDPOINT_DESCRIPTOR)); + } + InterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)(ULONG_PTR)EndpointDescriptor; + } +} + +NTSTATUS +NTAPI ForwardIrpAndWaitCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, @@ -19,6 +102,7 @@ ForwardIrpAndWaitCompletion( { if (Irp->PendingReturned) KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; } @@ -46,12 +130,12 @@ ForwardIrpAndWait( return Status; } -NTSTATUS NTAPI +NTSTATUS ForwardIrpAndForget( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { - PDEVICE_OBJECT LowerDevice = ((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->LowerDevice; + PDEVICE_OBJECT LowerDevice = ((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->LowerDeviceObject; ASSERT(LowerDevice); @@ -59,104 +143,3 @@ ForwardIrpAndForget( return IoCallDriver(LowerDevice, Irp); } -/* I really want PCSZ strings as last arguments because - * PnP ids are ANSI-encoded in PnP device string - * identification */ -NTSTATUS -UsbhubInitMultiSzString( - OUT PUNICODE_STRING Destination, - ... /* list of PCSZ */) -{ - va_list args; - PCSZ Source; - ANSI_STRING AnsiString; - UNICODE_STRING UnicodeString; - ULONG DestinationSize = 0; - NTSTATUS Status = STATUS_SUCCESS; - - ASSERT(Destination); - - /* Calculate length needed for destination unicode string */ - va_start(args, Destination); - Source = va_arg(args, PCSZ); - while (Source != NULL) - { - RtlInitAnsiString(&AnsiString, Source); - DestinationSize += RtlAnsiStringToUnicodeSize(&AnsiString) - + sizeof(WCHAR) /* final NULL */; - Source = va_arg(args, PCSZ); - } - va_end(args); - if (DestinationSize == 0) - { - RtlInitUnicodeString(Destination, NULL); - return STATUS_SUCCESS; - } - - /* Initialize destination string */ - DestinationSize += sizeof(WCHAR); // final NULL - Destination->Buffer = (PWSTR)ExAllocatePoolWithTag(PagedPool, DestinationSize, USB_HUB_TAG); - if (!Destination->Buffer) - return STATUS_INSUFFICIENT_RESOURCES; - Destination->Length = 0; - Destination->MaximumLength = (USHORT)DestinationSize; - - /* Copy arguments to destination string */ - /* Use a temporary unicode string, which buffer is shared with - * destination string, to copy arguments */ - UnicodeString.Length = Destination->Length; - UnicodeString.MaximumLength = Destination->MaximumLength; - UnicodeString.Buffer = Destination->Buffer; - va_start(args, Destination); - Source = va_arg(args, PCSZ); - while (Source != NULL) - { - RtlInitAnsiString(&AnsiString, Source); - Status = RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE); - if (!NT_SUCCESS(Status)) - { - ExFreePoolWithTag(Destination->Buffer, USB_HUB_TAG); - break; - } - Destination->Length += UnicodeString.Length + sizeof(WCHAR); - UnicodeString.MaximumLength -= UnicodeString.Length + sizeof(WCHAR); - UnicodeString.Buffer += UnicodeString.Length / sizeof(WCHAR) + 1; - UnicodeString.Length = 0; - Source = va_arg(args, PCSZ); - } - va_end(args); - if (NT_SUCCESS(Status)) - { - /* Finish multi-sz string */ - Destination->Buffer[Destination->Length / sizeof(WCHAR)] = L'\0'; - Destination->Length += sizeof(WCHAR); - } - return Status; -} - -NTSTATUS -UsbhubDuplicateUnicodeString( - OUT PUNICODE_STRING Destination, - IN PUNICODE_STRING Source, - IN POOL_TYPE PoolType) -{ - ASSERT(Destination); - - if (Source == NULL) - { - RtlInitUnicodeString(Destination, NULL); - return STATUS_SUCCESS; - } - - Destination->Buffer = ExAllocatePool(PoolType, Source->MaximumLength); - if (Destination->Buffer == NULL) - { - return STATUS_INSUFFICIENT_RESOURCES; - } - - Destination->MaximumLength = Source->MaximumLength; - Destination->Length = Source->Length; - RtlCopyMemory(Destination->Buffer, Source->Buffer, Source->MaximumLength); - - return STATUS_SUCCESS; -} diff --git a/reactos/drivers/usb/usbhub/pdo.c b/reactos/drivers/usb/usbhub/pdo.c index f217c6da0a1..d5d2d3e0d05 100644 --- a/reactos/drivers/usb/usbhub/pdo.c +++ b/reactos/drivers/usb/usbhub/pdo.c @@ -1,40 +1,166 @@ -/* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: USB hub driver - * FILE: drivers/usb/cromwell/hub/pdo.c - * PURPOSE: IRP_MJ_PNP operations for PDOs - * - * PROGRAMMERS: Copyright 2005-2006 Hervé Poussineau (hpoussin@reactos.org) - * 2010 Michael Martin (michael.martin@reactos.org) +/* + * PROJECT: ReactOS Universal Serial Bus Hub Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbhub/fdo.c + * PURPOSE: Handle PDO + * PROGRAMMERS: + * Hervé Poussineau (hpoussin@reactos.org) + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) */ -#define NDEBUG -#include #include "usbhub.h" #define IO_METHOD_FROM_CTL_CODE(ctlCode) (ctlCode&0x00000003) NTSTATUS -UsbhubInternalDeviceControlPdo( +NTAPI +UrbCompletion( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context) +{ + PIRP OriginalIrp; + DPRINT("Entered Urb Completion\n"); + + // + // Get the original Irp + // + OriginalIrp = (PIRP)Context; + + // + // Update it to match what was returned for the IRP that was passed to RootHub + // + OriginalIrp->IoStatus.Status = Irp->IoStatus.Status; + OriginalIrp->IoStatus.Information = Irp->IoStatus.Information; + DPRINT("Status %x, Information %x\n", Irp->IoStatus.Status, Irp->IoStatus.Information); + + // + // Complete the original Irp + // + IoCompleteRequest(OriginalIrp, IO_NO_INCREMENT); + + // + // Return this status so the IO Manager doesnt mess with the Irp + // + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +FowardUrbToRootHub( + PDEVICE_OBJECT RootHubDeviceObject, + IN ULONG IoControlCode, + PIRP Irp, + OUT PVOID OutParameter1, + OUT PVOID OutParameter2) +{ + NTSTATUS Status; + PIRP ForwardIrp; + IO_STATUS_BLOCK IoStatus; + PIO_STACK_LOCATION ForwardStack, CurrentStack; + PURB Urb; + + // + // Get the current stack location for the Irp + // + CurrentStack = IoGetCurrentIrpStackLocation(Irp); + ASSERT(CurrentStack); + + // + // Pull the Urb from that stack, it will be reused in the Irp sent to RootHub + // + Urb = (PURB)CurrentStack->Parameters.Others.Argument1; + ASSERT(Urb); + + // + // Create the Irp to forward to RootHub + // + ForwardIrp = IoBuildAsynchronousFsdRequest(IRP_MJ_SHUTDOWN, + RootHubDeviceObject, + NULL, + 0, + 0, + &IoStatus); + if (!ForwardIrp) + { + DPRINT1("Failed to allocate IRP\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Get the new Irps next stack + // + ForwardStack = IoGetNextIrpStackLocation(ForwardIrp); + + // + // Copy the stack for the current irp into the next stack of new irp + // + RtlCopyMemory(ForwardStack, CurrentStack, sizeof(IO_STACK_LOCATION)); + + IoStatus.Status = STATUS_NOT_SUPPORTED; + IoStatus.Information = 0; + + // + // Mark the Irp from upper driver as pending + // + IoMarkIrpPending(Irp); + + // + // Now set the completion routine for the new Irp. + // + IoSetCompletionRoutine(ForwardIrp, + UrbCompletion, + Irp, + TRUE, + TRUE, + TRUE); + + Status = IoCallDriver(RootHubDeviceObject, ForwardIrp); + + // + // Always return pending as the completion routine will take care of it + // + return STATUS_PENDING; +} + +NTSTATUS +USBHUB_PdoHandleInternalDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { + NTSTATUS Status; PIO_STACK_LOCATION Stack; ULONG_PTR Information = 0; - NTSTATUS Status; + PHUB_DEVICE_EXTENSION HubDeviceExtension; + PHUB_CHILDDEVICE_EXTENSION ChildDeviceExtension; + PDEVICE_OBJECT RootHubDeviceObject; + PURB Urb; - DPRINT1("Usbhub: UsbhubInternalDeviceControlPdo() called\n"); + //DPRINT1("UsbhubInternalDeviceControlPdo(%x) called\n", DeviceObject); + // + // get current stack location + // Stack = IoGetCurrentIrpStackLocation(Irp); + ASSERT(Stack); + + // + // Set default status + // Status = Irp->IoStatus.Status; + ChildDeviceExtension = (PHUB_CHILDDEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(ChildDeviceExtension->Common.IsFDO == FALSE); + HubDeviceExtension = (PHUB_DEVICE_EXTENSION)ChildDeviceExtension->ParentDeviceObject->DeviceExtension; + RootHubDeviceObject = HubDeviceExtension->RootHubPhysicalDeviceObject; + switch (Stack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_INTERNAL_USB_GET_PARENT_HUB_INFO: { PHUB_DEVICE_EXTENSION DeviceExtension; - DPRINT1("Usbhub: IOCTL_INTERNAL_USB_GET_PARENT_HUB_INFO\n"); + DPRINT("IOCTL_INTERNAL_USB_GET_PARENT_HUB_INFO\n"); if (Irp->AssociatedIrp.SystemBuffer == NULL || Stack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(PVOID)) { @@ -46,261 +172,483 @@ UsbhubInternalDeviceControlPdo( DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; pHubPointer = (PVOID*)Irp->AssociatedIrp.SystemBuffer; - *pHubPointer = DeviceExtension->dev; + // FIXME + *pHubPointer = NULL; Information = sizeof(PVOID); Status = STATUS_SUCCESS; } break; } + case IOCTL_INTERNAL_USB_SUBMIT_URB: + { + //DPRINT1("IOCTL_INTERNAL_USB_SUBMIT_URB\n"); + + // + // Get the Urb + // + Urb = (PURB)Stack->Parameters.Others.Argument1; + ASSERT(Urb); + + // + // Set the real device handle + // + //DPRINT("UsbdDeviceHandle %x, ChildDeviceHandle %x\n", Urb->UrbHeader.UsbdDeviceHandle, ChildDeviceExtension->UsbDeviceHandle); + + Urb->UrbHeader.UsbdDeviceHandle = ChildDeviceExtension->UsbDeviceHandle; + + // + // Submit to RootHub + // + switch (Urb->UrbHeader.Function) + { + // + // Debugging only + // + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + DPRINT1("URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE\n"); + break; + case URB_FUNCTION_CLASS_DEVICE: + DPRINT1("URB_FUNCTION_CLASS_DEVICE\n"); + break; + case URB_FUNCTION_GET_STATUS_FROM_DEVICE: + DPRINT1("URB_FUNCTION_GET_STATUS_FROM_DEVICE\n"); + break; + case URB_FUNCTION_SELECT_CONFIGURATION: + DPRINT1("URB_FUNCTION_SELECT_CONFIGURATION\n"); + break; + case URB_FUNCTION_SELECT_INTERFACE: + DPRINT1("URB_FUNCTION_SELECT_INTERFACE\n"); + break; + case URB_FUNCTION_CLASS_OTHER: + DPRINT1("URB_FUNCTION_CLASS_OTHER\n"); + break; + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + { + /* + DPRINT1("URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER\n"); + DPRINT1("PipeHandle %x\n", Urb->UrbBulkOrInterruptTransfer.PipeHandle); + DPRINT1("TransferFlags %x\n", Urb->UrbBulkOrInterruptTransfer.TransferFlags); + DPRINT1("Buffer %x\n", Urb->UrbBulkOrInterruptTransfer.TransferBuffer); + DPRINT1("BufferMDL %x\n", Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL); + DPRINT1("Length %x\n", Urb->UrbBulkOrInterruptTransfer.TransferBufferLength); + DPRINT1("UrbLink %x\n", Urb->UrbBulkOrInterruptTransfer.UrbLink); + DPRINT1("hca %x\n", Urb->UrbBulkOrInterruptTransfer.hca); + if (Urb->UrbBulkOrInterruptTransfer.TransferFlags == USBD_SHORT_TRANSFER_OK) + { + } + */ + break; + + } + case URB_FUNCTION_CLASS_INTERFACE: + DPRINT1("URB_FUNCTION_CLASS_INTERFACE\n"); + break; + default: + DPRINT1("IOCTL_INTERNAL_USB_SUBMIT_URB Function %x NOT IMPLEMENTED\n", Urb->UrbHeader.Function); + break; + } + Urb->UrbHeader.UsbdDeviceHandle = ChildDeviceExtension->UsbDeviceHandle; + //DPRINT1("Stack->CompletionRoutine %x\n", Stack->CompletionRoutine); + // + // Send the request to RootHub + // + Status = FowardUrbToRootHub(RootHubDeviceObject, IOCTL_INTERNAL_USB_SUBMIT_URB, Irp, Urb, NULL); + return Status; + break; + } + // + // FIXME: Can these be sent to RootHub? + // + case IOCTL_INTERNAL_USB_RESET_PORT: + DPRINT1("IOCTL_INTERNAL_USB_RESET_PORT\n"); + break; + case IOCTL_INTERNAL_USB_GET_PORT_STATUS: + { + PORT_STATUS_CHANGE PortStatus; + LONG PortId; + PUCHAR PortStatusBits; + + PortStatusBits = (PUCHAR)Stack->Parameters.Others.Argument1; + // + // USBD_PORT_ENABLED (bit 0) or USBD_PORT_CONNECTED (bit 1) + // + DPRINT1("IOCTL_INTERNAL_USB_GET_PORT_STATUS\n"); + DPRINT("Arg1 %x\n", *PortStatusBits); + *PortStatusBits = 0; + if (Stack->Parameters.Others.Argument1) + { + for (PortId = 1; PortId <= HubDeviceExtension->UsbExtHubInfo.NumberOfPorts; PortId++) + { + Status = GetPortStatusAndChange(RootHubDeviceObject, PortId, &PortStatus); + if (NT_SUCCESS(Status)) + { + DPRINT("Connect %x\n", ((PortStatus.Status & USB_PORT_STATUS_CONNECT) << 1) << ((PortId - 1) * 2)); + DPRINT("Enable %x\n", ((PortStatus.Status & USB_PORT_STATUS_ENABLE) >> 1) << ((PortId - 1) * 2)); + *PortStatusBits += + (((PortStatus.Status & USB_PORT_STATUS_CONNECT) << 1) << ((PortId - 1) * 2)) + + (((PortStatus.Status & USB_PORT_STATUS_ENABLE) >> 1) << ((PortId - 1) * 2)); + + } + } + } + + DPRINT1("Arg1 %x\n", *PortStatusBits); + Status = STATUS_SUCCESS; + break; + } + case IOCTL_INTERNAL_USB_ENABLE_PORT: + DPRINT1("IOCTL_INTERNAL_USB_ENABLE_PORT\n"); + break; + case IOCTL_INTERNAL_USB_CYCLE_PORT: + DPRINT1("IOCTL_INTERNAL_USB_CYCLE_PORT\n"); + break; + case IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE: + DPRINT1("IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE\n"); + break; default: { - DPRINT1("Usbhub: Unknown IOCTL code 0x%lx\n", Stack->Parameters.DeviceIoControl.IoControlCode); + DPRINT1("Unknown IOCTL code 0x%lx\n", Stack->Parameters.DeviceIoControl.IoControlCode); Information = Irp->IoStatus.Information; Status = Irp->IoStatus.Status; } } - Irp->IoStatus.Information = Information; - Irp->IoStatus.Status = Status; - IoCompleteRequest(Irp, IO_NO_INCREMENT); + if (Status != STATUS_PENDING) + { + Irp->IoStatus.Information = Information; + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } return Status; } -static NTSTATUS -UsbhubPdoStartDevice( +NTSTATUS +USBHUB_PdoStartDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { - PHUB_DEVICE_EXTENSION DeviceExtension; - NTSTATUS Status = STATUS_UNSUCCESSFUL; + PHUB_CHILDDEVICE_EXTENSION ChildDeviceExtension; + //NTSTATUS Status; + DPRINT("USBHUB_PdoStartDevice %x\n", DeviceObject); + ChildDeviceExtension = (PHUB_CHILDDEVICE_EXTENSION)DeviceObject->DeviceExtension; - DeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + // + // This should be a PDO + // + ASSERT(ChildDeviceExtension->Common.IsFDO == FALSE); - /* Register and activate device interface */ -/* - Status = IoRegisterDeviceInterface( - DeviceObject, - DeviceExtension->dev->descriptor.bDeviceClass == USB_CLASS_HUB ? - &GUID_DEVINTERFACE_USB_HUB : - &GUID_DEVINTERFACE_USB_DEVICE, - NULL, - &DeviceExtension->SymbolicLinkName); -*/ - if (!NT_SUCCESS(Status)) - { - DPRINT1("Usbhub: IoRegisterDeviceInterface() failed with status 0x%08lx\n", Status); - return Status; - } - - //Status = IoSetDeviceInterfaceState(&DeviceExtension->SymbolicLinkName, TRUE); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Usbhub: IoSetDeviceInterfaceState() failed with status 0x%08lx\n", Status); - return Status; - } + // + // FIXME: Fow now assume success + // + UNIMPLEMENTED return STATUS_SUCCESS; } -static NTSTATUS -UsbhubPdoQueryId( +NTSTATUS +USBHUB_PdoQueryId( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, OUT ULONG_PTR* Information) { - PHUB_CHILDDEVICE_EXTENSION DeviceExtension; + PHUB_CHILDDEVICE_EXTENSION ChildDeviceExtension; ULONG IdType; - PWCHAR SourceString = NULL; + PUNICODE_STRING SourceString = NULL; + PWCHAR ReturnString = NULL; NTSTATUS Status = STATUS_SUCCESS; IdType = IoGetCurrentIrpStackLocation(Irp)->Parameters.QueryId.IdType; - DeviceExtension = (PHUB_CHILDDEVICE_EXTENSION)DeviceObject->DeviceExtension; + ChildDeviceExtension = (PHUB_CHILDDEVICE_EXTENSION)DeviceObject->DeviceExtension; switch (IdType) { - case BusQueryDeviceID: + case BusQueryDeviceID: { - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryDeviceID\n"); - SourceString = DeviceExtension->DeviceId; + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryDeviceID\n"); + SourceString = &ChildDeviceExtension->usDeviceId; break; } - /* FIXME: Implement */ case BusQueryHardwareIDs: { - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryHardwareIDs\n"); - SourceString = DeviceExtension->HardwareIds; - Status = STATUS_NOT_SUPPORTED; + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryHardwareIDs\n"); + SourceString = &ChildDeviceExtension->usHardwareIds; break; } - /* FIXME: Implement */ case BusQueryCompatibleIDs: { - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryCompatibleIDs\n"); - SourceString = DeviceExtension->CompatibleIds; - Status = STATUS_NOT_SUPPORTED; + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryCompatibleIDs\n"); + SourceString = &ChildDeviceExtension->usCompatibleIds; break; } case BusQueryInstanceID: { - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryInstanceID\n"); - SourceString = DeviceExtension->InstanceId; + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryInstanceID\n"); + SourceString = &ChildDeviceExtension->usInstanceId; break; } default: - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_ID / unknown query id type 0x%lx\n", IdType); + DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_ID / unknown query id type 0x%lx\n", IdType); return STATUS_NOT_SUPPORTED; } - *Information = (ULONG_PTR)SourceString; + if (SourceString) + { + // + // allocate buffer + // + ReturnString = ExAllocatePool(PagedPool, SourceString->MaximumLength); + if (!ReturnString) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy buffer + // + RtlCopyMemory(ReturnString, SourceString->Buffer, SourceString->MaximumLength); + } + + *Information = (ULONG_PTR)ReturnString; + return Status; } -static NTSTATUS -UsbhubPdoQueryDeviceText( +NTSTATUS +USBHUB_PdoQueryDeviceText( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, OUT ULONG_PTR* Information) { - PHUB_CHILDDEVICE_EXTENSION DeviceExtension; + PHUB_CHILDDEVICE_EXTENSION ChildDeviceExtension; DEVICE_TEXT_TYPE DeviceTextType; + PUNICODE_STRING SourceString = NULL; + PWCHAR ReturnString = NULL; + NTSTATUS Status = STATUS_SUCCESS; LCID LocaleId; DeviceTextType = IoGetCurrentIrpStackLocation(Irp)->Parameters.QueryDeviceText.DeviceTextType; LocaleId = IoGetCurrentIrpStackLocation(Irp)->Parameters.QueryDeviceText.LocaleId; - DeviceExtension = (PHUB_CHILDDEVICE_EXTENSION)DeviceObject->DeviceExtension; + ChildDeviceExtension = (PHUB_CHILDDEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // FIXME: LocaleId + // switch (DeviceTextType) { case DeviceTextDescription: case DeviceTextLocationInformation: { - if (DeviceTextType == DeviceTextDescription) + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / DeviceTextDescription\n"); + + // + // does the device provide a text description + // + if (ChildDeviceExtension->usTextDescription.Buffer && ChildDeviceExtension->usTextDescription.Length) { - *Information = (ULONG_PTR)DeviceExtension->TextDescription; - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / DeviceTextDescription\n"); + // + // use device text + // + SourceString = &ChildDeviceExtension->usTextDescription; } - else - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / DeviceTextLocationInformation\n"); - -/* if (!DeviceExtension->dev->descriptor.iProduct) - return STATUS_NOT_SUPPORTED;*/ - - return STATUS_SUCCESS; + break; } default: - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / unknown device text type 0x%lx\n", DeviceTextType); - return STATUS_NOT_SUPPORTED; + { + DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / unknown device text type 0x%lx\n", DeviceTextType); + Status = STATUS_NOT_SUPPORTED; + break; + } } + + if (SourceString) + { + ReturnString = ExAllocatePool(PagedPool, SourceString->Length); + RtlCopyMemory(ReturnString, SourceString->Buffer, SourceString->Length); + DPRINT1("%S\n", ReturnString); + *Information = (ULONG_PTR)ReturnString; + } + + return Status; } -NTSTATUS NTAPI -UsbhubPnpPdo( +NTSTATUS +USBHUB_PdoHandlePnp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { + NTSTATUS Status; ULONG MinorFunction; PIO_STACK_LOCATION Stack; ULONG_PTR Information = 0; - NTSTATUS Status; + PHUB_CHILDDEVICE_EXTENSION UsbChildExtension; + ULONG Index; + ULONG bFound; + PDEVICE_RELATIONS DeviceRelation; + UsbChildExtension = (PHUB_CHILDDEVICE_EXTENSION)DeviceObject->DeviceExtension; Stack = IoGetCurrentIrpStackLocation(Irp); MinorFunction = Stack->MinorFunction; switch (MinorFunction) { - case IRP_MN_START_DEVICE: /* 0x0 */ + case IRP_MN_START_DEVICE: { - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_START_DEVICE\n"); - Status = UsbhubPdoStartDevice(DeviceObject, Irp); + DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n"); + Status = USBHUB_PdoStartDevice(DeviceObject, Irp); break; } - case IRP_MN_QUERY_CAPABILITIES: /* 0x09 */ + case IRP_MN_QUERY_CAPABILITIES: { PDEVICE_CAPABILITIES DeviceCapabilities; ULONG i; - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_CAPABILITIES\n"); + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_CAPABILITIES\n"); DeviceCapabilities = (PDEVICE_CAPABILITIES)Stack->Parameters.DeviceCapabilities.Capabilities; - /* FIXME: capabilities can change with connected device */ - DeviceCapabilities->LockSupported = TRUE; + // FIXME: capabilities can change with connected device + DeviceCapabilities->LockSupported = FALSE; DeviceCapabilities->EjectSupported = FALSE; - DeviceCapabilities->Removable = FALSE; + DeviceCapabilities->Removable = TRUE; DeviceCapabilities->DockDevice = FALSE; DeviceCapabilities->UniqueID = FALSE; - DeviceCapabilities->SilentInstall = TRUE; + DeviceCapabilities->SilentInstall = FALSE; DeviceCapabilities->RawDeviceOK = FALSE; DeviceCapabilities->SurpriseRemovalOK = FALSE; - DeviceCapabilities->HardwareDisabled = FALSE; /* FIXME */ - //DeviceCapabilities->NoDisplayInUI = FALSE; /* FIXME */ - DeviceCapabilities->DeviceState[0] = PowerDeviceD0; /* FIXME */ - for (i = 0; i < PowerSystemMaximum; i++) - DeviceCapabilities->DeviceState[i] = PowerDeviceD3; /* FIXME */ - //DeviceCapabilities->DeviceWake = PowerDeviceUndefined; /* FIXME */ - DeviceCapabilities->D1Latency = 0; /* FIXME */ - DeviceCapabilities->D2Latency = 0; /* FIXME */ - DeviceCapabilities->D3Latency = 0; /* FIXME */ + DeviceCapabilities->HardwareDisabled = FALSE; + //DeviceCapabilities->NoDisplayInUI = FALSE; + DeviceCapabilities->Address = UsbChildExtension->PortNumber; + DeviceCapabilities->UINumber = 0; + DeviceCapabilities->DeviceState[0] = PowerDeviceD0; + for (i = 1; i < PowerSystemMaximum; i++) + DeviceCapabilities->DeviceState[i] = PowerDeviceD3; + //DeviceCapabilities->DeviceWake = PowerDeviceUndefined; + DeviceCapabilities->D1Latency = 0; + DeviceCapabilities->D2Latency = 0; + DeviceCapabilities->D3Latency = 0; Status = STATUS_SUCCESS; break; } - case IRP_MN_QUERY_RESOURCES: /* 0x0a */ + case IRP_MN_QUERY_RESOURCES: { - PCM_RESOURCE_LIST ResourceList; + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_RESOURCES\n"); - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_RESOURCES\n"); - ResourceList = ExAllocatePool(PagedPool, sizeof(CM_RESOURCE_LIST)); - if (!ResourceList) - { - DPRINT1("Usbhub: ExAllocatePool() failed\n"); - Status = STATUS_INSUFFICIENT_RESOURCES; - } - else - { - ResourceList->Count = 0; - Information = (ULONG_PTR)ResourceList; - Status = STATUS_SUCCESS; - } + Information = Irp->IoStatus.Information; + Status = Irp->IoStatus.Status; break; } - case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: /* 0x0b */ + case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: { - PIO_RESOURCE_REQUIREMENTS_LIST ResourceList; + DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n"); + + Information = Irp->IoStatus.Information; + Status = Irp->IoStatus.Status; + break; + } + case IRP_MN_QUERY_DEVICE_TEXT: + { + Status = USBHUB_PdoQueryDeviceText(DeviceObject, Irp, &Information); + break; + } + case IRP_MN_QUERY_ID: + { + Status = USBHUB_PdoQueryId(DeviceObject, Irp, &Information); + break; + } + case IRP_MN_QUERY_BUS_INFORMATION: + { + PPNP_BUS_INFORMATION BusInfo; + BusInfo = (PPNP_BUS_INFORMATION)ExAllocatePool(PagedPool, sizeof(PNP_BUS_INFORMATION)); + RtlCopyMemory(&BusInfo->BusTypeGuid, + &GUID_BUS_TYPE_USB, + sizeof(BusInfo->BusTypeGuid)); + BusInfo->LegacyBusType = PNPBus; + // FIXME + BusInfo->BusNumber = 0; + Information = (ULONG_PTR)BusInfo; + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_REMOVE_DEVICE: + { + PHUB_DEVICE_EXTENSION HubDeviceExtension = (PHUB_DEVICE_EXTENSION)UsbChildExtension->ParentDeviceObject->DeviceExtension; + PUSB_BUS_INTERFACE_HUB_V5 HubInterface = &HubDeviceExtension->HubInterface; - DPRINT1("Usbhub: IRP_MJ_PNP / IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n"); - ResourceList = ExAllocatePool(PagedPool, sizeof(IO_RESOURCE_REQUIREMENTS_LIST)); - if (!ResourceList) + DPRINT("IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n"); + + /* remove us from pdo list */ + bFound = FALSE; + for(Index = 0; Index < USB_MAXCHILDREN; Index++) { - DPRINT1("Usbhub: ExAllocatePool() failed\n"); + if (HubDeviceExtension->ChildDeviceObject[Index] == DeviceObject) + { + /* Remove the device */ + Status = HubInterface->RemoveUsbDevice(HubDeviceExtension->UsbDInterface.BusContext, UsbChildExtension->UsbDeviceHandle, 0); + + /* FIXME handle error */ + ASSERT(Status == STATUS_SUCCESS); + + /* remove us */ + HubDeviceExtension->ChildDeviceObject[Index] = NULL; + bFound = TRUE; + break; + } + } + + /* Complete the IRP */ + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + if (bFound) + { + /* Delete the device object */ + IoDeleteDevice(DeviceObject); + } + + return STATUS_SUCCESS; + } + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + /* only target relations are supported */ + if (Stack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation) + { + /* not supported */ + Status = Irp->IoStatus.Status; + break; + } + + /* allocate device relations */ + DeviceRelation = (PDEVICE_RELATIONS)ExAllocatePool(NonPagedPool, sizeof(DEVICE_RELATIONS)); + if (!DeviceRelation) + { + /* no memory */ Status = STATUS_INSUFFICIENT_RESOURCES; + break; } - else - { - RtlZeroMemory(ResourceList, sizeof(IO_RESOURCE_REQUIREMENTS_LIST)); - ResourceList->ListSize = sizeof(IO_RESOURCE_REQUIREMENTS_LIST); - ResourceList->AlternativeLists = 1; - ResourceList->List->Version = 1; - ResourceList->List->Revision = 1; - ResourceList->List->Count = 0; - Information = (ULONG_PTR)ResourceList; - Status = STATUS_SUCCESS; - } + + /* init device relation */ + DeviceRelation->Count = 1; + DeviceRelation->Objects[0] = DeviceObject; + ObReferenceObject(DeviceRelation->Objects[0]); + + /* store result */ + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelation; + Status = STATUS_SUCCESS; break; } - case IRP_MN_QUERY_DEVICE_TEXT: /* 0x0c */ + case IRP_MN_QUERY_STOP_DEVICE: + case IRP_MN_QUERY_REMOVE_DEVICE: { - Status = UsbhubPdoQueryDeviceText(DeviceObject, Irp, &Information); - break; - } - case IRP_MN_QUERY_ID: /* 0x13 */ - { - Status = UsbhubPdoQueryId(DeviceObject, Irp, &Information); + /* Sure, no problem */ + Status = STATUS_SUCCESS; + Information = 0; break; } default: { - /* We can't forward request to the lower driver, because - * we are a Pdo, so we don't have lower driver... - */ - DPRINT1("Usbhub: IRP_MJ_PNP / unknown minor function 0x%lx\n", MinorFunction); + DPRINT1("PDO IRP_MJ_PNP / unknown minor function 0x%lx\n", MinorFunction); Information = Irp->IoStatus.Information; Status = Irp->IoStatus.Status; } diff --git a/reactos/drivers/usb/usbhub/usbhub.c b/reactos/drivers/usb/usbhub/usbhub.c index 2a9e54b8894..5e14a97d984 100644 --- a/reactos/drivers/usb/usbhub/usbhub.c +++ b/reactos/drivers/usb/usbhub/usbhub.c @@ -1,84 +1,125 @@ /* - * ReactOS USB hub driver - * Copyright (C) 2004 Aleksey Bragin - * (C) 2005 Mark Tempel - * (C) 2005 Herv� Poussineau - * (C) 2010 Michael Martin - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * + * PROJECT: ReactOS Universal Serial Bus Hub Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbhub/fdo.c + * PURPOSE: UsbHub Driver + * PROGRAMMERS: + * Hervé Poussineau (hpoussin@reactos.org) + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) */ -/* INCLUDES *******************************************************************/ -//#define NDEBUG #include "usbhub.h" -/* PUBLIC AND PRIVATE FUNCTIONS ***********************************************/ +NTSTATUS NTAPI +USBHUB_Create( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + DPRINT("USBHUB: IRP_MJ_CREATE\n"); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} NTSTATUS NTAPI -UsbhubAddDevice( - IN PDRIVER_OBJECT DriverObject, - IN PDEVICE_OBJECT Pdo) +USBHUB_Close( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) { - PDEVICE_OBJECT Fdo; - PHUB_DEVICE_EXTENSION DeviceExtension; + DPRINT("USBHUB: IRP_MJ_CLOSE\n"); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI +USBHUB_Cleanup( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + DPRINT("USBHUB: IRP_MJ_CLEANUP\n"); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + + +NTSTATUS NTAPI +USBHUB_AddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) +{ + PDEVICE_OBJECT DeviceObject; + PHUB_DEVICE_EXTENSION HubDeviceExtension; NTSTATUS Status; - + DPRINT("USBHUB: AddDevice\n"); + // + // Create the Device Object + // Status = IoCreateDevice(DriverObject, - sizeof(HUB_DEVICE_EXTENSION), - NULL, /* DeviceName */ - FILE_DEVICE_BUS_EXTENDER, - FILE_AUTOGENERATED_DEVICE_NAME, - FALSE, - &Fdo); + sizeof(HUB_DEVICE_EXTENSION), + NULL, + FILE_DEVICE_BUS_EXTENDER, + FILE_AUTOGENERATED_DEVICE_NAME, + FALSE, + &DeviceObject); if (!NT_SUCCESS(Status)) { - DPRINT1("Usbhub: IoCreateDevice() failed with status 0x%08lx\n", Status); + DPRINT1("USBHUB: IoCreateDevice() failed with status 0x%08lx\n", Status); return Status; } - // zerofill device extension - DeviceExtension = (PHUB_DEVICE_EXTENSION)Fdo->DeviceExtension; - RtlZeroMemory(DeviceExtension, sizeof(HUB_DEVICE_EXTENSION)); + // + // Zero Hub Extension + // + HubDeviceExtension = (PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + RtlZeroMemory(HubDeviceExtension, sizeof(HUB_DEVICE_EXTENSION)); - DeviceExtension->IsFDO = TRUE; - Fdo->Flags |= DO_POWER_PAGABLE; + // + // Set this to Fdo + // + HubDeviceExtension->Common.IsFDO = TRUE; + DeviceObject->Flags |= DO_POWER_PAGABLE; + + // + // initialize reset complete event + // + KeInitializeEvent(&HubDeviceExtension->ResetComplete, NotificationEvent, FALSE); + + // + // Attached to lower device + // //Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice); - DeviceExtension->LowerDevice = IoAttachDeviceToDeviceStack(Fdo, Pdo); + HubDeviceExtension->LowerDeviceObject = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject); if (!NT_SUCCESS(Status)) { - DPRINT("Usbhub: IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status); - IoDeleteDevice(Fdo); + DPRINT1("USBHUB: IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status); + IoDeleteDevice(DeviceObject); return Status; } - Fdo->Flags |= DO_BUFFERED_IO; - Fdo->Flags &= ~DO_DEVICE_INITIALIZING; + DeviceObject->Flags |= DO_BUFFERED_IO; + DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; } static NTSTATUS NTAPI -IrpStub( +USBHUB_IrpStub( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS Status; - if (((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsFDO) + if (((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Common.IsFDO) { DPRINT1("Usbhub: FDO stub for major function 0x%lx\n", IoGetCurrentIrpStackLocation(Irp)->MajorFunction); @@ -86,10 +127,10 @@ IrpStub( } else { - /* We can't forward request to the lower driver, because - * we are a Pdo, so we don't have lower driver... - */ - DPRINT1("Usbhub: PDO stub for major function 0x%lx\n", + // + // Cant forward as we are the PDO! + // + DPRINT1("USBHUB: ERROR- PDO stub for major function 0x%lx\n", IoGetCurrentIrpStackLocation(Irp)->MajorFunction); #ifndef NDEBUG DbgBreakPoint(); @@ -101,58 +142,78 @@ IrpStub( return Status; } -static NTSTATUS NTAPI -DispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) + +NTSTATUS NTAPI +USBHUB_DispatchDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) { - DPRINT1("Usbhub: DispatchDeviceControl\n"); - if (((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsFDO) - return UsbhubDeviceControlFdo(DeviceObject, Irp); + DPRINT("Usbhub: DispatchDeviceControl\n"); + if (((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Common.IsFDO) + return USBHUB_FdoHandleDeviceControl(DeviceObject, Irp); else - return IrpStub(DeviceObject, Irp); + return USBHUB_IrpStub(DeviceObject, Irp); } -static NTSTATUS NTAPI -DispatchInternalDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) +NTSTATUS NTAPI +USBHUB_DispatchInternalDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) { - DPRINT1("Usbhub: DispatchInternalDeviceControl\n"); - if (((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsFDO) - return IrpStub(DeviceObject, Irp); + DPRINT("Usbhub: DispatchInternalDeviceControl\n"); + if (((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Common.IsFDO) + return USBHUB_IrpStub(DeviceObject, Irp); else - return UsbhubInternalDeviceControlPdo(DeviceObject, Irp); + return USBHUB_PdoHandleInternalDeviceControl(DeviceObject, Irp); } -static NTSTATUS NTAPI -DispatchPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) +NTSTATUS NTAPI +USBHUB_DispatchPnp( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) { - DPRINT1("Usbhub: DispatchPnp\n"); - if (((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsFDO) - return UsbhubPnpFdo(DeviceObject, Irp); + DPRINT("USBHUB: DispatchPnp\n"); + if (((PHUB_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Common.IsFDO) + return USBHUB_FdoHandlePnp(DeviceObject, Irp); else - return UsbhubPnpPdo(DeviceObject, Irp); + return USBHUB_PdoHandlePnp(DeviceObject, Irp); } -/* - * Standard DriverEntry method. - */ +NTSTATUS NTAPI +USBHUB_DispatchPower( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_SUPPORTED; +} + +VOID +NTAPI +USBHUB_Unload( + IN PDRIVER_OBJECT DriverObject) +{ + UNIMPLEMENTED +} + + NTSTATUS NTAPI DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { - ULONG i; + DriverObject->DriverExtension->AddDevice = USBHUB_AddDevice; + DriverObject->DriverUnload = USBHUB_Unload; - DriverObject->DriverExtension->AddDevice = UsbhubAddDevice; - DPRINT1("Usbhub: DriverEntry\n"); + DPRINT("USBHUB: DriverEntry\n"); - for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) - DriverObject->MajorFunction[i] = IrpStub; - - DriverObject->MajorFunction[IRP_MJ_CREATE] = UsbhubCreate; - DriverObject->MajorFunction[IRP_MJ_CLOSE] = UsbhubClose; - DriverObject->MajorFunction[IRP_MJ_CLEANUP] = UsbhubCleanup; - DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDeviceControl; - DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = DispatchInternalDeviceControl; - DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp; + DriverObject->MajorFunction[IRP_MJ_CREATE] = USBHUB_Create; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = USBHUB_Close; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = USBHUB_Cleanup; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = USBHUB_DispatchDeviceControl; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = USBHUB_DispatchInternalDeviceControl; + DriverObject->MajorFunction[IRP_MJ_PNP] = USBHUB_DispatchPnp; + DriverObject->MajorFunction[IRP_MJ_POWER] =USBHUB_DispatchPower; return STATUS_SUCCESS; } diff --git a/reactos/drivers/usb/usbhub/usbhub.h b/reactos/drivers/usb/usbhub/usbhub.h index 988c6fa2f2e..14ea5159a74 100644 --- a/reactos/drivers/usb/usbhub/usbhub.h +++ b/reactos/drivers/usb/usbhub/usbhub.h @@ -1,15 +1,23 @@ +#pragma once + +#define NDEBUG +#include #include +#include #include #include #include #include +#include +#include #include //BROKEN: #include +#include #define USB_HUB_TAG 'hbsu' #define USB_MAXCHILDREN 127 -/* Lifted from broken header above */ +// Lifted from broken header above #define C_HUB_LOCAL_POWER 0 #define C_HUB_OVER_CURRENT 1 #define PORT_CONNECTION 0 @@ -27,86 +35,61 @@ #define PORT_TEST 21 #define PORT_INDICATOR 22 -typedef struct _USB_ENDPOINT +typedef struct _PORT_STATUS_CHANGE { - ULONG Flags; - LIST_ENTRY UrbList; - struct _USB_INTERFACE *Interface; - USB_ENDPOINT_DESCRIPTOR EndPointDescriptor; -} USB_ENDPOINT, *PUSB_ENDPOINT; + USHORT Status; + USHORT Change; +} PORT_STATUS_CHANGE, *PPORT_STATUS_CHANGE; -typedef struct _USB_INTERFACE -{ - struct _USB_CONFIGURATION *Config; - USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; - USB_ENDPOINT *EndPoints[]; -} USB_INTERFACE, *PUSB_INTERFACE; - -typedef struct _USB_CONFIGURATION -{ - struct _USB_DEVICE *Device; - USB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; - USB_INTERFACE *Interfaces[]; -} USB_CONFIGURATION, *PUSB_CONFIGURATION; - -typedef struct _USB_DEVICE -{ - UCHAR Address; - ULONG Port; - PVOID ParentDevice; - BOOLEAN IsHub; - USB_DEVICE_SPEED DeviceSpeed; - USB_DEVICE_TYPE DeviceType; - USB_DEVICE_DESCRIPTOR DeviceDescriptor; - USB_CONFIGURATION *ActiveConfig; - USB_INTERFACE *ActiveInterface; - USB_CONFIGURATION **Configs; -} USB_DEVICE, *PUSB_DEVICE; - -typedef struct _WORKITEMDATA +typedef struct _WORK_ITEM_DATA { WORK_QUEUE_ITEM WorkItem; PVOID Context; -} WORKITEMDATA, *PWORKITEMDATA; +} WORK_ITEM_DATA, *PWORK_ITEM_DATA; + +typedef struct +{ + BOOLEAN IsFDO; +} COMMON_DEVICE_EXTENSION, *PCOMMON_DEVICE_EXTENSION; typedef struct _HUB_CHILDDEVICE_EXTENSION { - BOOLEAN IsFDO; - PDEVICE_OBJECT Parent; - PWCHAR DeviceId; // REG_SZ - PWCHAR InstanceId; // REG_SZ - PWCHAR HardwareIds; // REG_MULTI_SZ - PWCHAR CompatibleIds; // REG_MULTI_SZ - PWCHAR TextDescription; + COMMON_DEVICE_EXTENSION Common; + PDEVICE_OBJECT ParentDeviceObject; + PUSB_DEVICE_HANDLE UsbDeviceHandle; + ULONG PortNumber; + UNICODE_STRING usDeviceId; + UNICODE_STRING usInstanceId; + UNICODE_STRING usHardwareIds; + UNICODE_STRING usCompatibleIds; + UNICODE_STRING usTextDescription; + UNICODE_STRING usLocationInformation; + USB_DEVICE_DESCRIPTOR DeviceDesc; + PUSB_CONFIGURATION_DESCRIPTOR FullConfigDesc; UNICODE_STRING SymbolicLinkName; } HUB_CHILDDEVICE_EXTENSION, *PHUB_CHILDDEVICE_EXTENSION; typedef struct _HUB_DEVICE_EXTENSION { - BOOLEAN IsFDO; - USB_DEVICE* dev; - PDEVICE_OBJECT LowerDevice; + COMMON_DEVICE_EXTENSION Common; + PDEVICE_OBJECT LowerDeviceObject; ULONG ChildCount; - PDEVICE_OBJECT Children[USB_MAXCHILDREN]; + PDEVICE_OBJECT ChildDeviceObject[USB_MAXCHILDREN]; + PDEVICE_OBJECT RootHubPhysicalDeviceObject; + PDEVICE_OBJECT RootHubFunctionalDeviceObject; - PUSB_DEVICE UsbChildren[USB_MAXCHILDREN]; + ULONG NumberOfHubs; + KEVENT ResetComplete; - PUSB_DEVICE RootHubUsbDevice; - - PDEVICE_OBJECT RootHubPdo; - PDEVICE_OBJECT RootHubFdo; - - ULONG HubCount; - - USHORT PortStatus[256]; - URB Urb; + PORT_STATUS_CHANGE *PortStatusChange; + URB PendingSCEUrb; + PIRP PendingSCEIrp; USB_BUS_INTERFACE_HUB_V5 HubInterface; USB_BUS_INTERFACE_USBDI_V2 UsbDInterface; USB_HUB_DESCRIPTOR HubDescriptor; USB_DEVICE_DESCRIPTOR HubDeviceDescriptor; - USB_CONFIGURATION_DESCRIPTOR HubConfigDescriptor; USB_INTERFACE_DESCRIPTOR HubInterfaceDescriptor; USB_ENDPOINT_DESCRIPTOR HubEndPointDescriptor; @@ -116,66 +99,74 @@ typedef struct _HUB_DEVICE_EXTENSION USBD_CONFIGURATION_HANDLE ConfigurationHandle; USBD_PIPE_HANDLE PipeHandle; + PVOID RootHubHandle; UNICODE_STRING SymbolicLinkName; } HUB_DEVICE_EXTENSION, *PHUB_DEVICE_EXTENSION; -/* createclose.c */ +// createclose.c NTSTATUS NTAPI -UsbhubCreate( +USBHUB_Create( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); NTSTATUS NTAPI -UsbhubClose( +USBHUB_Close( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); NTSTATUS NTAPI -UsbhubCleanup( +USBHUB_Cleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); -/* fdo.c */ -NTSTATUS NTAPI -UsbhubPnpFdo( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +// fdo.c +NTSTATUS +USBHUB_FdoHandleDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp); NTSTATUS -UsbhubDeviceControlFdo( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +USBHUB_FdoHandlePnp( + PDEVICE_OBJECT DeviceObject, + PIRP Irp); -/* misc.c */ +// misc.c NTSTATUS ForwardIrpAndWait( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); -NTSTATUS NTAPI +NTSTATUS ForwardIrpAndForget( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); +// pdo.c NTSTATUS -UsbhubDuplicateUnicodeString( - OUT PUNICODE_STRING Destination, - IN PUNICODE_STRING Source, - IN POOL_TYPE PoolType); +USBHUB_PdoHandlePnp( + PDEVICE_OBJECT DeviceObject, + PIRP Irp); NTSTATUS -UsbhubInitMultiSzString( - OUT PUNICODE_STRING Destination, - .../* list of PCSZ */); +USBHUB_PdoHandleInternalDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp); -/* pdo.c */ -NTSTATUS NTAPI -UsbhubPnpPdo( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +VOID +DumpDeviceDescriptor( + PUSB_DEVICE_DESCRIPTOR DeviceDescriptor); + +VOID +DumpConfigurationDescriptor( + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor); + +VOID +DumpFullConfigurationDescriptor( + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor); NTSTATUS -UsbhubInternalDeviceControlPdo( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +GetPortStatusAndChange( + IN PDEVICE_OBJECT RootHubDeviceObject, + IN ULONG PortId, + OUT PPORT_STATUS_CHANGE StatusChange); diff --git a/reactos/drivers/usb/usbhub/usbhub.rbuild b/reactos/drivers/usb/usbhub/usbhub.rbuild index c4c3bbd29a7..8b9543c7ef3 100644 --- a/reactos/drivers/usb/usbhub/usbhub.rbuild +++ b/reactos/drivers/usb/usbhub/usbhub.rbuild @@ -1,10 +1,13 @@ + + + include ntoskrnl hal usbd - createclose.c + pseh fdo.c misc.c pdo.c diff --git a/reactos/drivers/usb/usbhub/usbhub.rc b/reactos/drivers/usb/usbhub/usbhub.rc index 92314b2b0ce..aa3f2694311 100644 --- a/reactos/drivers/usb/usbhub/usbhub.rc +++ b/reactos/drivers/usb/usbhub/usbhub.rc @@ -1,5 +1,5 @@ #define REACTOS_VERSION_DLL -#define REACTOS_STR_FILE_DESCRIPTION "USB Hub Driver\0" +#define REACTOS_STR_FILE_DESCRIPTION "USBHUB Driver API\0" #define REACTOS_STR_INTERNAL_NAME "usbhub\0" #define REACTOS_STR_ORIGINAL_FILENAME "usbhub.sys\0" #include diff --git a/reactos/drivers/usb/usbohci/CMakeLists.txt b/reactos/drivers/usb/usbohci/CMakeLists.txt new file mode 100644 index 00000000000..53d605c7832 --- /dev/null +++ b/reactos/drivers/usb/usbohci/CMakeLists.txt @@ -0,0 +1,34 @@ + +set_cpp() + +remove_definitions(-D_WIN32_WINNT=0x502) +add_definitions(-D_WIN32_WINNT=0x600) + +add_library(usbohci SHARED + usbohci.cpp + usb_device.cpp + usb_request.cpp + usb_queue.cpp + hcd_controller.cpp + hardware.cpp + misc.cpp + purecall.cpp + hub_controller.cpp + memory_manager.cpp + usbohci.rc) + +target_link_libraries(usbohci + libcntpr + ${PSEH_LIB}) + +if(MSVC) + set_target_properties(usbohci PROPERTIES COMPILE_FLAGS "/GR-") +else() + target_link_libraries(usbohci -lgcc) + set_target_properties(usbohci PROPERTIES COMPILE_FLAGS "-fno-exceptions -fno-rtti") +endif(MSVC) + +set_module_type(usbohci kernelmodedriver) +add_importlibs(usbohci ntoskrnl hal usbd) + +add_cd_file(TARGET usbohci DESTINATION reactos/system32/drivers NO_CAB FOR all) \ No newline at end of file diff --git a/reactos/drivers/usb/usbohci/hardware.cpp b/reactos/drivers/usb/usbohci/hardware.cpp new file mode 100644 index 00000000000..87eb79f554f --- /dev/null +++ b/reactos/drivers/usb/usbohci/hardware.cpp @@ -0,0 +1,1707 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbohci/hcd_controller.cpp + * PURPOSE: USB OHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#define INITGUID +#include "usbohci.h" +#include "hardware.h" + +typedef VOID __stdcall HD_INIT_CALLBACK(IN PVOID CallBackContext); + +BOOLEAN +NTAPI +InterruptServiceRoutine( + IN PKINTERRUPT Interrupt, + IN PVOID ServiceContext); + +VOID +NTAPI +OhciDefferedRoutine( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2); + +VOID +NTAPI +StatusChangeWorkItemRoutine(PVOID Context); + +class CUSBHardwareDevice : public IUSBHardwareDevice +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + // com + NTSTATUS Initialize(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT FunctionalDeviceObject, PDEVICE_OBJECT PhysicalDeviceObject, PDEVICE_OBJECT LowerDeviceObject); + NTSTATUS PnpStart(PCM_RESOURCE_LIST RawResources, PCM_RESOURCE_LIST TranslatedResources); + NTSTATUS PnpStop(void); + NTSTATUS HandlePower(PIRP Irp); + NTSTATUS GetDeviceDetails(PUSHORT VendorId, PUSHORT DeviceId, PULONG NumberOfPorts, PULONG Speed); + NTSTATUS GetBulkHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor); + NTSTATUS GetControlHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor); + NTSTATUS GetInterruptEndpointDescriptors(struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptor); + NTSTATUS GetIsochronousHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor); + VOID HeadEndpointDescriptorModified(ULONG HeadType); + + + NTSTATUS GetDMA(OUT struct IDMAMemoryManager **m_DmaManager); + NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue); + + NTSTATUS StartController(); + NTSTATUS StopController(); + NTSTATUS ResetController(); + NTSTATUS ResetPort(ULONG PortIndex); + + NTSTATUS GetPortStatus(ULONG PortId, OUT USHORT *PortStatus, OUT USHORT *PortChange); + NTSTATUS ClearPortStatus(ULONG PortId, ULONG Status); + NTSTATUS SetPortFeature(ULONG PortId, ULONG Feature); + + VOID SetStatusChangeEndpointCallBack(PVOID CallBack, PVOID Context); + + KIRQL AcquireDeviceLock(void); + VOID ReleaseDeviceLock(KIRQL OldLevel); + virtual VOID GetCurrentFrameNumber(PULONG FrameNumber); + // local + BOOLEAN InterruptService(); + NTSTATUS InitializeController(); + NTSTATUS AllocateEndpointDescriptor(OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor); + + // friend function + friend BOOLEAN NTAPI InterruptServiceRoutine(IN PKINTERRUPT Interrupt, IN PVOID ServiceContext); + friend VOID NTAPI OhciDefferedRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2); + friend VOID NTAPI StatusChangeWorkItemRoutine(PVOID Context); + // constructor / destructor + CUSBHardwareDevice(IUnknown *OuterUnknown){} + virtual ~CUSBHardwareDevice(){} + +protected: + LONG m_Ref; // reference count + PDRIVER_OBJECT m_DriverObject; // driver object + PDEVICE_OBJECT m_PhysicalDeviceObject; // pdo + PDEVICE_OBJECT m_FunctionalDeviceObject; // fdo (hcd controller) + PDEVICE_OBJECT m_NextDeviceObject; // lower device object + KSPIN_LOCK m_Lock; // hardware lock + PKINTERRUPT m_Interrupt; // interrupt object + KDPC m_IntDpcObject; // dpc object for deferred isr processing + PVOID VirtualBase; // virtual base for memory manager + PHYSICAL_ADDRESS PhysicalAddress; // physical base for memory manager + PULONG m_Base; // OHCI operational port base registers + PDMA_ADAPTER m_Adapter; // dma adapter object + ULONG m_MapRegisters; // map registers count + USHORT m_VendorID; // vendor id + USHORT m_DeviceID; // device id + PUSBQUEUE m_UsbQueue; // usb request queue + POHCIHCCA m_HCCA; // hcca virtual base + PHYSICAL_ADDRESS m_HCCAPhysicalAddress; // hcca physical address + POHCI_ENDPOINT_DESCRIPTOR m_ControlEndpointDescriptor; // dummy control endpoint descriptor + POHCI_ENDPOINT_DESCRIPTOR m_BulkEndpointDescriptor; // dummy control endpoint descriptor + POHCI_ENDPOINT_DESCRIPTOR m_IsoEndpointDescriptor; // iso endpoint descriptor + POHCI_ENDPOINT_DESCRIPTOR m_InterruptEndpoints[OHCI_STATIC_ENDPOINT_COUNT]; // endpoints for interrupt / iso transfers + ULONG m_NumberOfPorts; // number of ports + PDMAMEMORYMANAGER m_MemoryManager; // memory manager + HD_INIT_CALLBACK* m_SCECallBack; // status change callback routine + PVOID m_SCEContext; // status change callback routine context + WORK_QUEUE_ITEM m_StatusChangeWorkItem; // work item for status change callback + ULONG m_SyncFramePhysAddr; // periodic frame list physical address + ULONG m_IntervalValue; // periodic interval value +}; + +//================================================================================================= +// COM +// +NTSTATUS +STDMETHODCALLTYPE +CUSBHardwareDevice::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + if (IsEqualGUIDAligned(refiid, IID_IUnknown)) + { + *Output = PVOID(PUNKNOWN(this)); + PUNKNOWN(*Output)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS +CUSBHardwareDevice::Initialize( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT FunctionalDeviceObject, + PDEVICE_OBJECT PhysicalDeviceObject, + PDEVICE_OBJECT LowerDeviceObject) +{ + BUS_INTERFACE_STANDARD BusInterface; + PCI_COMMON_CONFIG PciConfig; + NTSTATUS Status; + ULONG BytesRead; + + DPRINT("CUSBHardwareDevice::Initialize\n"); + + // + // Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors. + // + Status = CreateDMAMemoryManager(&m_MemoryManager); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create DMAMemoryManager Object\n"); + return Status; + } + + // + // Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules + // + Status = CreateUSBQueue(&m_UsbQueue); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create UsbQueue!\n"); + return Status; + } + + // + // store device objects + // + m_DriverObject = DriverObject; + m_FunctionalDeviceObject = FunctionalDeviceObject; + m_PhysicalDeviceObject = PhysicalDeviceObject; + m_NextDeviceObject = LowerDeviceObject; + + // + // initialize device lock + // + KeInitializeSpinLock(&m_Lock); + + // + // intialize status change work item + // + ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this)); + + m_VendorID = 0; + m_DeviceID = 0; + + Status = GetBusInterface(PhysicalDeviceObject, &BusInterface); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to get BusInteface!\n"); + return Status; + } + + BytesRead = (*BusInterface.GetBusData)(BusInterface.Context, + PCI_WHICHSPACE_CONFIG, + &PciConfig, + 0, + PCI_COMMON_HDR_LENGTH); + + if (BytesRead != PCI_COMMON_HDR_LENGTH) + { + DPRINT1("Failed to get pci config information!\n"); + return STATUS_SUCCESS; + } + + m_VendorID = PciConfig.VendorID; + m_DeviceID = PciConfig.DeviceID; + + if (PciConfig.Command & PCI_ENABLE_BUS_MASTER) + { + // + // master is enabled + // + return STATUS_SUCCESS; + } + + DPRINT1("PCI Configuration shows this as a non Bus Mastering device! Enabling...\n"); + + PciConfig.Command |= PCI_ENABLE_BUS_MASTER; + BusInterface.SetBusData(BusInterface.Context, PCI_WHICHSPACE_CONFIG, &PciConfig, 0, PCI_COMMON_HDR_LENGTH); + + BytesRead = (*BusInterface.GetBusData)(BusInterface.Context, + PCI_WHICHSPACE_CONFIG, + &PciConfig, + 0, + PCI_COMMON_HDR_LENGTH); + + if (BytesRead != PCI_COMMON_HDR_LENGTH) + { + DPRINT1("Failed to get pci config information!\n"); + ASSERT(FALSE); + return STATUS_SUCCESS; + } + + if (!(PciConfig.Command & PCI_ENABLE_BUS_MASTER)) + { + DPRINT1("Failed to enable master\n"); + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::PnpStart( + PCM_RESOURCE_LIST RawResources, + PCM_RESOURCE_LIST TranslatedResources) +{ + ULONG Index; + PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor; + DEVICE_DESCRIPTION DeviceDescription; + PVOID ResourceBase; + NTSTATUS Status; + ULONG Version; + + DPRINT("CUSBHardwareDevice::PnpStart\n"); + for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++) + { + // + // get resource descriptor + // + ResourceDescriptor = &TranslatedResources->List[0].PartialResourceList.PartialDescriptors[Index]; + + switch(ResourceDescriptor->Type) + { + case CmResourceTypeInterrupt: + { + KeInitializeDpc(&m_IntDpcObject, + OhciDefferedRoutine, + this); + + Status = IoConnectInterrupt(&m_Interrupt, + InterruptServiceRoutine, + (PVOID)this, + NULL, + ResourceDescriptor->u.Interrupt.Vector, + (KIRQL)ResourceDescriptor->u.Interrupt.Level, + (KIRQL)ResourceDescriptor->u.Interrupt.Level, + (KINTERRUPT_MODE)(ResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED), + (ResourceDescriptor->ShareDisposition != CmResourceShareDeviceExclusive), + ResourceDescriptor->u.Interrupt.Affinity, + FALSE); + + if (!NT_SUCCESS(Status)) + { + // + // failed to register interrupt + // + DPRINT1("IoConnect Interrupt failed with %x\n", Status); + return Status; + } + break; + } + case CmResourceTypeMemory: + { + // + // get resource base + // + ResourceBase = MmMapIoSpace(ResourceDescriptor->u.Memory.Start, ResourceDescriptor->u.Memory.Length, MmNonCached); + if (!ResourceBase) + { + // + // failed to map registers + // + DPRINT1("MmMapIoSpace failed\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Get controllers capabilities + // + Version = READ_REGISTER_ULONG((PULONG)((ULONG_PTR)ResourceBase + OHCI_REVISION_OFFSET)); + + DPRINT("Version %x\n", Version); + + // + // Store Resource base + // + m_Base = (PULONG)ResourceBase; + break; + } + } + } + + + // + // zero device description + // + RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION)); + + // + // initialize device description + // + DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION; + DeviceDescription.Master = TRUE; + DeviceDescription.ScatterGather = TRUE; + DeviceDescription.Dma32BitAddresses = TRUE; + DeviceDescription.DmaWidth = Width32Bits; + DeviceDescription.InterfaceType = PCIBus; + DeviceDescription.MaximumLength = MAXULONG; + + // + // get dma adapter + // + m_Adapter = IoGetDmaAdapter(m_PhysicalDeviceObject, &DeviceDescription, &m_MapRegisters); + if (!m_Adapter) + { + // + // failed to get dma adapter + // + DPRINT1("Failed to acquire dma adapter\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Create Common Buffer + // + VirtualBase = m_Adapter->DmaOperations->AllocateCommonBuffer(m_Adapter, + PAGE_SIZE * 4, + &PhysicalAddress, + FALSE); + if (!VirtualBase) + { + DPRINT1("Failed to allocate a common buffer\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the DMAMemoryManager + // + Status = m_MemoryManager->Initialize(this, &m_Lock, PAGE_SIZE * 4, VirtualBase, PhysicalAddress, 32); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to initialize the DMAMemoryManager\n"); + return Status; + } + + // + // initializes the controller + // + Status = InitializeController(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to Initialize the controller \n"); + return Status; + } + + // + // Initialize the UsbQueue now that we have an AdapterObject. + // + Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to Initialize the UsbQueue\n"); + return Status; + } + + + // + // Stop the controller before modifying schedules + // + Status = StopController(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to stop the controller \n"); + return Status; + } + + + // + // Start the controller + // + DPRINT1("Starting Controller\n"); + Status = StartController(); + + // + // done + // + return Status; +} + +NTSTATUS +CUSBHardwareDevice::PnpStop(void) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CUSBHardwareDevice::HandlePower( + PIRP Irp) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CUSBHardwareDevice::GetDeviceDetails( + OUT OPTIONAL PUSHORT VendorId, + OUT OPTIONAL PUSHORT DeviceId, + OUT OPTIONAL PULONG NumberOfPorts, + OUT OPTIONAL PULONG Speed) +{ + if (VendorId) + { + // + // get vendor + // + *VendorId = m_VendorID; + } + + if (DeviceId) + { + // + // get device id + // + *DeviceId = m_DeviceID; + } + + if (NumberOfPorts) + { + // + // get number of ports + // + *NumberOfPorts = m_NumberOfPorts; + } + + if (Speed) + { + // + // speed is 0x100 + // + *Speed = 0x100; + } + + return STATUS_SUCCESS; +} + +NTSTATUS CUSBHardwareDevice::GetDMA( + OUT struct IDMAMemoryManager **OutDMAMemoryManager) +{ + if (!m_MemoryManager) + return STATUS_UNSUCCESSFUL; + *OutDMAMemoryManager = m_MemoryManager; + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::GetUSBQueue( + OUT struct IUSBQueue **OutUsbQueue) +{ + if (!m_UsbQueue) + return STATUS_UNSUCCESSFUL; + *OutUsbQueue = m_UsbQueue; + return STATUS_SUCCESS; +} + + +NTSTATUS +CUSBHardwareDevice::StartController(void) +{ + ULONG Control, NumberOfPorts, Index, Descriptor, FrameInterval, Periodic; + + // + // lets write physical address of dummy control endpoint descriptor + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_HEAD_ED_OFFSET), m_ControlEndpointDescriptor->PhysicalAddress.LowPart); + + // + // lets write physical address of dummy bulk endpoint descriptor + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_BULK_HEAD_ED_OFFSET), m_BulkEndpointDescriptor->PhysicalAddress.LowPart); + + // + // get frame interval + // + FrameInterval = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET)); + FrameInterval = ((FrameInterval & OHCI_FRAME_INTERVAL_TOGGLE) ^ OHCI_FRAME_INTERVAL_TOGGLE); + DPRINT1("FrameInterval %x IntervalValue %x\n", FrameInterval, m_IntervalValue); + FrameInterval |= OHCI_FSMPS(m_IntervalValue) | m_IntervalValue; + DPRINT1("FrameInterval %x\n", FrameInterval); + + // + // write frame interval + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval); + + // + // write address of HCCA + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), m_HCCAPhysicalAddress.LowPart); + + // + // now enable the interrupts + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), OHCI_NORMAL_INTERRUPTS | OHCI_MASTER_INTERRUPT_ENABLE); + + // + // enable all queues + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_ENABLE_LIST); + + // + // 90 % periodic + // + Periodic = OHCI_PERIODIC(m_IntervalValue); + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_PERIODIC_START_OFFSET), Periodic); + DPRINT("Periodic Start %x\n", Periodic); + + // + // start the controller + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_ENABLE_LIST | OHCI_CONTROL_BULK_RATIO_1_4 | OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL); + + // + // wait a bit + // + KeStallExecutionProcessor(100); + + // + // is the controller started + // + Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)); + + // + // assert that the controller has been started + // + ASSERT((Control & OHCI_HC_FUNCTIONAL_STATE_MASK) == OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL); + ASSERT((Control & OHCI_ENABLE_LIST) == OHCI_ENABLE_LIST); + DPRINT1("Control %x\n", Control); + + // + // read descriptor + // + Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET)); + + // + // no over current protection + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor | OHCI_RH_NO_OVER_CURRENT_PROTECTION); + + // + // enable power on all ports + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_STATUS_OFFSET), OHCI_RH_LOCAL_POWER_STATUS_CHANGE); + + // + // wait a bit + // + KeStallExecutionProcessor(10); + + // + // write descriptor + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET), Descriptor); + + // + // retrieve number of ports + // + for(Index = 0; Index < 10; Index++) + { + // + // wait a bit + // + KeStallExecutionProcessor(10); + + // + // read descriptor + // + Descriptor = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET)); + + // + // get number of ports + // + NumberOfPorts = OHCI_RH_GET_PORT_COUNT(Descriptor); + + // + // check if we have received the ports + // + if (NumberOfPorts) + break; + } + + // + // sanity check + // + ASSERT(NumberOfPorts < OHCI_MAX_PORT_COUNT); + + // + // store number of ports + // + m_NumberOfPorts = NumberOfPorts; + + // + // print out number ports + // + DPRINT1("NumberOfPorts %lu\n", m_NumberOfPorts); + + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::AllocateEndpointDescriptor( + OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor) +{ + POHCI_ENDPOINT_DESCRIPTOR Descriptor; + PHYSICAL_ADDRESS DescriptorAddress; + NTSTATUS Status; + + // + // allocate descriptor + // + Status = m_MemoryManager->Allocate(sizeof(OHCI_ENDPOINT_DESCRIPTOR), (PVOID*)&Descriptor, &DescriptorAddress); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate descriptor + // + return Status; + } + + // + // intialize descriptor + // + Descriptor->Flags = OHCI_ENDPOINT_SKIP; + Descriptor->HeadPhysicalDescriptor = 0; + Descriptor->NextPhysicalEndpoint = 0; + Descriptor->TailPhysicalDescriptor = 0; + Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart; + + // + // store result + // + *OutDescriptor = Descriptor; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::GetBulkHeadEndpointDescriptor( + struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) +{ + *OutDescriptor = m_BulkEndpointDescriptor; + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::GetInterruptEndpointDescriptors( + struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptor) +{ + *OutDescriptor = m_InterruptEndpoints; + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::GetIsochronousHeadEndpointDescriptor( + struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) +{ + // + // get descriptor + // + *OutDescriptor = m_IsoEndpointDescriptor; + return STATUS_SUCCESS; +} + +VOID +CUSBHardwareDevice::HeadEndpointDescriptorModified( + ULONG Type) +{ + ULONG Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET)); + + if (Type == USB_ENDPOINT_TYPE_CONTROL) + { + // + // notify controller + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), Value | OHCI_CONTROL_LIST_FILLED); + } + else if (Type == USB_ENDPOINT_TYPE_BULK) + { + // + // notify controller + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), Value | OHCI_BULK_LIST_FILLED); + } +} + +NTSTATUS +CUSBHardwareDevice::GetControlHeadEndpointDescriptor( + struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) +{ + *OutDescriptor = m_ControlEndpointDescriptor; + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::InitializeController() +{ + NTSTATUS Status; + ULONG Index, Interval, IntervalIndex, InsertIndex; + POHCI_ENDPOINT_DESCRIPTOR Descriptor; + + // + // first allocate the hcca area + // + Status = m_MemoryManager->Allocate(sizeof(OHCIHCCA), (PVOID*)&m_HCCA, &m_HCCAPhysicalAddress); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + // + // now allocate an endpoint for control transfers + // this endpoint will never be removed + // + Status = AllocateEndpointDescriptor(&m_ControlEndpointDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + // + // now allocate an endpoint for bulk transfers + // this endpoint will never be removed + // + Status = AllocateEndpointDescriptor(&m_BulkEndpointDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + // + // now allocate an endpoint for iso transfers + // this endpoint will never be removed + // + Status = AllocateEndpointDescriptor(&m_IsoEndpointDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + // + // now allocate endpoint descriptors for iso / interrupt transfers interval is 1,2,4,8,16,32 + // + for(Index = 0; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++) + { + // + // allocate endpoint descriptor + // + Status = AllocateEndpointDescriptor(&Descriptor); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + // + // save in array + // + m_InterruptEndpoints[Index] = Descriptor; + } + + + // + // now link the descriptors, taken from Haiku + // + Interval = OHCI_BIGGEST_INTERVAL; + IntervalIndex = OHCI_STATIC_ENDPOINT_COUNT - 1; + while (Interval > 1) + { + InsertIndex = Interval / 2; + while (InsertIndex < OHCI_BIGGEST_INTERVAL) + { + // + // assign endpoint address + // + m_HCCA->InterruptTable[InsertIndex] = m_InterruptEndpoints[IntervalIndex]->PhysicalAddress.LowPart; + InsertIndex += Interval; + } + + IntervalIndex--; + Interval /= 2; + } + + // + // link all endpoint descriptors to first descriptor in array + // + m_HCCA->InterruptTable[0] = m_InterruptEndpoints[0]->PhysicalAddress.LowPart; + for (Index = 1; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++) + { + // + // link descriptor + // + m_InterruptEndpoints[Index]->NextPhysicalEndpoint = m_InterruptEndpoints[0]->PhysicalAddress.LowPart; + } + + // + // Now link the first endpoint to the isochronous endpoint + // + m_InterruptEndpoints[0]->NextPhysicalEndpoint = m_IsoEndpointDescriptor->PhysicalAddress.LowPart; + + // + // set iso endpoint type + // + m_IsoEndpointDescriptor->Flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::StopController(void) +{ + ULONG Control, Reset, Status; + ULONG Index, FrameInterval; + + // + // alignment check + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET), 0xFFFFFFFF); + Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_HCCA_OFFSET)); + //ASSERT((m_HCCAPhysicalAddress.QuadPart & Control) == Control); + + + // + // check context + // + Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)); + + if ((Control & OHCI_INTERRUPT_ROUTING)) + { + // + // read command status + // + Status = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET)); + + // + // change ownership + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), Status | OHCI_OWNERSHIP_CHANGE_REQUEST); + for(Index = 0; Index < 100; Index++) + { + // + // wait a bit + // + KeStallExecutionProcessor(100); + + // + // check control + // + Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)); + if (!(Control & OHCI_INTERRUPT_ROUTING)) + { + // + // acquired ownership + // + break; + } + } + + // + // if the ownership is still not changed, perform reset + // + if (Control & OHCI_INTERRUPT_ROUTING) + { + DPRINT1("SMM not responding\n"); + } + else + { + DPRINT("SMM has given up ownership\n"); + } + } + else + { + // + // read contents of control register + // + Control = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)) & OHCI_HC_FUNCTIONAL_STATE_MASK); + DPRINT("Controller State %x\n", Control); + + if (Control != OHCI_HC_FUNCTIONAL_STATE_RESET) + { + // + // OHCI 5.1.1.3.4, no SMM, BIOS active + // + if (Control != OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL) + { + // + // lets resume + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESUME); + Index = 0; + do + { + // + // wait untill its resumed + // + KeStallExecutionProcessor(10); + + // + // check control register + // + Control = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)) & OHCI_HC_FUNCTIONAL_STATE_MASK); + if (Control & OHCI_HC_FUNCTIONAL_STATE_RESUME) + { + // + // it has resumed + // + break; + } + + // + // check for time outs + // + Index++; + if(Index > 100) + { + DPRINT1("Failed to resume controller\n"); + break; + } + }while(TRUE); + } + } + else + { + // + // 5.1.1.3.5 OHCI, no SMM, no BIOS + // + Index = 0; + + // + // some controllers also depend on this + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET); + do + { + // + // wait untill its reset + // + KeStallExecutionProcessor(10); + + // + // check control register + // + Control = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)) & OHCI_HC_FUNCTIONAL_STATE_MASK); + if (Control == OHCI_HC_FUNCTIONAL_STATE_RESET) + { + // + // it has reset + // + break; + } + + // + // check for time outs + // + Index++; + if(Index > 100) + { + DPRINT1("Failed to reset controller\n"); + break; + } + + }while(TRUE); + } + } + + // + // read from interval + // + FrameInterval = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET)); + + // + // store interval value for later + // + m_IntervalValue = OHCI_GET_INTERVAL_VALUE(FrameInterval); + + DPRINT1("FrameInterval %x Interval %x\n", FrameInterval, m_IntervalValue); + + // + // now reset controller + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET), OHCI_HOST_CONTROLLER_RESET); + + // + // reset time is 10ms + // + for(Index = 0; Index < 10; Index++) + { + // + // wait a bit + // + KeStallExecutionProcessor(10); + + // + // read command status + // + Reset = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_COMMAND_STATUS_OFFSET)); + + // + // was reset bit cleared + // + if ((Reset & OHCI_HOST_CONTROLLER_RESET) == 0) + { + // + // restore the frame interval register + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval); + + // + // controller completed reset + // + return STATUS_SUCCESS; + } + } + + // + // failed to reset controller + // + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS +CUSBHardwareDevice::ResetController(void) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CUSBHardwareDevice::ResetPort( + IN ULONG PortIndex) +{ + ASSERT(FALSE); + + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::GetPortStatus( + ULONG PortId, + OUT USHORT *PortStatus, + OUT USHORT *PortChange) +{ + ULONG Value; + + if (PortId > m_NumberOfPorts) + return STATUS_UNSUCCESSFUL; + + // init result variables + *PortStatus = 0; + *PortChange = 0; + + // + // read port status + // + Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId))); + DPRINT("GetPortStatus PortId %x Value %x\n", PortId, Value); + + // connected + if (Value & OHCI_RH_PORTSTATUS_CCS) + *PortStatus |= USB_PORT_STATUS_CONNECT; + + // did a device connect? + if (Value & OHCI_RH_PORTSTATUS_CSC) + *PortChange |= USB_PORT_STATUS_CONNECT; + + // port enabled + if (Value & OHCI_RH_PORTSTATUS_PES) + *PortStatus |= USB_PORT_STATUS_ENABLE; + + // port disconnect or hardware error + if (Value & OHCI_RH_PORTSTATUS_PESC) + *PortChange |= USB_PORT_STATUS_CONNECT; + + // port suspend + if (Value & OHCI_RH_PORTSTATUS_PSS) + *PortStatus |= USB_PORT_STATUS_SUSPEND; + + // port suspend + if (Value & OHCI_RH_PORTSTATUS_PSSC) + *PortChange |= USB_PORT_STATUS_ENABLE; + + // port reset started (change bit only set at completion) + if (Value & OHCI_RH_PORTSTATUS_PRS) + { + *PortStatus |= USB_PORT_STATUS_RESET; + *PortChange |= USB_PORT_STATUS_RESET; + } + + // port reset ended (change bit only set at completion) + if (Value & OHCI_RH_PORTSTATUS_PRSC) + *PortChange |= USB_PORT_STATUS_RESET; + + // low speed device + if (Value & OHCI_RH_PORTSTATUS_LSDA) + *PortStatus |= USB_PORT_STATUS_LOW_SPEED; + + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBHardwareDevice::ClearPortStatus( + ULONG PortId, + ULONG Status) +{ + ULONG Value; + + DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Status); + + if (PortId > m_NumberOfPorts) + return STATUS_UNSUCCESSFUL; + + Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId))); + KeStallExecutionProcessor(100); + + if (Status == C_PORT_RESET) + { + // + // sanity checks + // + ASSERT((Value & OHCI_RH_PORTSTATUS_PRSC)); + + // + // clear reset bit complete + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRSC); + + // + // sanity check + // + ASSERT((Value & OHCI_RH_PORTSTATUS_PES)); + } + + if (Status == C_PORT_CONNECTION || Status == C_PORT_ENABLE) + { + // + // clear change bits + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_CSC | OHCI_RH_PORTSTATUS_PESC); + + // + // wait for port to stabilize + // + if (Status == C_PORT_CONNECTION) + { + LARGE_INTEGER Timeout; + + // + // delay is 100 ms + // + Timeout.QuadPart = 100; + DPRINT1("Waiting %d milliseconds for port to stabilize after connection\n", Timeout.LowPart); + + // + // convert to 100 ns units (absolute) + // + Timeout.QuadPart *= -10000; + + // + // perform the wait + // + KeDelayExecutionThread(KernelMode, FALSE, &Timeout); + } + } + + // + // re-enable root hub change + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_INTERRUPT_ENABLE_OFFSET), OHCI_ROOT_HUB_STATUS_CHANGE); + + return STATUS_SUCCESS; +} + + +NTSTATUS +CUSBHardwareDevice::SetPortFeature( + ULONG PortId, + ULONG Feature) +{ + ULONG Value; + + DPRINT("CUSBHardwareDevice::SetPortFeature PortId %x Feature %x\n", PortId, Feature); + + // + // read port status + // + Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId))); + + + if (Feature == PORT_ENABLE) + { + // + // enable port + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PES); + return STATUS_SUCCESS; + } + else if (Feature == PORT_POWER) + { + LARGE_INTEGER Timeout; + + // + // enable power + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PPS); + + // + // read descriptor A for the delay data + // + Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_DESCRIPTOR_A_OFFSET)); + + // + // compute the delay + // + Timeout.QuadPart = OHCI_RH_GET_POWER_ON_TO_POWER_GOOD_TIME(Value); + + // + // delay is multiplied by 2 ms + // + Timeout.QuadPart *= 2; + DPRINT1("Waiting %d milliseconds for port power up\n", Timeout.LowPart); + + // + // convert to 100 ns units (absolute) + // + Timeout.QuadPart *= -10000; + + // + // perform the wait + // + KeDelayExecutionThread(KernelMode, FALSE, &Timeout); + + return STATUS_SUCCESS; + } + else if (Feature == PORT_SUSPEND) + { + // + // enable port + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PSS); + return STATUS_SUCCESS; + } + else if (Feature == PORT_RESET) + { + LARGE_INTEGER Timeout; + + // + // assert + // + ASSERT((Value & OHCI_RH_PORTSTATUS_CCS)); + + // + // reset port + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId)), OHCI_RH_PORTSTATUS_PRS); + + do + { + // + // read port status + // + Value = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_RH_PORT_STATUS(PortId))); + + if ((Value & OHCI_RH_PORTSTATUS_PRS) == 0) + { + // + // reset is complete + // + break; + } + + // + // wait a bit + // + KeStallExecutionProcessor(100); + }while(TRUE); + + // + // delay is 10 ms + // + Timeout.QuadPart = 10; + DPRINT1("Waiting %d milliseconds for port to recover after reset\n", Timeout.LowPart); + + // + // convert to 100 ns units (absolute) + // + Timeout.QuadPart *= -10000; + + // + // perform the wait + // + KeDelayExecutionThread(KernelMode, FALSE, &Timeout); + + // + // is there a status change callback + // + if (m_SCECallBack != NULL) + { + // + // issue callback + // + m_SCECallBack(m_SCEContext); + } + return STATUS_SUCCESS; + } + return STATUS_SUCCESS; +} + + + +VOID +CUSBHardwareDevice::SetStatusChangeEndpointCallBack( + PVOID CallBack, + PVOID Context) +{ + m_SCECallBack = (HD_INIT_CALLBACK*)CallBack; + m_SCEContext = Context; +} + +KIRQL +CUSBHardwareDevice::AcquireDeviceLock(void) +{ + KIRQL OldLevel; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // return old irql + // + return OldLevel; +} + +VOID +CUSBHardwareDevice::GetCurrentFrameNumber( + PULONG FrameNumber) +{ + ULONG Control; + ULONG Number; + + + Number = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_NUMBER_OFFSET)); + DPRINT("FrameNumberInterval %x Frame %x\n", Number, m_HCCA->CurrentFrameNumber); + + // + // remove reserved bits + // + Number &= 0xFFFF; + + // + // store frame number + // + *FrameNumber = Number; + + // + // is the controller started + // + Control = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_CONTROL_OFFSET)); + ASSERT((Control & OHCI_ENABLE_LIST) == OHCI_ENABLE_LIST); + + +} + +VOID +CUSBHardwareDevice::ReleaseDeviceLock( + KIRQL OldLevel) +{ + KeReleaseSpinLock(&m_Lock, OldLevel); +} + +BOOLEAN +NTAPI +InterruptServiceRoutine( + IN PKINTERRUPT Interrupt, + IN PVOID ServiceContext) +{ + CUSBHardwareDevice *This; + ULONG DoneHead, Status, Acknowledge = 0; + + // + // get context + // + This = (CUSBHardwareDevice*) ServiceContext; + + DPRINT("InterruptServiceRoutine\n"); + + // + // get done head + // + DoneHead = This->m_HCCA->DoneHead; + + // + // check if zero + // + if (DoneHead == 0) + { + // + // the interrupt was not caused by DoneHead update + // check if something important happened + // + DPRINT("InterruptStatus %x InterruptEnable %x\n", READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET)), + READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_ENABLE_OFFSET))); + Status = READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET)) & READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_ENABLE_OFFSET)) & (~OHCI_WRITEBACK_DONE_HEAD); + if (Status == 0) + { + // + // nothing happened, appears to be shared interrupt + // + return FALSE; + } + } + else + { + // + // DoneHead update happened, check if there are other events too + // + Status = OHCI_WRITEBACK_DONE_HEAD; + + // + // since ed descriptors are 16 byte aligned, the controller sets the lower bits if there were other interrupt requests + // + if (DoneHead & OHCI_DONE_INTERRUPTS) + { + // + // get other events + // + Status |= READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET)) & READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_ENABLE_OFFSET)); + } + } + + // + // sanity check + // + ASSERT(Status != 0); + + if (Status & OHCI_WRITEBACK_DONE_HEAD) + { + // + // head completed + // + Acknowledge |= OHCI_WRITEBACK_DONE_HEAD; + This->m_HCCA->DoneHead = 0; + } + + if (Status & OHCI_RESUME_DETECTED) + { + // + // resume + // + DPRINT1("InterruptServiceRoutine> Resume\n"); + Acknowledge |= OHCI_RESUME_DETECTED; + } + + + if (Status & OHCI_UNRECOVERABLE_ERROR) + { + DPRINT1("InterruptServiceRoutine> Controller error\n"); + + // + // halt controller + // + ASSERT(FALSE); + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_CONTROL_OFFSET), OHCI_HC_FUNCTIONAL_STATE_RESET); + } + + if (Status & OHCI_ROOT_HUB_STATUS_CHANGE) + { + // + // disable interrupt as it will fire untill the port has been reset + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_DISABLE_OFFSET), OHCI_ROOT_HUB_STATUS_CHANGE); + Acknowledge |= OHCI_ROOT_HUB_STATUS_CHANGE; + } + + // + // is there something to acknowledge + // + if (Acknowledge) + { + // + // ack change + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_INTERRUPT_STATUS_OFFSET), Acknowledge); + } + + // + // defer processing + // + DPRINT("Status %x Acknowledge %x FrameNumber %x\n", Status, Acknowledge, This->m_HCCA->CurrentFrameNumber); + KeInsertQueueDpc(&This->m_IntDpcObject, (PVOID)Status, (PVOID)(DoneHead & ~1)); + + // + // interrupt handled + // + return TRUE; +} + +VOID +NTAPI +OhciDefferedRoutine( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2) +{ + CUSBHardwareDevice *This; + ULONG CStatus, Index, PortStatus; + ULONG DoneHead; + + // + // get parameters + // + This = (CUSBHardwareDevice*)DeferredContext; + CStatus = (ULONG) SystemArgument1; + DoneHead = (ULONG)SystemArgument2; + + DPRINT("OhciDefferedRoutine Status %x\n", CStatus); + + if (CStatus & OHCI_WRITEBACK_DONE_HEAD) + { + // + // notify queue of event + // + This->m_UsbQueue->TransferDescriptorCompletionCallback(DoneHead); + } + if (CStatus & OHCI_ROOT_HUB_STATUS_CHANGE) + { + // + // device connected, lets check which port + // + for(Index = 0; Index < This->m_NumberOfPorts; Index++) + { + // + // read port status + // + PortStatus = READ_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_RH_PORT_STATUS(Index))); + + // + // check if there is a status change + // + if (PortStatus & OHCI_RH_PORTSTATUS_CSC) + { + // + // did a device connect + // + if (PortStatus & OHCI_RH_PORTSTATUS_CCS) + { + // + // device connected + // + DPRINT1("New device arrival at Port %d LowSpeed %x\n", Index, (PortStatus & OHCI_RH_PORTSTATUS_LSDA)); + + // + // enable port + // + WRITE_REGISTER_ULONG((PULONG)((PUCHAR)This->m_Base + OHCI_RH_PORT_STATUS(Index)), OHCI_RH_PORTSTATUS_PES); + } + else + { + // + // device disconnected + // + DPRINT1("Device disconnected at Port %x\n", Index); + } + + // + // is there a status change callback + // + if (This->m_SCECallBack != NULL) + { + // + // queue work item for processing + // + ExQueueWorkItem(&This->m_StatusChangeWorkItem, DelayedWorkQueue); + } + } + } + } + + +} + +VOID +NTAPI +StatusChangeWorkItemRoutine( + PVOID Context) +{ + // + // cast to hardware object + // + CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context; + + // + // is there a callback + // + if (This->m_SCECallBack) + { + // + // issue callback + // + This->m_SCECallBack(This->m_SCEContext); + } + +} + +NTSTATUS +CreateUSBHardware( + PUSBHARDWAREDEVICE *OutHardware) +{ + PUSBHARDWAREDEVICE This; + + This = new(NonPagedPool, TAG_USBOHCI) CUSBHardwareDevice(0); + + if (!This) + return STATUS_INSUFFICIENT_RESOURCES; + + This->AddRef(); + + // return result + *OutHardware = (PUSBHARDWAREDEVICE)This; + + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/usb/usbohci/hardware.h b/reactos/drivers/usb/usbohci/hardware.h new file mode 100644 index 00000000000..e88c0941bbc --- /dev/null +++ b/reactos/drivers/usb/usbohci/hardware.h @@ -0,0 +1,346 @@ +#pragma once + +#include + +// +// OHCI Operational Registers +// + +#define OHCI_REVISION_OFFSET (0x00) +#define OHCI_REVISION_LOW(rev) ((rev) & 0x0f) +#define OHCI_REVISION_HIGH(rev) (((rev) >> 4) & 0x03) + + +// +// OHCI Control Register +// +#define OHCI_CONTROL_OFFSET (0x004) +#define OHCI_CONTROL_BULK_SERVICE_RATIO_MASK (0x003) +#define OHCI_CONTROL_BULK_RATIO_1_1 (0x000) +#define OHCI_CONTROL_BULK_RATIO_1_2 (0x001) +#define OHCI_CONTROL_BULK_RATIO_1_3 (0x002) +#define OHCI_CONTROL_BULK_RATIO_1_4 (0x003) +#define OHCI_PERIODIC_LIST_ENABLE (0x004) +#define OHCI_ISOCHRONOUS_ENABLE (0x008) +#define OHCI_CONTROL_LIST_ENABLE (0x010) +#define OHCI_BULK_LIST_ENABLE (0x020) +#define OHCI_HC_FUNCTIONAL_STATE_MASK (0x0C0) +#define OHCI_HC_FUNCTIONAL_STATE_RESET (0x000) +#define OHCI_HC_FUNCTIONAL_STATE_RESUME (0x040) +#define OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL (0x080) +#define OHCI_HC_FUNCTIONAL_STATE_SUSPEND (0x0c0) +#define OHCI_INTERRUPT_ROUTING (0x100) +#define OHCI_REMOTE_WAKEUP_CONNECTED (0x200) +#define OHCI_REMORE_WAKEUP_ENABLED (0x400) + +// +// OHCI Command Status Register +// +#define OHCI_COMMAND_STATUS_OFFSET (0x08) +#define OHCI_HOST_CONTROLLER_RESET 0x00000001 +#define OHCI_CONTROL_LIST_FILLED 0x00000002 +#define OHCI_BULK_LIST_FILLED 0x00000004 +#define OHCI_OWNERSHIP_CHANGE_REQUEST 0x00000008 +#define OHCI_SCHEDULING_OVERRUN_COUNT_MASK 0x00030000 + + +// +// OHCI Interrupt Status Register +// +#define OHCI_INTERRUPT_STATUS_OFFSET 0x0c +#define OHCI_SCHEDULING_OVERRUN 0x00000001 +#define OHCI_WRITEBACK_DONE_HEAD 0x00000002 +#define OHCI_START_OF_FRAME 0x00000004 +#define OHCI_RESUME_DETECTED 0x00000008 +#define OHCI_UNRECOVERABLE_ERROR 0x00000010 +#define OHCI_FRAME_NUMBER_OVERFLOW 0x00000020 +#define OHCI_ROOT_HUB_STATUS_CHANGE 0x00000040 +#define OHCI_OWNERSHIP_CHANGE 0x40000000 +#define OHCI_MASTER_INTERRUPT_ENABLE 0x80000000 + + +// +// OHCI Interrupt Enable Register +// +#define OHCI_INTERRUPT_ENABLE_OFFSET 0x10 + +// +// OHCI Interrupt Enable Register +// +#define OHCI_INTERRUPT_DISABLE_OFFSET 0x14 + +// +// OHCI HCCA Register +// +#define OHCI_HCCA_OFFSET 0x18 +#define OHCI_PERIOD_CURRENT_ED_OFFSET 0x1c +#define OHCI_CONTROL_HEAD_ED_OFFSET 0x20 +#define OHCI_CONTROL_CURRENT_ED_OFFSET 0x24 +#define OHCI_BULK_HEAD_ED_OFFSET 0x28 + +// +// OHCI Root Hub Descriptor A register +// +#define OHCI_RH_DESCRIPTOR_A_OFFSET 0x48 +#define OHCI_RH_GET_PORT_COUNT(s) ((s) & 0xff) +#define OHCI_RH_POWER_SWITCHING_MODE 0x0100 +#define OHCI_RH_NO_POWER_SWITCHING 0x0200 +#define OHCI_RH_DEVICE_TYPE 0x0400 +#define OHCI_RH_OVER_CURRENT_PROTECTION_MODE 0x0800 +#define OHCI_RH_NO_OVER_CURRENT_PROTECTION 0x1000 +#define OHCI_RH_GET_POWER_ON_TO_POWER_GOOD_TIME(s) ((s) >> 24) + +// +// Frame interval register (section 7.3.1) +// +#define OHCI_FRAME_INTERVAL_OFFSET 0x34 +#define OHCI_GET_INTERVAL_VALUE(s) ((s) & 0x3fff) +#define OHCI_GET_FS_LARGEST_DATA_PACKET(s) (((s) >> 16) & 0x7fff) +#define OHCI_FRAME_INTERVAL_TOGGLE 0x80000000 + +// +// frame interval +// +#define OHCI_FRAME_INTERVAL_NUMBER_OFFSET 0x3C + +// +// periodic start register +// +#define OHCI_PERIODIC_START_OFFSET 0x40 +#define OHCI_PERIODIC(i) ((i) * 9 / 10) + +// +// Root Hub Descriptor B register (section 7.4.2) +// + +#define OHCI_RH_DESCRIPTOR_B 0x4c + +// +// Root Hub status register (section 7.4.3) +// +#define OHCI_RH_STATUS_OFFSET 0x50 +#define OHCI_RH_LOCAL_POWER_STATUS 0x00000001 +#define OHCI_RH_OVER_CURRENT_INDICATOR 0x00000002 +#define OHCI_RH_DEVICE_REMOTE_WAKEUP_ENABLE 0x00008000 +#define OHCI_RH_LOCAL_POWER_STATUS_CHANGE 0x00010000 +#define OHCI_RH_OVER_CURRENT_INDICATOR_CHANGE 0x00020000 +#define OHCI_RH_CLEAR_REMOTE_WAKEUP_ENABLE 0x80000000 + +// +// Root Hub port status (n) register (section 7.4.4) +// +#define OHCI_RH_PORT_STATUS(n) (0x54 + (n) * 4)// 0 based indexing +#define OHCI_RH_PORTSTATUS_CCS 0x00000001 +#define OHCI_RH_PORTSTATUS_PES 0x00000002 +#define OHCI_RH_PORTSTATUS_PSS 0x00000004 +#define OHCI_RH_PORTSTATUS_POCI 0x00000008 +#define OHCI_RH_PORTSTATUS_PRS 0x00000010 +#define OHCI_RH_PORTSTATUS_PPS 0x00000100 +#define OHCI_RH_PORTSTATUS_LSDA 0x00000200 +#define OHCI_RH_PORTSTATUS_CSC 0x00010000 +#define OHCI_RH_PORTSTATUS_PESC 0x00020000 +#define OHCI_RH_PORTSTATUS_PSSC 0x00040000 +#define OHCI_RH_PORTSTATUS_OCIC 0x00080000 +#define OHCI_RH_PORTSTATUS_PRSC 0x00100000 + +// +// Enable List +// + +#define OHCI_ENABLE_LIST (OHCI_PERIODIC_LIST_ENABLE \ + | OHCI_ISOCHRONOUS_ENABLE \ + | OHCI_CONTROL_LIST_ENABLE \ + | OHCI_BULK_LIST_ENABLE) + +// +// All interupts +// +#define OHCI_ALL_INTERRUPTS (OHCI_SCHEDULING_OVERRUN \ + | OHCI_WRITEBACK_DONE_HEAD \ + | OHCI_START_OF_FRAME \ + | OHCI_RESUME_DETECTED \ + | OHCI_UNRECOVERABLE_ERROR \ + | OHCI_FRAME_NUMBER_OVERFLOW \ + | OHCI_ROOT_HUB_STATUS_CHANGE \ + | OHCI_OWNERSHIP_CHANGE) + +// +// All normal interupts +// +#define OHCI_NORMAL_INTERRUPTS (OHCI_SCHEDULING_OVERRUN \ + | OHCI_WRITEBACK_DONE_HEAD \ + | OHCI_RESUME_DETECTED \ + | OHCI_UNRECOVERABLE_ERROR \ + | OHCI_ROOT_HUB_STATUS_CHANGE \ + | OHCI_OWNERSHIP_CHANGE) + +// +// FSMPS +// + +#define OHCI_FSMPS(i) (((i - 210) * 6 / 7) << 16) + +// +// Periodic +// + +#define OHCI_PERIODIC(i) ((i) * 9 / 10) + +// -------------------------------- +// HCCA structure (section 4.4) +// 256 bytes aligned +// -------------------------------- + +#define OHCI_NUMBER_OF_INTERRUPTS 32 +#define OHCI_STATIC_ENDPOINT_COUNT 6 +#define OHCI_BIGGEST_INTERVAL 32 + +typedef struct +{ + ULONG InterruptTable[OHCI_NUMBER_OF_INTERRUPTS]; + ULONG CurrentFrameNumber; + ULONG DoneHead; + UCHAR Reserved[120]; +}OHCIHCCA, *POHCIHCCA; + +#define OHCI_DONE_INTERRUPTS 1 +#define OHCI_HCCA_SIZE 256 +#define OHCI_HCCA_ALIGN 256 +#define OHCI_PAGE_SIZE 0x1000 +#define OHCI_PAGE(x) ((x) &~ 0xfff) +#define OHCI_PAGE_OFFSET(x) ((x) & 0xfff) + + +typedef struct _OHCI_ENDPOINT_DESCRIPTOR +{ + // Hardware part + ULONG Flags; + ULONG TailPhysicalDescriptor; + ULONG HeadPhysicalDescriptor; + ULONG NextPhysicalEndpoint; + + // Software part + PHYSICAL_ADDRESS PhysicalAddress; + PVOID HeadLogicalDescriptor; + PVOID NextDescriptor; + PVOID Request; +}OHCI_ENDPOINT_DESCRIPTOR, *POHCI_ENDPOINT_DESCRIPTOR; + + +#define OHCI_ENDPOINT_SKIP 0x00004000 +#define OHCI_ENDPOINT_SET_DEVICE_ADDRESS(s) (s) +#define OHCI_ENDPOINT_GET_DEVICE_ADDRESS(s) ((s) & 0xFF) +#define OHCI_ENDPOINT_GET_ENDPOINT_NUMBER(s) (((s) >> 7) & 0xf) +#define OHCI_ENDPOINT_SET_ENDPOINT_NUMBER(s) ((s) << 7) +#define OHCI_ENDPOINT_GET_MAX_PACKET_SIZE(s) (((s) >> 16) & 0x07ff) +#define OHCI_ENDPOINT_SET_MAX_PACKET_SIZE(s) ((s) << 16) +#define OHCI_ENDPOINT_LOW_SPEED 0x00002000 +#define OHCI_ENDPOINT_FULL_SPEED 0x00000000 +#define OHCI_ENDPOINT_DIRECTION_OUT 0x00000800 +#define OHCI_ENDPOINT_DIRECTION_IN 0x00001000 +#define OHCI_ENDPOINT_GENERAL_FORMAT 0x00000000 +#define OHCI_ENDPOINT_ISOCHRONOUS_FORMAT 0x00008000 +#define OHCI_ENDPOINT_HEAD_MASK 0xfffffffc +#define OHCI_ENDPOINT_HALTED 0x00000001 +// +// Maximum port count set by OHCI +// +#define OHCI_MAX_PORT_COUNT 15 + + +typedef struct +{ + ULONG PortStatus; + ULONG PortChange; +}OHCI_PORT_STATUS; + + +typedef struct +{ + // Hardware part 16 bytes + ULONG Flags; // Flags field + ULONG BufferPhysical; // Physical buffer pointer + ULONG NextPhysicalDescriptor; // Physical pointer next descriptor + ULONG LastPhysicalByteAddress; // Physical pointer to buffer end + // Software part + PHYSICAL_ADDRESS PhysicalAddress; // Physical address of this descriptor + PVOID NextLogicalDescriptor; + ULONG BufferSize; // Size of the buffer + PVOID BufferLogical; // Logical pointer to the buffer +}OHCI_GENERAL_TD, *POHCI_GENERAL_TD; + + +#define OHCI_TD_BUFFER_ROUNDING 0x00040000 +#define OHCI_TD_DIRECTION_PID_MASK 0x00180000 +#define OHCI_TD_DIRECTION_PID_SETUP 0x00000000 +#define OHCI_TD_DIRECTION_PID_OUT 0x00080000 +#define OHCI_TD_DIRECTION_PID_IN 0x00100000 +#define OHCI_TD_GET_DELAY_INTERRUPT(x) (((x) >> 21) & 7) +#define OHCI_TD_SET_DELAY_INTERRUPT(x) ((x) << 21) +#define OHCI_TD_INTERRUPT_MASK 0x00e00000 +#define OHCI_TD_TOGGLE_CARRY 0x00000000 +#define OHCI_TD_TOGGLE_0 0x02000000 +#define OHCI_TD_TOGGLE_1 0x03000000 +#define OHCI_TD_TOGGLE_MASK 0x03000000 +#define OHCI_TD_GET_ERROR_COUNT(x) (((x) >> 26) & 3) +#define OHCI_TD_GET_CONDITION_CODE(x) ((x) >> 28) +#define OHCI_TD_SET_CONDITION_CODE(x) ((x) << 28) +#define OHCI_TD_CONDITION_CODE_MASK 0xf0000000 + +#define OHCI_TD_INTERRUPT_IMMEDIATE 0x00 +#define OHCI_TD_INTERRUPT_NONE 0x07 + +#define OHCI_TD_CONDITION_NO_ERROR 0x00 +#define OHCI_TD_CONDITION_CRC_ERROR 0x01 +#define OHCI_TD_CONDITION_BIT_STUFFING 0x02 +#define OHCI_TD_CONDITION_TOGGLE_MISMATCH 0x03 +#define OHCI_TD_CONDITION_STALL 0x04 +#define OHCI_TD_CONDITION_NO_RESPONSE 0x05 +#define OHCI_TD_CONDITION_PID_CHECK_FAILURE 0x06 +#define OHCI_TD_CONDITION_UNEXPECTED_PID 0x07 +#define OHCI_TD_CONDITION_DATA_OVERRUN 0x08 +#define OHCI_TD_CONDITION_DATA_UNDERRUN 0x09 +#define OHCI_TD_CONDITION_BUFFER_OVERRUN 0x0c +#define OHCI_TD_CONDITION_BUFFER_UNDERRUN 0x0d +#define OHCI_TD_CONDITION_NOT_ACCESSED 0x0f + +// -------------------------------- +// Isochronous transfer descriptor structure (section 4.3.2) +// -------------------------------- + +#define OHCI_ITD_NOFFSET 8 + +typedef struct _OHCI_ISO_TD_ +{ + + // Hardware part 32 byte + ULONG Flags; + ULONG BufferPhysical; // Physical page number of byte 0 + ULONG NextPhysicalDescriptor; // Next isochronous transfer descriptor + ULONG LastPhysicalByteAddress; // Physical buffer end + USHORT Offset[OHCI_ITD_NOFFSET]; // Buffer offsets + + // Software part + PHYSICAL_ADDRESS PhysicalAddress; // Physical address of this descriptor + struct _OHCI_ISO_TD_ * NextLogicalDescriptor; // Logical pointer next descriptor +}OHCI_ISO_TD, *POHCI_ISO_TD; + +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, Flags) == 0); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, BufferPhysical) == 4); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, NextPhysicalDescriptor) == 8); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, LastPhysicalByteAddress) == 12); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, Offset) == 16); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, PhysicalAddress) == 32); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, NextLogicalDescriptor) == 40); +C_ASSERT(sizeof(OHCI_ISO_TD) == 48); + +#define OHCI_ITD_GET_STARTING_FRAME(x) ((x) & 0x0000ffff) +#define OHCI_ITD_SET_STARTING_FRAME(x) ((x) & 0xffff) +#define OHCI_ITD_GET_DELAY_INTERRUPT(x) (((x) >> 21) & 7) +#define OHCI_ITD_SET_DELAY_INTERRUPT(x) ((x) << 21) +#define OHCI_ITD_NO_INTERRUPT 0x00e00000 +#define OHCI_ITD_GET_FRAME_COUNT(x) ((((x) >> 24) & 7) + 1) +#define OHCI_ITD_SET_FRAME_COUNT(x) (((x) - 1) << 24) +#define OHCI_ITD_GET_CONDITION_CODE(x) ((x) >> 28) +#define OHCI_ITD_NO_CONDITION_CODE 0xf0000000 + diff --git a/reactos/drivers/usb/usbohci/hcd_controller.cpp b/reactos/drivers/usb/usbohci/hcd_controller.cpp new file mode 100644 index 00000000000..4e8019334ca --- /dev/null +++ b/reactos/drivers/usb/usbohci/hcd_controller.cpp @@ -0,0 +1,774 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbohci/hcd_controller.cpp + * PURPOSE: USB OHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#define INITGUID +#include "usbohci.h" + +class CHCDController : public IHCDController, + public IDispatchIrp +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + // IHCDController interface functions + NTSTATUS Initialize(IN PROOTHDCCONTROLLER RootHCDController, IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject); + + // IDispatchIrp interface functions + NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + + // local functions + NTSTATUS CreateFDO(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * OutDeviceObject); + NTSTATUS SetSymbolicLink(BOOLEAN Enable); + + // constructor / destructor + CHCDController(IUnknown *OuterUnknown){} + virtual ~CHCDController(){} + +protected: + LONG m_Ref; + PROOTHDCCONTROLLER m_RootController; + PDRIVER_OBJECT m_DriverObject; + PDEVICE_OBJECT m_PhysicalDeviceObject; + PDEVICE_OBJECT m_FunctionalDeviceObject; + PDEVICE_OBJECT m_NextDeviceObject; + PUSBHARDWAREDEVICE m_Hardware; + PHUBCONTROLLER m_HubController; + ULONG m_FDODeviceNumber; +}; + +//================================================================================================= +// COM +// +NTSTATUS +STDMETHODCALLTYPE +CHCDController::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} + +//------------------------------------------------------------------------------------------------- +NTSTATUS +CHCDController::Initialize( + IN PROOTHDCCONTROLLER RootHCDController, + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) +{ + NTSTATUS Status; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + + // + // create usb hardware + // + Status = CreateUSBHardware(&m_Hardware); + if (!NT_SUCCESS(Status)) + { + // + // failed to create hardware object + // + DPRINT1("Failed to create hardware object\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize members + // + m_DriverObject = DriverObject; + m_PhysicalDeviceObject = PhysicalDeviceObject; + m_RootController = RootHCDController; + + // + // create FDO + // + Status = CreateFDO(m_DriverObject, &m_FunctionalDeviceObject); + if (!NT_SUCCESS(Status)) + { + // + // failed to create PDO + // + return Status; + } + + // + // now attach to device stack + // + m_NextDeviceObject = IoAttachDeviceToDeviceStack(m_FunctionalDeviceObject, m_PhysicalDeviceObject); + if (!m_NextDeviceObject) + { + // + // failed to attach to device stack + // + IoDeleteDevice(m_FunctionalDeviceObject); + m_FunctionalDeviceObject = 0; + + return STATUS_NO_SUCH_DEVICE; + } + + // + // initialize hardware object + // + Status = m_Hardware->Initialize(m_DriverObject, m_FunctionalDeviceObject, m_PhysicalDeviceObject, m_NextDeviceObject); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to initialize hardware object %x\n", Status); + + // + // failed to initialize hardware object, detach from device stack + // + IoDetachDevice(m_NextDeviceObject); + + // + // now delete the device + // + IoDeleteDevice(m_FunctionalDeviceObject); + + // + // nullify pointers :) + // + m_FunctionalDeviceObject = 0; + m_NextDeviceObject = 0; + + return Status; + } + + + // + // set device flags + // + m_FunctionalDeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; + + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)m_FunctionalDeviceObject->DeviceExtension; + PC_ASSERT(DeviceExtension); + + // + // initialize device extension + // + DeviceExtension->IsFDO = TRUE; + DeviceExtension->IsHub = FALSE; + DeviceExtension->Dispatcher = PDISPATCHIRP(this); + + // + // device is initialized + // + m_FunctionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + + // + // is there a root controller + // + if (m_RootController) + { + // + // add reference + // + m_RootController->AddRef(); + + // + // register with controller + // + m_RootController->RegisterHCD(this); + } + + + // + // done + // + return STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------------------------- +NTSTATUS +CHCDController::HandleDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + PUSB_HCD_DRIVERKEY_NAME DriverKey; + ULONG ResultLength; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + PC_ASSERT(DeviceExtension->IsFDO); + + DPRINT("HandleDeviceControl>Type: IoCtl %x InputBufferLength %lu OutputBufferLength %lu\n", + IoStack->Parameters.DeviceIoControl.IoControlCode, + IoStack->Parameters.DeviceIoControl.InputBufferLength, + IoStack->Parameters.DeviceIoControl.OutputBufferLength); + + // + // perform ioctl for FDO + // + if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_HCD_DRIVERKEY_NAME) + { + // + // check if sizee is at least >= USB_HCD_DRIVERKEY_NAME + // + if(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(USB_HCD_DRIVERKEY_NAME)) + { + // + // get device property size + // + Status = IoGetDeviceProperty(m_PhysicalDeviceObject, DevicePropertyDriverKeyName, 0, NULL, &ResultLength); + + // + // get input buffer + // + DriverKey = (PUSB_HCD_DRIVERKEY_NAME)Irp->AssociatedIrp.SystemBuffer; + + // + // check result + // + if (Status == STATUS_BUFFER_TOO_SMALL) + { + // + // does the caller provide enough buffer space + // + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength >= ResultLength) + { + // + // it does + // + Status = IoGetDeviceProperty(m_PhysicalDeviceObject, DevicePropertyDriverKeyName, IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG), DriverKey->DriverKeyName, &ResultLength); + + if (NT_SUCCESS(Status)) + { + // + // informal debug print + // + DPRINT1("Result %S\n", DriverKey->DriverKeyName); + } + } + + // + // store result + // + DriverKey->ActualLength = ResultLength + FIELD_OFFSET(USB_HCD_DRIVERKEY_NAME, DriverKeyName) + sizeof(WCHAR); + Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength; + Status = STATUS_SUCCESS; + } + } + else + { + // + // buffer is certainly too small + // + Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = sizeof(USB_HCD_DRIVERKEY_NAME); + } + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_USB_GET_ROOT_HUB_NAME) + { + // + // check if sizee is at least >= USB_HCD_DRIVERKEY_NAME + // + if(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(USB_HCD_DRIVERKEY_NAME)) + { + // + // sanity check + // + PC_ASSERT(m_HubController); + + // + // get input buffer + // + DriverKey = (PUSB_HCD_DRIVERKEY_NAME)Irp->AssociatedIrp.SystemBuffer; + + // + // get symbolic link + // + Status = m_HubController->GetHubControllerSymbolicLink(IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG), DriverKey->DriverKeyName, &ResultLength); + + + if (NT_SUCCESS(Status)) + { + // + // null terminate it + // + PC_ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(ULONG) - sizeof(WCHAR) >= ResultLength); + + DriverKey->DriverKeyName[ResultLength / sizeof(WCHAR)] = L'\0'; + DPRINT1("Result %S\n", DriverKey->DriverKeyName); + } + + // + // store result + // + DriverKey->ActualLength = ResultLength + FIELD_OFFSET(USB_HCD_DRIVERKEY_NAME, DriverKeyName) + sizeof(WCHAR); + Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength; + Status = STATUS_SUCCESS; + } + else + { + // + // buffer is certainly too small + // + Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = sizeof(USB_HCD_DRIVERKEY_NAME); + } + } + + // + // complete the request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // done + // + return Status; +} + +NTSTATUS +CHCDController::HandlePnp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PCM_RESOURCE_LIST RawResourceList; + PCM_RESOURCE_LIST TranslatedResourceList; + PDEVICE_RELATIONS DeviceRelations; + NTSTATUS Status; + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + PC_ASSERT(DeviceExtension->IsFDO); + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + switch(IoStack->MinorFunction) + { + case IRP_MN_START_DEVICE: + { + DPRINT("CHCDController::HandlePnp IRP_MN_START FDO\n"); + + // + // first start lower device object + // + Status = SyncForwardIrp(m_NextDeviceObject, Irp); + + if (NT_SUCCESS(Status)) + { + // + // operation succeeded, lets start the device + // + RawResourceList = IoStack->Parameters.StartDevice.AllocatedResources; + TranslatedResourceList = IoStack->Parameters.StartDevice.AllocatedResourcesTranslated; + + if (m_Hardware) + { + // + // start the hardware + // + Status = m_Hardware->PnpStart(RawResourceList, TranslatedResourceList); + } + + // + // enable symbolic link + // + Status = SetSymbolicLink(TRUE); + } + + DPRINT("CHCDController::HandlePnp IRP_MN_START FDO: Status %x\n", Status); + break; + } + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + DPRINT("CHCDController::HandlePnp IRP_MN_QUERY_DEVICE_RELATIONS Type %lx\n", IoStack->Parameters.QueryDeviceRelations.Type); + + if (m_HubController == NULL) + { + // + // create hub controller + // + Status = CreateHubController(&m_HubController); + if (!NT_SUCCESS(Status)) + { + // + // failed to create hub controller + // + break; + } + + // + // initialize hub controller + // + Status = m_HubController->Initialize(m_DriverObject, PHCDCONTROLLER(this), m_Hardware, TRUE, 0 /* FIXME*/); + if (!NT_SUCCESS(Status)) + { + // + // failed to initialize hub controller + // + break; + } + + // + // add reference to prevent it from getting deleting while hub driver adds / removes references + // + m_HubController->AddRef(); + } + + if (IoStack->Parameters.QueryDeviceRelations.Type == BusRelations) + { + // + // allocate device relations + // + DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS)); + + if (!DeviceRelations) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // init device relations + // + DeviceRelations->Count = 1; + Status = m_HubController->GetHubControllerDeviceObject(&DeviceRelations->Objects [0]); + + // + // sanity check + // + PC_ASSERT(Status == STATUS_SUCCESS); + + ObReferenceObject(DeviceRelations->Objects [0]); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + Status = STATUS_SUCCESS; + } + else + { + // + // not supported + // + Status = STATUS_NOT_SUPPORTED; + } + break; + } + case IRP_MN_STOP_DEVICE: + { + DPRINT("CHCDController::HandlePnp IRP_MN_STOP_DEVICE\n"); + + if (m_Hardware) + { + // + // stop the hardware + // + Status = m_Hardware->PnpStop(); + } + else + { + // + // fake success + // + Status = STATUS_SUCCESS; + } + + if (NT_SUCCESS(Status)) + { + // + // stop lower device + // + Status = SyncForwardIrp(m_NextDeviceObject, Irp); + } + break; + } + case IRP_MN_QUERY_REMOVE_DEVICE: + case IRP_MN_QUERY_STOP_DEVICE: + { + // + // sure + // + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(m_NextDeviceObject, Irp); + } + case IRP_MN_REMOVE_DEVICE: + { + DPRINT("CHCDController::HandlePnp IRP_MN_REMOVE_DEVICE FDO\n"); + + // + // delete the symbolic link + // + SetSymbolicLink(FALSE); + + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + IoCallDriver(m_NextDeviceObject, Irp); + + // + // detach device from device stack + // + IoDetachDevice(m_NextDeviceObject); + + // + // delete device + // + IoDeleteDevice(m_FunctionalDeviceObject); + + return STATUS_SUCCESS; + } + default: + { + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(m_NextDeviceObject, Irp); + } + } + + // + // store result and complete request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} + +NTSTATUS +CHCDController::HandlePower( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + UNIMPLEMENTED + + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CHCDController::CreateFDO( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT * OutDeviceObject) +{ + WCHAR CharDeviceName[64]; + NTSTATUS Status; + ULONG UsbDeviceNumber = 0; + UNICODE_STRING DeviceName; + + while (TRUE) + { + // + // construct device name + // + swprintf(CharDeviceName, L"\\Device\\USBFDO-%d", UsbDeviceNumber); + + // + // initialize device name + // + RtlInitUnicodeString(&DeviceName, CharDeviceName); + + // + // create device + // + Status = IoCreateDevice(DriverObject, + sizeof(COMMON_DEVICE_EXTENSION), + &DeviceName, + FILE_DEVICE_CONTROLLER, + 0, + FALSE, + OutDeviceObject); + + // + // check for success + // + if (NT_SUCCESS(Status)) + break; + + // + // is there a device object with that same name + // + if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION)) + { + // + // Try the next name + // + UsbDeviceNumber++; + continue; + } + + // + // bail out on other errors + // + if (!NT_SUCCESS(Status)) + { + DPRINT1("CreateFDO: Failed to create %wZ, Status %x\n", &DeviceName, Status); + return Status; + } + } + + // + // store FDO number + // + m_FDODeviceNumber = UsbDeviceNumber; + + DPRINT("CreateFDO: DeviceName %wZ\n", &DeviceName); + + /* done */ + return Status; +} + +NTSTATUS +CHCDController::SetSymbolicLink( + BOOLEAN Enable) +{ + NTSTATUS Status; + WCHAR LinkName[32]; + WCHAR FDOName[32]; + UNICODE_STRING Link, FDO; + + if (Enable) + { + // + // create legacy link + // + swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber); + swprintf(FDOName, L"\\Device\\USBFDO-%d", m_FDODeviceNumber); + RtlInitUnicodeString(&Link, LinkName); + RtlInitUnicodeString(&FDO, FDOName); + + // + // create symbolic link + // + Status = IoCreateSymbolicLink(&Link, &FDO); + + if (!NT_SUCCESS(Status)) + { + // + // FIXME: handle me + // + ASSERT(0); + } + } + else + { + // + // create legacy link + // + swprintf(LinkName, L"\\DosDevices\\HCD%d", m_FDODeviceNumber); + RtlInitUnicodeString(&Link, LinkName); + + // + // now delete the symbolic link + // + Status = IoDeleteSymbolicLink(&Link); + + if (!NT_SUCCESS(Status)) + { + // + // FIXME: handle me + // + ASSERT(0); + } + } + + // + // done + // + return Status; +} + +NTSTATUS +CreateHCDController( + PHCDCONTROLLER *OutHcdController) +{ + PHCDCONTROLLER This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBOHCI) CHCDController(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutHcdController = (PHCDCONTROLLER)This; + + // + // done + // + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/usb/usbohci/hub_controller.cpp b/reactos/drivers/usb/usbohci/hub_controller.cpp new file mode 100644 index 00000000000..1888564a7cd --- /dev/null +++ b/reactos/drivers/usb/usbohci/hub_controller.cpp @@ -0,0 +1,3731 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbohci/hub_controller.cpp + * PURPOSE: USB OHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#define INITGUID +#include "usbohci.h" + +VOID StatusChangeEndpointCallBack( + PVOID Context); + +class CHubController : public IHubController, + public IDispatchIrp +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + // IHubController interface functions + virtual NTSTATUS Initialize(IN PDRIVER_OBJECT DriverObject, IN PHCDCONTROLLER Controller, IN PUSBHARDWAREDEVICE Device, IN BOOLEAN IsRootHubDevice, IN ULONG DeviceAddress); + virtual NTSTATUS GetHubControllerDeviceObject(PDEVICE_OBJECT * HubDeviceObject); + virtual NTSTATUS GetHubControllerSymbolicLink(ULONG BufferLength, PVOID Buffer, PULONG RequiredLength); + + // IDispatchIrp interface functions + virtual NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + virtual NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + virtual NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp); + + // local functions + NTSTATUS HandleQueryInterface(PIO_STACK_LOCATION IoStack); + NTSTATUS SetDeviceInterface(BOOLEAN bEnable); + NTSTATUS CreatePDO(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * OutDeviceObject); + PUSBHARDWAREDEVICE GetUsbHardware(); + ULONG AcquireDeviceAddress(); + VOID ReleaseDeviceAddress(ULONG DeviceAddress); + BOOLEAN ValidateUsbDevice(PUSBDEVICE UsbDevice); + NTSTATUS AddUsbDevice(PUSBDEVICE UsbDevice); + NTSTATUS RemoveUsbDevice(PUSBDEVICE UsbDevice); + VOID SetNotification(PVOID CallbackContext, PRH_INIT_CALLBACK CallbackRoutine); + // internal ioctl routines + NTSTATUS HandleGetDescriptor(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleGetDescriptorFromInterface(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassDevice(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleGetStatusFromDevice(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleSelectConfiguration(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleSelectInterface(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassOther(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassInterface(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClassEndpoint(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleBulkOrInterruptTransfer(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleIsochronousTransfer(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleClearStall(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleSyncResetAndClearStall(IN OUT PIRP Irp, PURB Urb); + NTSTATUS HandleAbortPipe(IN OUT PIRP Irp, PURB Urb); + + friend VOID StatusChangeEndpointCallBack(PVOID Context); + + // constructor / destructor + CHubController(IUnknown *OuterUnknown){} + virtual ~CHubController(){} + +protected: + LONG m_Ref; + PHCDCONTROLLER m_Controller; + PUSBHARDWAREDEVICE m_Hardware; + BOOLEAN m_IsRootHubDevice; + ULONG m_DeviceAddress; + + BOOLEAN m_InterfaceEnabled; + UNICODE_STRING m_HubDeviceInterfaceString; + + PDEVICE_OBJECT m_HubControllerDeviceObject; + PDRIVER_OBJECT m_DriverObject; + + PVOID m_HubCallbackContext; + PRH_INIT_CALLBACK m_HubCallbackRoutine; + + USB_DEVICE_DESCRIPTOR m_DeviceDescriptor; + + KSPIN_LOCK m_Lock; + RTL_BITMAP m_DeviceAddressBitmap; + PULONG m_DeviceAddressBitmapBuffer; + LIST_ENTRY m_UsbDeviceList; + PIRP m_PendingSCEIrp; + + //Internal Functions + BOOLEAN QueryStatusChageEndpoint(PIRP Irp); +}; + +typedef struct +{ + LIST_ENTRY Entry; + PUSBDEVICE Device; +}USBDEVICE_ENTRY, *PUSBDEVICE_ENTRY; + +/* Lifted from Linux with slight changes */ +const UCHAR ROOTHUB2_DEVICE_DESCRIPTOR [] = +{ + 0x12, /* bLength; */ + USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType; Device */ + 0x00, 0x20, /* bcdUSB; v1.1 */ + USB_DEVICE_CLASS_HUB, /* bDeviceClass; HUB_CLASSCODE */ + 0x01, /* bDeviceSubClass; */ + 0x00, /* bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* bMaxPacketSize0; 8 Bytes */ + /* Fill Vendor and Product in when init root hub */ + 0x00, 0x00, /* idVendor; */ + 0x00, 0x00, /* idProduct; */ + 0x00, 0x00, /* bcdDevice */ + 0x00, /* iManufacturer; */ + 0x00, /* iProduct; */ + 0x00, /* iSerialNumber; */ + 0x01 /* bNumConfigurations; */ + +}; + +const USB_CONFIGURATION_DESCRIPTOR ROOTHUB2_CONFIGURATION_DESCRIPTOR = +{ + sizeof(USB_CONFIGURATION_DESCRIPTOR), + USB_CONFIGURATION_DESCRIPTOR_TYPE, + sizeof(USB_CONFIGURATION_DESCRIPTOR) + sizeof(USB_INTERFACE_DESCRIPTOR) + sizeof(USB_ENDPOINT_DESCRIPTOR), + 1, + 1, + 0, + 0x40, /* self powered */ + 0x0 +}; + +const USB_INTERFACE_DESCRIPTOR ROOTHUB2_INTERFACE_DESCRIPTOR = +{ + sizeof(USB_INTERFACE_DESCRIPTOR), /* bLength */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType; Interface */ + 0, /* bInterfaceNumber; */ + 0, /* bAlternateSetting; */ + 0x1, /* bNumEndpoints; */ + 0x09, /* bInterfaceClass; HUB_CLASSCODE */ + 0x01, /* bInterfaceSubClass; */ + 0x00, /* bInterfaceProtocol: */ + 0x00, /* iInterface; */ +}; + +const USB_ENDPOINT_DESCRIPTOR ROOTHUB2_ENDPOINT_DESCRIPTOR = +{ + sizeof(USB_ENDPOINT_DESCRIPTOR), /* bLength */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ + 0x81, /* bEndPointAddress */ + USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ + 0x01, /* wMaxPacketSize */ + 0xC /* bInterval */ +}; + +//---------------------------------------------------------------------------------------- +NTSTATUS +STDMETHODCALLTYPE +CHubController::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} +//---------------------------------------------------------------------------------------- +NTSTATUS +CHubController::Initialize( + IN PDRIVER_OBJECT DriverObject, + IN PHCDCONTROLLER Controller, + IN PUSBHARDWAREDEVICE Device, + IN BOOLEAN IsRootHubDevice, + IN ULONG DeviceAddress) +{ + NTSTATUS Status; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + USHORT VendorID, DeviceID; + ULONG Dummy1; + + DPRINT("CHubController::Initialize\n"); + + // + // initialize members + // + m_Controller = Controller; + m_Hardware = Device; + m_IsRootHubDevice = IsRootHubDevice; + m_DeviceAddress = DeviceAddress; + m_DriverObject = DriverObject; + KeInitializeSpinLock(&m_Lock); + InitializeListHead(&m_UsbDeviceList); + + // + // allocate device address bitmap buffer + // + m_DeviceAddressBitmapBuffer = (PULONG)ExAllocatePoolWithTag(NonPagedPool, 16, TAG_USBOHCI); + if (!m_DeviceAddressBitmapBuffer) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize device address bitmap + // + RtlInitializeBitMap(&m_DeviceAddressBitmap, m_DeviceAddressBitmapBuffer, 128); + RtlClearAllBits(&m_DeviceAddressBitmap); + + + // + // create PDO + // + Status = CreatePDO(m_DriverObject, &m_HubControllerDeviceObject); + if (!NT_SUCCESS(Status)) + { + // + // failed to create hub device object + // + return Status; + } + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)m_HubControllerDeviceObject->DeviceExtension; + + // + // initialize device extension + // + DeviceExtension->IsFDO = FALSE; + DeviceExtension->IsHub = TRUE; //FIXME + DeviceExtension->Dispatcher = PDISPATCHIRP(this); + + // + // intialize device descriptor + // + C_ASSERT(sizeof(USB_DEVICE_DESCRIPTOR) == sizeof(ROOTHUB2_DEVICE_DESCRIPTOR)); + RtlMoveMemory(&m_DeviceDescriptor, ROOTHUB2_DEVICE_DESCRIPTOR, sizeof(USB_DEVICE_DESCRIPTOR)); + + if (NT_SUCCESS(m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &Dummy1, &Dummy1))) + { + // + // update device descriptor + // + m_DeviceDescriptor.idVendor = VendorID; + m_DeviceDescriptor.idProduct = DeviceID; + m_DeviceDescriptor.bcdUSB = 0x110; //FIXME + } + + // + // Set the SCE Callback that the Hardware Device will call on port status change + // + Device->SetStatusChangeEndpointCallBack((PVOID)StatusChangeEndpointCallBack, this); + + // + // clear init flag + // + m_HubControllerDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + return STATUS_SUCCESS; +} + +// +// Queries the ports to see if there has been a device connected or removed. +// +BOOLEAN +CHubController::QueryStatusChageEndpoint( + PIRP Irp) +{ + ULONG PortCount, PortId; + PIO_STACK_LOCATION IoStack; + USHORT PortStatus, PortChange; + PURB Urb; + PUCHAR TransferBuffer; + UCHAR Changed = FALSE; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + ASSERT(IoStack); + + // + // Get the Urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + ASSERT(Urb); + + // + // Get the number of ports and check each one for device connected + // + m_Hardware->GetDeviceDetails(NULL, NULL, &PortCount, NULL); + DPRINT("SCE Request %p TransferBufferLength %lu Flags %x MDL %p\n", Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength, Urb->UrbBulkOrInterruptTransfer.TransferFlags, Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL); + + TransferBuffer = (PUCHAR)Urb->UrbBulkOrInterruptTransfer.TransferBuffer; + + // + // Loop the ports + // + for (PortId = 0; PortId < PortCount; PortId++) + { + m_Hardware->GetPortStatus(PortId, &PortStatus, &PortChange); + + DPRINT("Port %d: Status %x, Change %x\n", PortId, PortStatus, PortChange); + + + // + // If theres a flag in PortChange return TRUE so the SCE Irp will be completed + // + if (PortChange != 0) + { + DPRINT1("Change state on port %d\n", PortId); + // Set the value for the port number + *TransferBuffer = 1 << ((PortId + 1) & 7); + Changed = TRUE; + } + } + + return Changed; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::GetHubControllerDeviceObject(PDEVICE_OBJECT * HubDeviceObject) +{ + // + // store controller object + // + *HubDeviceObject = m_HubControllerDeviceObject; + + return STATUS_SUCCESS; +} +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::GetHubControllerSymbolicLink( + ULONG BufferLength, + PVOID Buffer, + PULONG RequiredLength) +{ + if (!m_InterfaceEnabled) + { + // + // device interface not yet enabled + // + return STATUS_UNSUCCESSFUL; + } + + if (BufferLength < (ULONG)m_HubDeviceInterfaceString.Length - 8) + { + // + // buffer too small + // length is without '\??\' + // + *RequiredLength = m_HubDeviceInterfaceString.Length- 8; + + // + // done + // + return STATUS_BUFFER_OVERFLOW; + } + + // + // copy symbolic link + // + RtlCopyMemory(Buffer, &m_HubDeviceInterfaceString.Buffer[4], m_HubDeviceInterfaceString.Length - 8); + + // + // store length, length is without '\??\' + // + *RequiredLength = m_HubDeviceInterfaceString.Length - 8; + + // + // done + // + return STATUS_SUCCESS; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandlePnp( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PDEVICE_CAPABILITIES DeviceCapabilities; + PPNP_BUS_INFORMATION BusInformation; + PDEVICE_RELATIONS DeviceRelations; + NTSTATUS Status; + ULONG Index = 0, Length; + USHORT VendorID, DeviceID; + ULONG HiSpeed, NumPorts; + WCHAR Buffer[300]; + LPWSTR DeviceName; + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(DeviceExtension->IsFDO == FALSE); + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + switch(IoStack->MinorFunction) + { + case IRP_MN_START_DEVICE: + { + DPRINT("CHubController::HandlePnp IRP_MN_START_DEVICE\n"); + // + // register device interface + // + Status = SetDeviceInterface(TRUE); + break; + } + case IRP_MN_QUERY_STOP_DEVICE: + case IRP_MN_QUERY_REMOVE_DEVICE: + { + // + // sure + // + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_ID: + { + DPRINT("CHubController::HandlePnp IRP_MN_QUERY_ID Type %x\n", IoStack->Parameters.QueryId.IdType); + + if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID) + { + if (m_Hardware) + { + // + // query device id + // + Status = m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &NumPorts, &HiSpeed); + + if (HiSpeed == 0x200) + { + // + // USB 2.0 hub + // + swprintf(Buffer, L"USB\\ROOT_HUB20"); + } + else + { + // + // USB 1.1 hub + // + swprintf(Buffer, L"USB\\ROOT_HUB"); + } + + DPRINT("Name %S\n", Buffer); + + // + // calculate length + // + Length = (wcslen(Buffer) + 1); + + // + // allocate buffer + // + DeviceName = (LPWSTR)ExAllocatePoolWithTag(PagedPool, Length * sizeof(WCHAR), TAG_USBOHCI); + + if (!DeviceName) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // copy device name + // + wcscpy(DeviceName, Buffer); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceName; + Status = STATUS_SUCCESS; + break; + } + Status = STATUS_UNSUCCESSFUL; + PC_ASSERT(0); + break; + } + + if (IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs) + { + if (m_Hardware) + { + // + // query device id + // + Status = m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &NumPorts, &HiSpeed); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("CHubController::HandlePnp> failed to get hardware id %x\n", Status); + VendorID = 0x8086; + DeviceID = 0x3A37; + } + + if (HiSpeed == 0x200) + { + // + // USB 2.0 hub + // + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20&VID%04x&PID%04x&REV0000", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20&VID%04x&PID%04x", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20") + 1; + } + else + { + // + // USB 1.1 hub + // + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB&VID%04x&PID%04x&REV0000", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB&VID%04x&PID%04x", VendorID, DeviceID) + 1; + Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB") + 1; + } + + Buffer[Index] = UNICODE_NULL; + Index++; + + + DPRINT("Name %S\n", Buffer); + + // + // allocate buffer + // + DeviceName = (LPWSTR)ExAllocatePoolWithTag(PagedPool, Index * sizeof(WCHAR), TAG_USBOHCI); + + if (!DeviceName) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // copy device name + // + RtlMoveMemory(DeviceName, Buffer, Index * sizeof(WCHAR)); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceName; + Status = STATUS_SUCCESS; + break; + } + } + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_CAPABILITIES: + { + DPRINT("CHubController::HandlePnp IRP_MN_QUERY_CAPABILITIES\n"); + + DeviceCapabilities = (PDEVICE_CAPABILITIES)IoStack->Parameters.DeviceCapabilities.Capabilities; + + DeviceCapabilities->LockSupported = FALSE; + DeviceCapabilities->EjectSupported = FALSE; + DeviceCapabilities->Removable = FALSE; + DeviceCapabilities->DockDevice = FALSE; + DeviceCapabilities->UniqueID = FALSE; + DeviceCapabilities->SilentInstall = FALSE; + DeviceCapabilities->RawDeviceOK = FALSE; + DeviceCapabilities->SurpriseRemovalOK = FALSE; + DeviceCapabilities->Address = 0; + DeviceCapabilities->UINumber = 0; + DeviceCapabilities->DeviceD2 = 1; + + /* FIXME */ + DeviceCapabilities->HardwareDisabled = FALSE; + DeviceCapabilities->NoDisplayInUI = FALSE; + DeviceCapabilities->DeviceState[0] = PowerDeviceD0; + for (Index = 1; Index < PowerSystemMaximum; Index++) + DeviceCapabilities->DeviceState[Index] = PowerDeviceD3; + DeviceCapabilities->DeviceWake = PowerDeviceUnspecified; + DeviceCapabilities->D1Latency = 0; + DeviceCapabilities->D2Latency = 0; + DeviceCapabilities->D3Latency = 0; + + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_INTERFACE: + { + DPRINT("CHubController::HandlePnp IRP_MN_QUERY_INTERFACE\n"); + + // + // handle device interface requests + // + Status = HandleQueryInterface(IoStack); + break; + } + case IRP_MN_REMOVE_DEVICE: + { + DPRINT("CHubController::HandlePnp IRP_MN_REMOVE_DEVICE\n"); + + // + // deactivate device interface for BUS PDO + // + SetDeviceInterface(FALSE); + + // + // complete the request first + // + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // now delete device + // + IoDeleteDevice(m_HubControllerDeviceObject); + + // + // nullify pointer + // + m_HubControllerDeviceObject = 0; + + // + // done + // + return STATUS_SUCCESS; + } + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + DPRINT("CHubController::HandlePnp IRP_MN_QUERY_DEVICE_RELATIONS Type %x\n", IoStack->Parameters.QueryDeviceRelations.Type); + + if (IoStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation) + { + // + // allocate device relations + // + DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), TAG_USBOHCI); + if (!DeviceRelations) + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // initialize device relations + // + DeviceRelations->Count = 1; + DeviceRelations->Objects[0] = DeviceObject; + ObReferenceObject(DeviceObject); + + // + // done + // + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + } + else + { + // + // not handled + // + Status = Irp->IoStatus.Status; + } + break; + } + case IRP_MN_QUERY_BUS_INFORMATION: + { + DPRINT("CHubController::HandlePnp IRP_MN_QUERY_BUS_INFORMATION\n"); + + // + // allocate buffer for bus information + // + BusInformation = (PPNP_BUS_INFORMATION)ExAllocatePool(PagedPool, sizeof(PNP_BUS_INFORMATION)); + if (BusInformation) + { + // + // copy BUS guid + // + RtlMoveMemory(&BusInformation->BusTypeGuid, &GUID_BUS_TYPE_USB, sizeof(GUID)); + + // + // set bus type + // + BusInformation->LegacyBusType = PNPBus; + BusInformation->BusNumber = 0; + + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = (ULONG_PTR)BusInformation; + } + else + { + // + // no memory + // + Status = STATUS_INSUFFICIENT_RESOURCES; + } + break; + } + case IRP_MN_STOP_DEVICE: + { + DPRINT("CHubController::HandlePnp IRP_MN_STOP_DEVICE\n"); + // + // stop device + // + Status = STATUS_SUCCESS; + break; + } + default: + { + // + // ignore request with default status + // + Status = Irp->IoStatus.Status; + break; + } + } + + // + // complete request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandlePower( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + UNIMPLEMENTED + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_NOT_IMPLEMENTED; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleIsochronousTransfer( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT_DESCRIPTOR EndPointDesc = NULL; + + // + // Check PipeHandle to determine if this is a Bulk or Interrupt Transfer Request + // + EndPointDesc = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbIsochronousTransfer.PipeHandle; + + if (!EndPointDesc) + { + DPRINT1("No EndpointDesc\n"); + Urb->UrbIsochronousTransfer.Hdr.Status = USBD_STATUS_INVALID_PIPE_HANDLE; + return STATUS_INVALID_PARAMETER; + } + + // + // sanity checks + // + ASSERT(EndPointDesc); + ASSERT((EndPointDesc->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_ISOCHRONOUS); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleIsochronousTransfer invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + return UsbDevice->SubmitIrp(Irp); +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleBulkOrInterruptTransfer( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT_DESCRIPTOR EndPointDesc = NULL; + // + // First check if the request is for the Status Change Endpoint + // + + // + // Is the Request for the root hub + // + if (Urb->UrbHeader.UsbdDeviceHandle == 0) + { + ASSERT(m_PendingSCEIrp == NULL); + if (QueryStatusChageEndpoint(Irp)) + { + StatusChangeEndpointCallBack(this); + return STATUS_SUCCESS; + } + + // + // Else pend the IRP, to be completed when a device connects or disconnects. + // + DPRINT("Pending SCE Irp\n");; + m_PendingSCEIrp = Irp; + IoMarkIrpPending(Irp); + return STATUS_PENDING; + } + + // + // Check PipeHandle to determine if this is a Bulk or Interrupt Transfer Request + // + EndPointDesc = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbBulkOrInterruptTransfer.PipeHandle; + + // + // sanity checks + // + ASSERT(EndPointDesc); + ASSERT((EndPointDesc->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK || (EndPointDesc->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleBuldOrInterruptTransfer invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + return UsbDevice->SubmitIrp(Irp); +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassOther( + IN OUT PIRP Irp, + PURB Urb) +{ + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + USHORT PortStatus = 0, PortChange = 0; + PUSHORT Buffer; + ULONG NumPort; + ULONG PortId; + + DPRINT("CHubController::HandleClassOther> Request %x Value %x\n", Urb->UrbControlVendorClassRequest.Request, Urb->UrbControlVendorClassRequest.Value); + + // + // get number of ports available + // + Status = m_Hardware->GetDeviceDetails(NULL, NULL, &NumPort, NULL); + PC_ASSERT(Status == STATUS_SUCCESS); + + // + // sanity check + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.Index - 1 < (USHORT)NumPort); + + // + // port range reported start from 1 -n + // convert back port id so it matches the hardware + // + PortId = Urb->UrbControlVendorClassRequest.Index - 1; + + // + // check request code + // + switch(Urb->UrbControlVendorClassRequest.Request) + { + case USB_REQUEST_GET_STATUS: + { + // + // sanity check + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength == sizeof(USHORT) * 2); + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer); + + // + // get port status + // + Status = m_Hardware->GetPortStatus(PortId, &PortStatus, &PortChange); + + if (NT_SUCCESS(Status)) + { + // + // request contains buffer of 2 ushort which are used from submitting port status and port change status + // + DPRINT("PortId %x PortStatus %x PortChange %x\n", PortId, PortStatus, PortChange); + Buffer = (PUSHORT)Urb->UrbControlVendorClassRequest.TransferBuffer; + + // + // store status, then port change + // + *Buffer = PortStatus; + Buffer++; + *Buffer = PortChange; + } + + // + // done + // + break; + } + case USB_REQUEST_CLEAR_FEATURE: + { + switch (Urb->UrbControlVendorClassRequest.Value) + { + case C_PORT_CONNECTION: + Status = m_Hardware->ClearPortStatus(PortId, C_PORT_CONNECTION); + break; + case C_PORT_RESET: + Status= m_Hardware->ClearPortStatus(PortId, C_PORT_RESET); + break; + default: + DPRINT("Unknown Value for Clear Feature %x \n", Urb->UrbControlVendorClassRequest.Value); + break; + } + + Status = STATUS_SUCCESS; + break; + } + case USB_REQUEST_SET_FEATURE: + { + // + // request set feature + // + switch(Urb->UrbControlVendorClassRequest.Value) + { + case PORT_ENABLE: + { + // + // port enable + // + Status = m_Hardware->SetPortFeature(PortId, PORT_ENABLE); + break; + } + + case PORT_SUSPEND: + { + // + // set suspend port feature + // + Status = m_Hardware->SetPortFeature(PortId, PORT_SUSPEND); + break; + } + case PORT_POWER: + { + // + // set power feature on port + // + Status = m_Hardware->SetPortFeature(PortId, PORT_POWER); + break; + } + + case PORT_RESET: + { + // + // reset port feature + // + Status = m_Hardware->SetPortFeature(PortId, PORT_RESET); + PC_ASSERT(Status == STATUS_SUCCESS); + break; + } + default: + DPRINT1("Unsupported request id %x\n", Urb->UrbControlVendorClassRequest.Value); + PC_ASSERT(FALSE); + } + break; + } + default: + DPRINT1("CHubController::HandleClassOther Unknown request code %x\n", Urb->UrbControlVendorClassRequest.Request); + PC_ASSERT(0); + Status = STATUS_INVALID_DEVICE_REQUEST; + } + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleSelectConfiguration( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + PUSBD_INTERFACE_INFORMATION InterfaceInfo; + + // + // is the request for the Root Hub + // + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // FIXME: support setting device to unconfigured state + // + PC_ASSERT(Urb->UrbSelectConfiguration.ConfigurationDescriptor); + + // + // set device handle + // + Urb->UrbSelectConfiguration.ConfigurationHandle = (PVOID)&ROOTHUB2_CONFIGURATION_DESCRIPTOR; + + // + // copy interface info + // + InterfaceInfo = &Urb->UrbSelectConfiguration.Interface; + + InterfaceInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)&ROOTHUB2_INTERFACE_DESCRIPTOR; + InterfaceInfo->Class = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceClass; + InterfaceInfo->SubClass = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceSubClass; + InterfaceInfo->Protocol = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceProtocol; + InterfaceInfo->Reserved = 0; + + // + // sanity check + // + PC_ASSERT(InterfaceInfo->NumberOfPipes == 1); + + // + // copy pipe info + // + InterfaceInfo->Pipes[0].MaximumPacketSize = ROOTHUB2_ENDPOINT_DESCRIPTOR.wMaxPacketSize; + InterfaceInfo->Pipes[0].EndpointAddress = ROOTHUB2_ENDPOINT_DESCRIPTOR.bEndpointAddress; + InterfaceInfo->Pipes[0].Interval = ROOTHUB2_ENDPOINT_DESCRIPTOR.bInterval; + InterfaceInfo->Pipes[0].PipeType = (USBD_PIPE_TYPE)(ROOTHUB2_ENDPOINT_DESCRIPTOR.bmAttributes & USB_ENDPOINT_TYPE_MASK); + InterfaceInfo->Pipes[0].PipeHandle = (PVOID)&ROOTHUB2_ENDPOINT_DESCRIPTOR; + + return STATUS_SUCCESS; + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleSelectConfiguration invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // select configuration + // + return UsbDevice->SelectConfiguration(Urb->UrbSelectConfiguration.ConfigurationDescriptor, &Urb->UrbSelectConfiguration.Interface, &Urb->UrbSelectConfiguration.ConfigurationHandle); + } +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleSelectInterface( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSBDEVICE UsbDevice; + + // + // sanity check + // + PC_ASSERT(Urb->UrbSelectInterface.ConfigurationHandle); + + // + // is the request for the Root Hub + // + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // no op for root hub + // + return STATUS_SUCCESS; + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleSelectInterface invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // select interface + // + return UsbDevice->SelectInterface(Urb->UrbSelectInterface.ConfigurationHandle, &Urb->UrbSelectInterface.Interface); + } +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleGetStatusFromDevice( + IN OUT PIRP Irp, + PURB Urb) +{ + PUSHORT DeviceStatus; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + + // + // sanity checks + // + PC_ASSERT(Urb->UrbControlGetStatusRequest.Index == 0); + PC_ASSERT(Urb->UrbControlGetStatusRequest.TransferBufferLength >= sizeof(USHORT)); + PC_ASSERT(Urb->UrbControlGetStatusRequest.TransferBuffer); + + // + // get status buffer + // + DeviceStatus = (PUSHORT)Urb->UrbControlGetStatusRequest.TransferBuffer; + + + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // FIXME need more flags ? + // + *DeviceStatus = USB_PORT_STATUS_CONNECT; + return STATUS_SUCCESS; + } + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleGetStatusFromDevice invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_STATUS; + CtrlSetup.wValue.LowByte = 0; + CtrlSetup.wValue.HiByte = 0; + CtrlSetup.wIndex.W = Urb->UrbControlGetStatusRequest.Index; + CtrlSetup.wLength = (USHORT)Urb->UrbControlGetStatusRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0x80; + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + ASSERT(Status == STATUS_SUCCESS); + DPRINT("CHubController::HandleGetStatusFromDevice Status %x Length %lu DeviceStatus %x\n", Status, Urb->UrbControlDescriptorRequest.TransferBufferLength, *DeviceStatus); + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassDevice( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + PUSB_HUB_DESCRIPTOR UsbHubDescriptor; + ULONG PortCount, Dummy2; + USHORT Dummy1; + PUSBDEVICE UsbDevice; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + + DPRINT("CHubController::HandleClassDevice Request %x Class %x\n", Urb->UrbControlVendorClassRequest.Request, Urb->UrbControlVendorClassRequest.Value >> 8); + + // + // check class request type + // + switch(Urb->UrbControlVendorClassRequest.Request) + { + case USB_REQUEST_GET_STATUS: + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("USB_REQUEST_GET_STATUS invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_STATUS; + CtrlSetup.wValue.LowByte = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value; + CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wLength = (USHORT)Urb->UrbControlGetStatusRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0xA0; + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + ASSERT(Status == STATUS_SUCCESS); + break; + } + case USB_REQUEST_GET_DESCRIPTOR: + { + switch (Urb->UrbControlVendorClassRequest.Value >> 8) + { + case USB_DEVICE_CLASS_RESERVED: // FALL THROUGH + case USB_DEVICE_CLASS_HUB: + { + // + // sanity checks + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength >= sizeof(USB_HUB_DESCRIPTOR)); + + // + // get hub descriptor + // + UsbHubDescriptor = (PUSB_HUB_DESCRIPTOR)Urb->UrbControlVendorClassRequest.TransferBuffer; + + // + // one hub is handled + // + UsbHubDescriptor->bDescriptorLength = sizeof(USB_HUB_DESCRIPTOR); + Urb->UrbControlVendorClassRequest.TransferBufferLength = sizeof(USB_HUB_DESCRIPTOR); + + // + // type should 0x29 according to msdn + // + UsbHubDescriptor->bDescriptorType = 0x29; + + // + // get port count + // + Status = m_Hardware->GetDeviceDetails(&Dummy1, &Dummy1, &PortCount, &Dummy2); + PC_ASSERT(Status == STATUS_SUCCESS); + + // + // FIXME: retrieve values + // + UsbHubDescriptor->bNumberOfPorts = (UCHAR)PortCount; + UsbHubDescriptor->wHubCharacteristics = 0x00; + UsbHubDescriptor->bPowerOnToPowerGood = 0x01; + UsbHubDescriptor->bHubControlCurrent = 0x00; + + // + // done + // + Status = STATUS_SUCCESS; + break; + } + default: + DPRINT1("CHubController::HandleClassDevice Class %x not implemented\n", Urb->UrbControlVendorClassRequest.Value >> 8); + break; + } + break; + } + default: + DPRINT1("CHubController::HandleClassDevice Type %x not implemented\n", Urb->UrbControlVendorClassRequest.Request); + } + + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleGetDescriptorFromInterface( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + PUSBDEVICE UsbDevice; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + + // + // sanity check + // + ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength); + ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleGetDescriptorFromInterface invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; + CtrlSetup.wValue.LowByte = Urb->UrbControlDescriptorRequest.Index; + CtrlSetup.wValue.HiByte = Urb->UrbControlDescriptorRequest.DescriptorType; + CtrlSetup.wIndex.W = Urb->UrbControlDescriptorRequest.LanguageId; + CtrlSetup.wLength = (USHORT)Urb->UrbControlDescriptorRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0x81; + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + ASSERT(Status == STATUS_SUCCESS); + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleGetDescriptor( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + PUCHAR Buffer; + PUSBDEVICE UsbDevice; + ULONG Length; + + DPRINT("CHubController::HandleGetDescriptor\n"); + + // + // check descriptor type + // + switch(Urb->UrbControlDescriptorRequest.DescriptorType) + { + case USB_DEVICE_DESCRIPTOR_TYPE: + { + // + // sanity check + // + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength >= sizeof(USB_DEVICE_DESCRIPTOR)); + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // copy root hub device descriptor + // + RtlCopyMemory((PUCHAR)Urb->UrbControlDescriptorRequest.TransferBuffer, &m_DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); + Status = STATUS_SUCCESS; + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleGetDescriptor invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // retrieve device descriptor from device + // + UsbDevice->GetDeviceDescriptor((PUSB_DEVICE_DESCRIPTOR)Urb->UrbControlDescriptorRequest.TransferBuffer); + Status = STATUS_SUCCESS; + } + break; + } + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + { + // + // sanity checks + // + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength >= sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + if (Urb->UrbHeader.UsbdDeviceHandle == NULL) + { + // + // request is for the root bus controller + // + RtlCopyMemory(Urb->UrbControlDescriptorRequest.TransferBuffer, &ROOTHUB2_CONFIGURATION_DESCRIPTOR, sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + // + // get configuration descriptor, very retarded! + // + ConfigurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)Urb->UrbControlDescriptorRequest.TransferBuffer; + + // + // check if buffer can hold interface and endpoint descriptor + // + if (ConfigurationDescriptor->wTotalLength > Urb->UrbControlDescriptorRequest.TransferBufferLength) + { + // + // buffer too small + // + Status = STATUS_SUCCESS; + ASSERT(FALSE); + break; + } + + // + // copy interface descriptor template + // + Buffer = (PUCHAR)(ConfigurationDescriptor + 1); + RtlCopyMemory(Buffer, &ROOTHUB2_INTERFACE_DESCRIPTOR, sizeof(USB_INTERFACE_DESCRIPTOR)); + + // + // copy end point descriptor template + // + Buffer += sizeof(USB_INTERFACE_DESCRIPTOR); + RtlCopyMemory(Buffer, &ROOTHUB2_ENDPOINT_DESCRIPTOR, sizeof(USB_ENDPOINT_DESCRIPTOR)); + + // + // done + // + Status = STATUS_SUCCESS; + + } + else + { + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("USB_CONFIGURATION_DESCRIPTOR_TYPE invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + if (sizeof(USB_CONFIGURATION_DESCRIPTOR) > Urb->UrbControlDescriptorRequest.TransferBufferLength) + { + // + // buffer too small + // + Urb->UrbControlDescriptorRequest.TransferBufferLength = UsbDevice->GetConfigurationDescriptorsLength(); + + // + // bail out + // + Status = STATUS_SUCCESS; + break; + } + + // + // perform work in IUSBDevice + // + UsbDevice->GetConfigurationDescriptors((PUSB_CONFIGURATION_DESCRIPTOR)Urb->UrbControlDescriptorRequest.TransferBuffer, Urb->UrbControlDescriptorRequest.TransferBufferLength, &Length); + + // + // store result size + // + Urb->UrbControlDescriptorRequest.TransferBufferLength = Length; + Status = STATUS_SUCCESS; + } + break; + } + case USB_STRING_DESCRIPTOR_TYPE: + { + // + // sanity check + // + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("USB_STRING_DESCRIPTOR_TYPE invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + // + // generate setup packet + // + CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; + CtrlSetup.wValue.LowByte = Urb->UrbControlDescriptorRequest.Index; + CtrlSetup.wValue.HiByte = Urb->UrbControlDescriptorRequest.DescriptorType; + CtrlSetup.wIndex.W = Urb->UrbControlDescriptorRequest.LanguageId; + CtrlSetup.wLength = (USHORT)Urb->UrbControlDescriptorRequest.TransferBufferLength; + CtrlSetup.bmRequestType.B = 0x80; + + // + // submit setup packet + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer); + break; + } + default: + DPRINT1("CHubController::HandleGetDescriptor DescriptorType %x unimplemented\n", Urb->UrbControlDescriptorRequest.DescriptorType); + break; + } + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassEndpoint( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + + // + // sanity check + // + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer); + PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength); + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleClassEndpoint invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + DPRINT1("URB_FUNCTION_CLASS_ENDPOINT\n"); + DPRINT1("TransferFlags %x\n", Urb->UrbControlVendorClassRequest.TransferFlags); + DPRINT1("TransferBufferLength %x\n", Urb->UrbControlVendorClassRequest.TransferBufferLength); + DPRINT1("TransferBuffer %x\n", Urb->UrbControlVendorClassRequest.TransferBuffer); + DPRINT1("TransferBufferMDL %x\n", Urb->UrbControlVendorClassRequest.TransferBufferMDL); + DPRINT1("RequestTypeReservedBits %x\n", Urb->UrbControlVendorClassRequest.RequestTypeReservedBits); + DPRINT1("Request %x\n", Urb->UrbControlVendorClassRequest.Request); + DPRINT1("Value %x\n", Urb->UrbControlVendorClassRequest.Value); + DPRINT1("Index %x\n", Urb->UrbControlVendorClassRequest.Index); + + // + // initialize setup packet + // + CtrlSetup.bmRequestType.B = 0x22; //FIXME: Const. + CtrlSetup.bRequest = Urb->UrbControlVendorClassRequest.Request; + CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value; + CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wLength = Urb->UrbControlVendorClassRequest.TransferBufferLength; + + if (Urb->UrbControlVendorClassRequest.TransferFlags & USBD_TRANSFER_DIRECTION_IN) + { + // + // data direction is device to host + // + CtrlSetup.bmRequestType.B |= 0x80; + } + + + // + // issue request + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlVendorClassRequest.TransferBufferLength, Urb->UrbControlVendorClassRequest.TransferBuffer); + + // + // assert on failure + // + PC_ASSERT(NT_SUCCESS(Status)); + + + // + // done + // + return Status; +} + +NTSTATUS +CHubController::HandleSyncResetAndClearStall( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status = STATUS_SUCCESS; + PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + ULONG Type; + + // + // sanity check + // + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST)); + PC_ASSERT(Urb->UrbPipeRequest.PipeHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleAbortPipe invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get endpoint descriptor + // + EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbPipeRequest.PipeHandle; + + // + // get type + // + Type = (EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK); + if (Type != USB_ENDPOINT_TYPE_ISOCHRONOUS) + { + // + // clear stall + // + Status = HandleClearStall(Irp, Urb); + } + DPRINT1("URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL Status %x\n", Status); + + // + // FIXME reset data toggle + // + + // + // done + // + return Status; +} + +NTSTATUS +CHubController::HandleAbortPipe( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + NTSTATUS Status; + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + + // + // sanity check + // + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST)); + PC_ASSERT(Urb->UrbPipeRequest.PipeHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleAbortPipe invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get endpoint descriptor + // + EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbPipeRequest.PipeHandle; + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + // + // issue request + // + Status = UsbDevice->AbortPipe(EndpointDescriptor); + DPRINT1("URB_FUNCTION_ABORT_PIPE Status %x\n", Status); + + // + // done + // + return Status; +} + + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClearStall( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + + + // + // sanity check + // + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST)); + PC_ASSERT(Urb->UrbPipeRequest.PipeHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleClearStall invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get endpoint descriptor + // + EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbPipeRequest.PipeHandle; + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + DPRINT1("URB_FUNCTION_SYNC_CLEAR_STALL\n"); + + // + // initialize setup packet + // + CtrlSetup.bmRequestType.B = 0x02; + CtrlSetup.bRequest = USB_REQUEST_CLEAR_FEATURE; + CtrlSetup.wValue.W = USB_FEATURE_ENDPOINT_STALL; + CtrlSetup.wIndex.W = EndpointDescriptor->bEndpointAddress; + CtrlSetup.wLength = 0; + CtrlSetup.wValue.W = 0; + + // + // issue request + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, 0, 0); + + DPRINT1("URB_FUNCTION_CLEAR_STALL Status %x\n", Status); + + // + // done + // + return Status; +} + + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleClassInterface( + IN OUT PIRP Irp, + IN OUT PURB Urb) +{ + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSBDEVICE UsbDevice; + + // + // sanity check + // + //ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer || Urb->UrbControlVendorClassRequest.TransferBufferMDL); + //ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength); + PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle); + + // + // check if this is a valid usb device handle + // + if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle))) + { + DPRINT1("HandleClassInterface invalid device handle %p\n", Urb->UrbHeader.UsbdDeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device + // + UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle); + + + DPRINT1("URB_FUNCTION_CLASS_INTERFACE\n"); + DPRINT1("TransferFlags %x\n", Urb->UrbControlVendorClassRequest.TransferFlags); + DPRINT1("TransferBufferLength %x\n", Urb->UrbControlVendorClassRequest.TransferBufferLength); + DPRINT1("TransferBuffer %x\n", Urb->UrbControlVendorClassRequest.TransferBuffer); + DPRINT1("TransferBufferMDL %x\n", Urb->UrbControlVendorClassRequest.TransferBufferMDL); + DPRINT1("RequestTypeReservedBits %x\n", Urb->UrbControlVendorClassRequest.RequestTypeReservedBits); + DPRINT1("Request %x\n", Urb->UrbControlVendorClassRequest.Request); + DPRINT1("Value %x\n", Urb->UrbControlVendorClassRequest.Value); + DPRINT1("Index %x\n", Urb->UrbControlVendorClassRequest.Index); + + // + // initialize setup packet + // + CtrlSetup.bmRequestType.B = 0x21; + CtrlSetup.bRequest = Urb->UrbControlVendorClassRequest.Request; + CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value; + CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index; + CtrlSetup.wLength = Urb->UrbControlVendorClassRequest.TransferBufferLength; + + if (Urb->UrbControlVendorClassRequest.TransferFlags & USBD_TRANSFER_DIRECTION_IN) + { + // + // data direction is device to host + // + CtrlSetup.bmRequestType.B |= 0x80; + } + + // + // issue request + // + Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlVendorClassRequest.TransferBufferLength, Urb->UrbControlVendorClassRequest.TransferBuffer); + + // + // assert on failure + // + if (!NT_SUCCESS(Status)) + { + // + // display error + // + DPRINT1("URB_FUNCTION_CLASS_INTERFACE failed with Urb Status %x\n", Urb->UrbHeader.Status); + } + + // + // done + // + return Status; +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::HandleDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PURB Urb; + NTSTATUS Status = STATUS_NOT_IMPLEMENTED; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // determine which request should be performed + // + switch(IoStack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_INTERNAL_USB_SUBMIT_URB: + { + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + PC_ASSERT(Urb); + + switch (Urb->UrbHeader.Function) + { + case URB_FUNCTION_SYNC_RESET_PIPE: + case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL: + Status = HandleSyncResetAndClearStall(Irp, Urb); + break; + case URB_FUNCTION_ABORT_PIPE: + Status = HandleAbortPipe(Irp, Urb); + break; + case URB_FUNCTION_SYNC_CLEAR_STALL: + Status = HandleClearStall(Irp, Urb); + break; + case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE: + Status = HandleGetDescriptorFromInterface(Irp, Urb); + break; + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + Status = HandleGetDescriptor(Irp, Urb); + break; + case URB_FUNCTION_CLASS_DEVICE: + Status = HandleClassDevice(Irp, Urb); + break; + case URB_FUNCTION_GET_STATUS_FROM_DEVICE: + Status = HandleGetStatusFromDevice(Irp, Urb); + break; + case URB_FUNCTION_SELECT_CONFIGURATION: + Status = HandleSelectConfiguration(Irp, Urb); + break; + case URB_FUNCTION_SELECT_INTERFACE: + Status = HandleSelectInterface(Irp, Urb); + break; + case URB_FUNCTION_CLASS_OTHER: + Status = HandleClassOther(Irp, Urb); + break; + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + Status = HandleBulkOrInterruptTransfer(Irp, Urb); + break; + case URB_FUNCTION_ISOCH_TRANSFER: + Status = HandleIsochronousTransfer(Irp, Urb); + break; + case URB_FUNCTION_CLASS_INTERFACE: + Status = HandleClassInterface(Irp, Urb); + break; + case URB_FUNCTION_CLASS_ENDPOINT: + Status = HandleClassEndpoint(Irp, Urb); + break; + default: + DPRINT1("IOCTL_INTERNAL_USB_SUBMIT_URB Function %x NOT IMPLEMENTED\n", Urb->UrbHeader.Function); + break; + } + // + // request completed + // + break; + } + case IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE: + { + DPRINT("IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE %p\n", this); + + if (IoStack->Parameters.Others.Argument1) + { + // + // store object as device handle + // + *(PVOID *)IoStack->Parameters.Others.Argument1 = (PVOID)this; + Status = STATUS_SUCCESS; + } + else + { + // + // mis-behaving hub driver + // + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + // + // request completed + // + break; + } + case IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO: + { + DPRINT("IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO\n"); + + // + // this is the first request send, it delivers the PDO to the caller + // + if (IoStack->Parameters.Others.Argument1) + { + // + // store root hub pdo object + // + *(PVOID *)IoStack->Parameters.Others.Argument1 = DeviceObject; + } + + if (IoStack->Parameters.Others.Argument2) + { + // + // documentation claims to deliver the hcd controller object, although it is wrong + // + *(PVOID *)IoStack->Parameters.Others.Argument2 = DeviceObject; + } + + // + // request completed + // + Status = STATUS_SUCCESS; + break; + } + case IOCTL_INTERNAL_USB_GET_HUB_COUNT: + { + DPRINT("IOCTL_INTERNAL_USB_GET_HUB_COUNT\n"); + + // + // after IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO is delivered, the usbhub driver + // requests this ioctl to deliver the number of presents. + + if (IoStack->Parameters.Others.Argument1) + { + // + // FIXME / verify: there is only one hub + // + *(PULONG)IoStack->Parameters.Others.Argument1 = 1; + } + + // + // request completed + // + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(ULONG); + break; + } + case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION: + { + DPRINT1("IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION UNIMPLEMENTED\n"); + Status = STATUS_SUCCESS; + break; + } + default: + { + DPRINT1("HandleDeviceControl>Type: IoCtl %x InputBufferLength %lu OutputBufferLength %lu NOT IMPLEMENTED\n", + IoStack->Parameters.DeviceIoControl.IoControlCode, + IoStack->Parameters.DeviceIoControl.InputBufferLength, + IoStack->Parameters.DeviceIoControl.OutputBufferLength); + break; + } + } + if (Status != STATUS_PENDING) + { + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + return Status; +} + +//----------------------------------------------------------------------------------------- +PUSBHARDWAREDEVICE +CHubController::GetUsbHardware() +{ + return m_Hardware; +} + +//----------------------------------------------------------------------------------------- +ULONG +CHubController::AcquireDeviceAddress() +{ + KIRQL OldLevel; + ULONG DeviceAddress; + + // + // acquire device lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // find address + // + DeviceAddress = RtlFindClearBits(&m_DeviceAddressBitmap, 1, 0); + if (DeviceAddress != MAXULONG) + { + // + // reserve address + // + RtlSetBits(&m_DeviceAddressBitmap, DeviceAddress, 1); + + // + // device addresses start from 0x1 - 0xFF + // + DeviceAddress++; + } + + // + // release spin lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // return device address + // + return DeviceAddress; +} +//----------------------------------------------------------------------------------------- +VOID +CHubController::ReleaseDeviceAddress( + ULONG DeviceAddress) +{ + KIRQL OldLevel; + + // + // acquire device lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // sanity check + // + PC_ASSERT(DeviceAddress != 0); + + // + // convert back to bit number + // + DeviceAddress--; + + // + // clear bit + // + RtlClearBits(&m_DeviceAddressBitmap, DeviceAddress, 1); + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); +} +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::RemoveUsbDevice( + PUSBDEVICE UsbDevice) +{ + PUSBDEVICE_ENTRY DeviceEntry; + PLIST_ENTRY Entry; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + KIRQL OldLevel; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // point to first entry + // + Entry = m_UsbDeviceList.Flink; + + // + // find matching entry + // + while(Entry != &m_UsbDeviceList) + { + // + // get entry + // + DeviceEntry = (PUSBDEVICE_ENTRY)CONTAINING_RECORD(Entry, USBDEVICE_ENTRY, Entry); + + // + // is it current entry + // + if (DeviceEntry->Device == UsbDevice) + { + // + // remove entry + // + RemoveEntryList(Entry); + + // + // free entry + // + ExFreePoolWithTag(DeviceEntry, TAG_USBOHCI); + + // + // done + // + Status = STATUS_SUCCESS; + break; + } + + // + // goto next device + // + Entry = Entry->Flink; + } + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // return result + // + return Status; +} +//----------------------------------------------------------------------------------------- +BOOLEAN +CHubController::ValidateUsbDevice(PUSBDEVICE UsbDevice) +{ + PUSBDEVICE_ENTRY DeviceEntry; + PLIST_ENTRY Entry; + KIRQL OldLevel; + BOOLEAN Result = FALSE; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // point to first entry + // + Entry = m_UsbDeviceList.Flink; + + // + // find matching entry + // + while(Entry != &m_UsbDeviceList) + { + // + // get entry + // + DeviceEntry = (PUSBDEVICE_ENTRY)CONTAINING_RECORD(Entry, USBDEVICE_ENTRY, Entry); + + // + // is it current entry + // + if (DeviceEntry->Device == UsbDevice) + { + // + // device is valid + // + Result = TRUE; + break; + } + + // + // goto next device + // + Entry = Entry->Flink; + } + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // return result + // + return Result; + +} + +//----------------------------------------------------------------------------------------- +NTSTATUS +CHubController::AddUsbDevice( + PUSBDEVICE UsbDevice) +{ + PUSBDEVICE_ENTRY DeviceEntry; + KIRQL OldLevel; + + // + // allocate device entry + // + DeviceEntry = (PUSBDEVICE_ENTRY)ExAllocatePoolWithTag(NonPagedPool, sizeof(USBDEVICE_ENTRY), TAG_USBOHCI); + if (!DeviceEntry) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize entry + // + DeviceEntry->Device = UsbDevice; + + // + // acquire lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // insert entry + // + InsertTailList(&m_UsbDeviceList, &DeviceEntry->Entry); + + // + // release spin lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + // + // done + // + return STATUS_SUCCESS; +} + +//----------------------------------------------------------------------------------------- +VOID +CHubController::SetNotification( + PVOID CallbackContext, + PRH_INIT_CALLBACK CallbackRoutine) +{ + KIRQL OldLevel; + + // + // acquire hub controller lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // now set the callback routine and context of the hub + // + m_HubCallbackContext = CallbackContext; + m_HubCallbackRoutine = CallbackRoutine; + + // + // release hub controller lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); +} + +//================================================================================================= +// +// Generic Interface functions +// +VOID +USB_BUSIFFN +USBI_InterfaceReference( + PVOID BusContext) +{ + CHubController * Controller = (CHubController*)BusContext; + + DPRINT("USBH_InterfaceReference\n"); + + // + // add reference + // + Controller->AddRef(); +} + +VOID +USB_BUSIFFN +USBI_InterfaceDereference( + PVOID BusContext) +{ + CHubController * Controller = (CHubController*)BusContext; + + DPRINT("USBH_InterfaceDereference\n"); + + // + // release + // + Controller->Release(); +} +//================================================================================================= +// +// USB Hub Interface functions +// +NTSTATUS +USB_BUSIFFN +USBHI_CreateUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE *NewDevice, + PUSB_DEVICE_HANDLE HubDeviceHandle, + USHORT PortStatus, + USHORT PortNumber) +{ + PUSBDEVICE NewUsbDevice; + CHubController * Controller; + NTSTATUS Status; + + DPRINT("USBHI_CreateUsbDevice PortStatus %x\n", PortStatus); + + // + // first get hub controller + // + Controller = (CHubController *)BusContext; + + // + // sanity check + // + PC_ASSERT(Controller); + PC_ASSERT(BusContext == HubDeviceHandle); + + // + // now allocate usb device + // + Status = CreateUSBDevice(&NewUsbDevice); + + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // release controller + // + Controller->Release(); + DPRINT1("USBHI_CreateUsbDevice: failed to create usb device %x\n", Status); + return Status; + } + + // + // now initialize device + // + Status = NewUsbDevice->Initialize(PHUBCONTROLLER(Controller), Controller->GetUsbHardware(),PVOID(Controller), PortNumber, PortStatus); + + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // release usb device + // + NewUsbDevice->Release(); + DPRINT1("USBHI_CreateUsbDevice: failed to initialize usb device %x\n", Status); + return Status; + } + + // + // insert into list + // + Status = Controller->AddUsbDevice(NewUsbDevice); + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // release usb device + // + NewUsbDevice->Release(); + + DPRINT1("USBHI_CreateUsbDevice: failed to add usb device %x\n", Status); + return Status; + } + + // + // store the handle + // + *NewDevice = NewUsbDevice; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_InitializeUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + ULONG DeviceAddress; + NTSTATUS Status; + ULONG Index = 0; + + DPRINT("USBHI_InitializeUsbDevice\n"); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_InitializeUsbDevice invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // now reserve an address + // + DeviceAddress = Controller->AcquireDeviceAddress(); + + // + // is the device address valid + // + if (DeviceAddress == MAXULONG) + { + // + // failed to get an device address from the device address pool + // + DPRINT1("USBHI_InitializeUsbDevice failed to get device address\n"); + return STATUS_DEVICE_DATA_ERROR; + } + + do + { + // + // now set the device address + // + Status = UsbDevice->SetDeviceAddress((UCHAR)DeviceAddress); + + if (NT_SUCCESS(Status)) + break; + + }while(Index++ < 3 ); + + // + // check for failure + // + if (!NT_SUCCESS(Status)) + { + // + // failed to set device address + // + DPRINT1("USBHI_InitializeUsbDevice failed to set address with %x\n", Status); + + // + // release address + // + Controller->ReleaseDeviceAddress(DeviceAddress); + + // + // return error + // + return STATUS_DEVICE_DATA_ERROR; + } + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetUsbDescriptors( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle, + PUCHAR DeviceDescriptorBuffer, + PULONG DeviceDescriptorBufferLength, + PUCHAR ConfigDescriptorBuffer, + PULONG ConfigDescriptorBufferLength) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + + DPRINT("USBHI_GetUsbDescriptors\n"); + + // + // sanity check + // + PC_ASSERT(DeviceDescriptorBuffer); + PC_ASSERT(DeviceDescriptorBufferLength); + PC_ASSERT(*DeviceDescriptorBufferLength >= sizeof(USB_DEVICE_DESCRIPTOR)); + PC_ASSERT(ConfigDescriptorBufferLength); + PC_ASSERT(*ConfigDescriptorBufferLength >= sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_GetUsbDescriptors invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // get device descriptor + // + UsbDevice->GetDeviceDescriptor((PUSB_DEVICE_DESCRIPTOR)DeviceDescriptorBuffer); + + // + // store result length + // + *DeviceDescriptorBufferLength = sizeof(USB_DEVICE_DESCRIPTOR); + + // + // get configuration descriptor + // + UsbDevice->GetConfigurationDescriptors((PUSB_CONFIGURATION_DESCRIPTOR)ConfigDescriptorBuffer, *ConfigDescriptorBufferLength, ConfigDescriptorBufferLength); + + // + // complete the request + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_RemoveUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle, + ULONG Flags) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + NTSTATUS Status; + + DPRINT("USBHI_RemoveUsbDevice\n"); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_RemoveUsbDevice invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // check if there were flags passed + // + if (Flags & USBD_KEEP_DEVICE_DATA || Flags & USBD_MARK_DEVICE_BUSY) + { + // + // ignore flags for now + // + return STATUS_SUCCESS; + } + + // + // remove device + // + Status = Controller->RemoveUsbDevice(UsbDevice); + if (!NT_SUCCESS(Status)) + { + // + // invalid device handle + // + DPRINT1("USBHI_RemoveUsbDevice Invalid device handle %p\n", UsbDevice); + PC_ASSERT(0); + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // release usb device + // + UsbDevice->Release(); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_RestoreUsbDevice( + PVOID BusContext, + PUSB_DEVICE_HANDLE OldDeviceHandle, + PUSB_DEVICE_HANDLE NewDeviceHandle) +{ + PUSBDEVICE OldUsbDevice, NewUsbDevice; + CHubController * Controller; + + DPRINT("USBHI_RestoreUsbDevice\n"); + + // + // first get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + OldUsbDevice = (PUSBDEVICE)OldDeviceHandle; + NewUsbDevice = (PUSBDEVICE)NewDeviceHandle; + PC_ASSERT(OldUsbDevice); + PC_ASSERT(NewDeviceHandle); + + // + // validate device handle + // + PC_ASSERT(Controller->ValidateUsbDevice(NewUsbDevice)); + PC_ASSERT(Controller->ValidateUsbDevice(OldUsbDevice)); + + DPRINT("NewUsbDevice: DeviceAddress %x\n", NewUsbDevice->GetDeviceAddress()); + DPRINT("OldUsbDevice: DeviceAddress %x\n", OldUsbDevice->GetDeviceAddress()); + + // + // remove old device handle + // + USBHI_RemoveUsbDevice(BusContext, OldDeviceHandle, 0); + + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_QueryDeviceInformation( + PVOID BusContext, + PUSB_DEVICE_HANDLE DeviceHandle, + PVOID DeviceInformationBuffer, + ULONG DeviceInformationBufferLength, + PULONG LengthReturned) +{ + PUSB_DEVICE_INFORMATION_0 DeviceInfo; + PUSBDEVICE UsbDevice; + CHubController * Controller; + + DPRINT("USBHI_QueryDeviceInformation %p\n", BusContext); + + // + // sanity check + // + PC_ASSERT(DeviceInformationBufferLength >= sizeof(USB_DEVICE_INFORMATION_0)); + PC_ASSERT(DeviceInformationBuffer); + PC_ASSERT(LengthReturned); + + // + // get controller object + // + Controller = (CHubController*)BusContext; + PC_ASSERT(Controller); + + // + // get device object + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + PC_ASSERT(UsbDevice); + + if (BusContext != DeviceHandle) + { + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_QueryDeviceInformation invalid device handle %p\n", DeviceHandle); + + // + // invalid device handle + // + return STATUS_DEVICE_NOT_CONNECTED; + } + + // + // access information buffer + // + DeviceInfo = (PUSB_DEVICE_INFORMATION_0)DeviceInformationBuffer; + + // + // initialize with default values + // + DeviceInfo->InformationLevel = 0; + DeviceInfo->ActualLength = sizeof(USB_DEVICE_INFORMATION_0); + DeviceInfo->PortNumber = UsbDevice->GetPort(); + DeviceInfo->CurrentConfigurationValue = UsbDevice->GetConfigurationValue(); + DeviceInfo->DeviceAddress = UsbDevice->GetDeviceAddress(); + DeviceInfo->HubAddress = 0; //FIXME + DeviceInfo->DeviceSpeed = UsbDevice->GetSpeed(); + DeviceInfo->DeviceType = UsbDevice->GetType(); + DeviceInfo->NumberOfOpenPipes = 0; //FIXME + + // + // get device descriptor + // + UsbDevice->GetDeviceDescriptor(&DeviceInfo->DeviceDescriptor); + + // + // FIXME return pipe information + // + + // + // store result length + // + *LengthReturned = sizeof(USB_DEVICE_INFORMATION_0); + + return STATUS_SUCCESS; + } + + // + // access information buffer + // + DeviceInfo = (PUSB_DEVICE_INFORMATION_0)DeviceInformationBuffer; + + // + // initialize with default values + // + DeviceInfo->InformationLevel = 0; + DeviceInfo->ActualLength = sizeof(USB_DEVICE_INFORMATION_0); + DeviceInfo->PortNumber = 0; + DeviceInfo->CurrentConfigurationValue = 0; //FIXME; + DeviceInfo->DeviceAddress = 0; + DeviceInfo->HubAddress = 0; //FIXME + DeviceInfo->DeviceSpeed = UsbFullSpeed; //FIXME + DeviceInfo->DeviceType = Usb11Device; //FIXME + DeviceInfo->NumberOfOpenPipes = 0; //FIXME + + // + // get device descriptor + // + RtlMoveMemory(&DeviceInfo->DeviceDescriptor, ROOTHUB2_DEVICE_DESCRIPTOR, sizeof(USB_DEVICE_DESCRIPTOR)); + + // + // FIXME return pipe information + // + + // + // store result length + // +#ifdef _MSC_VER + *LengthReturned = FIELD_OFFSET(USB_DEVICE_INFORMATION_0, PipeList[DeviceInfo->NumberOfOpenPipes]); +#else + *LengthReturned = sizeof(USB_DEVICE_INFORMATION_0) + (DeviceInfo->NumberOfOpenPipes > 1 ? (DeviceInfo->NumberOfOpenPipes - 1) * sizeof(USB_PIPE_INFORMATION_0) : 0); +#endif + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetControllerInformation( + PVOID BusContext, + PVOID ControllerInformationBuffer, + ULONG ControllerInformationBufferLength, + PULONG LengthReturned) +{ + PUSB_CONTROLLER_INFORMATION_0 ControllerInfo; + + DPRINT("USBHI_GetControllerInformation\n"); + + // + // sanity checks + // + PC_ASSERT(ControllerInformationBuffer); + PC_ASSERT(ControllerInformationBufferLength >= sizeof(USB_CONTROLLER_INFORMATION_0)); + + // + // get controller info buffer + // + ControllerInfo = (PUSB_CONTROLLER_INFORMATION_0)ControllerInformationBuffer; + + // + // FIXME only version 0 is supported for now + // + PC_ASSERT(ControllerInfo->InformationLevel == 0); + + // + // fill in information + // + ControllerInfo->ActualLength = sizeof(USB_CONTROLLER_INFORMATION_0); + ControllerInfo->SelectiveSuspendEnabled = FALSE; //FIXME + ControllerInfo->IsHighSpeedController = FALSE; + + // + // set length returned + // + *LengthReturned = ControllerInfo->ActualLength; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_ControllerSelectiveSuspend( + PVOID BusContext, + BOOLEAN Enable) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetExtendedHubInformation( + PVOID BusContext, + PDEVICE_OBJECT HubPhysicalDeviceObject, + PVOID HubInformationBuffer, + ULONG HubInformationBufferLength, + PULONG LengthReturned) +{ + PUSB_EXTHUB_INFORMATION_0 HubInfo; + CHubController * Controller; + PUSBHARDWAREDEVICE Hardware; + ULONG Index; + ULONG NumPort, Dummy2; + USHORT Dummy1; + NTSTATUS Status; + + DPRINT("USBHI_GetExtendedHubInformation\n"); + + // + // sanity checks + // + PC_ASSERT(HubInformationBuffer); + PC_ASSERT(HubInformationBufferLength == sizeof(USB_EXTHUB_INFORMATION_0)); + PC_ASSERT(LengthReturned); + + // + // get hub controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get usb hardware device + // + Hardware = Controller->GetUsbHardware(); + + // + // retrieve number of ports + // + Status = Hardware->GetDeviceDetails(&Dummy1, &Dummy1, &NumPort, &Dummy2); + if (!NT_SUCCESS(Status)) + { + // + // failed to get hardware details, ouch ;) + // + DPRINT1("USBHI_GetExtendedHubInformation failed to get hardware details with %x\n", Status); + return Status; + } + + // + // get hub information buffer + // + HubInfo = (PUSB_EXTHUB_INFORMATION_0)HubInformationBuffer; + + // + // initialize hub information + // + HubInfo->InformationLevel = 0; + + // + // store port count + // + HubInfo->NumberOfPorts = NumPort; + + // + // initialize port information + // + for(Index = 0; Index < NumPort; Index++) + { + HubInfo->Port[Index].PhysicalPortNumber = Index + 1; + HubInfo->Port[Index].PortLabelNumber = Index + 1; + HubInfo->Port[Index].VidOverride = 0; + HubInfo->Port[Index].PidOverride = 0; + HubInfo->Port[Index].PortAttributes = 0; //FIXME + } + + // + // store result length + // +#ifdef _MSC_VER + *LengthReturned = FIELD_OFFSET(USB_EXTHUB_INFORMATION_0, Port[HubInfo->NumberOfPorts]); +#else + *LengthReturned = FIELD_OFFSET(USB_EXTHUB_INFORMATION_0, Port) + sizeof(USB_EXTPORT_INFORMATION_0) * HubInfo->NumberOfPorts; +#endif + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USB_BUSIFFN +USBHI_GetRootHubSymbolicName( + PVOID BusContext, + PVOID HubSymNameBuffer, + ULONG HubSymNameBufferLength, + PULONG HubSymNameActualLength) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +PVOID +USB_BUSIFFN +USBHI_GetDeviceBusContext( + PVOID HubBusContext, + PVOID DeviceHandle) +{ + UNIMPLEMENTED + return NULL; +} + +NTSTATUS +USB_BUSIFFN +USBHI_RootHubInitNotification( + PVOID BusContext, + PVOID CallbackContext, + PRH_INIT_CALLBACK CallbackRoutine) +{ + CHubController * Controller; + + DPRINT("USBHI_RootHubInitNotification %p \n", CallbackContext); + + // + // get controller object + // + Controller = (CHubController*)BusContext; + PC_ASSERT(Controller); + + // + // set notification routine + // + Controller->SetNotification(CallbackContext, CallbackRoutine); + + // + // FIXME: determine when to perform callback + // + CallbackRoutine(CallbackContext); + + // + // done + // + return STATUS_SUCCESS; +} + +VOID +USB_BUSIFFN +USBHI_FlushTransfers( + PVOID BusContext, + PVOID DeviceHandle) +{ + UNIMPLEMENTED +} + +VOID +USB_BUSIFFN +USBHI_SetDeviceHandleData( + PVOID BusContext, + PVOID DeviceHandle, + PDEVICE_OBJECT UsbDevicePdo) +{ + PUSBDEVICE UsbDevice; + CHubController * Controller; + + // + // get controller + // + Controller = (CHubController *)BusContext; + PC_ASSERT(Controller); + + // + // get device handle + // + UsbDevice = (PUSBDEVICE)DeviceHandle; + + // + // validate device handle + // + if (!Controller->ValidateUsbDevice(UsbDevice)) + { + DPRINT1("USBHI_SetDeviceHandleData DeviceHandle %p is invalid\n", DeviceHandle); + + // + // invalid handle + // + return; + } + else + { + // + // usbhub sends this request as a part of the Pnp startup sequence + // looks like we need apply a dragon voodoo to fixup the device stack + // otherwise usbhub will cause a bugcheck + // + DPRINT("USBHI_SetDeviceHandleData %p\n", UsbDevicePdo); + + // + // sanity check + // + PC_ASSERT(UsbDevicePdo->AttachedDevice); + + // + // should be usbstor + // fixup device stack voodoo part #2 + // + UsbDevicePdo->AttachedDevice->StackSize++; + + // + // set device handle data + // + UsbDevice->SetDeviceHandleData(UsbDevicePdo); + } +} + +//================================================================================================= +// +// USB Device Interface functions +// + +VOID +USB_BUSIFFN +USBDI_GetUSBDIVersion( + PVOID BusContext, + PUSBD_VERSION_INFORMATION VersionInformation, + PULONG HcdCapabilites) +{ + CHubController * Controller; + PUSBHARDWAREDEVICE Device; + ULONG Speed, Dummy2; + USHORT Dummy1; + + DPRINT("USBDI_GetUSBDIVersion\n"); + + // + // get controller + // + Controller = (CHubController*)BusContext; + + // + // get usb hardware + // + Device = Controller->GetUsbHardware(); + PC_ASSERT(Device); + + if (VersionInformation) + { + // + // windows xp supported + // + VersionInformation->USBDI_Version = 0x00000500; + + // + // get device speed + // + Device->GetDeviceDetails(&Dummy1, &Dummy1, &Dummy2, &Speed); + + // + // store speed details + // + VersionInformation->Supported_USB_Version = Speed; + } + + // + // no flags supported + // + *HcdCapabilites = 0; +} + +NTSTATUS +USB_BUSIFFN +USBDI_QueryBusTime( + PVOID BusContext, + PULONG CurrentFrame) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +USB_BUSIFFN +USBDI_SubmitIsoOutUrb( + PVOID BusContext, + PURB Urb) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +USB_BUSIFFN +USBDI_QueryBusInformation( + PVOID BusContext, + ULONG Level, + PVOID BusInformationBuffer, + PULONG BusInformationBufferLength, + PULONG BusInformationActualLength) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +BOOLEAN +USB_BUSIFFN +USBDI_IsDeviceHighSpeed( + PVOID BusContext) +{ + CHubController * Controller; + PUSBHARDWAREDEVICE Device; + ULONG Speed, Dummy2; + USHORT Dummy1; + + DPRINT("USBDI_IsDeviceHighSpeed\n"); + + // + // get controller + // + Controller = (CHubController*)BusContext; + + // + // get usb hardware + // + Device = Controller->GetUsbHardware(); + PC_ASSERT(Device); + + // + // get device speed + // + Device->GetDeviceDetails(&Dummy1, &Dummy1, &Dummy2, &Speed); + + // + // USB 2.0 equals 0x200 + // + return (Speed == 0x200); +} + +NTSTATUS +USB_BUSIFFN +USBDI_EnumLogEntry( + PVOID BusContext, + ULONG DriverTag, + ULONG EnumTag, + ULONG P1, + ULONG P2) +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CHubController::HandleQueryInterface( + PIO_STACK_LOCATION IoStack) +{ + PUSB_BUS_INTERFACE_HUB_V5 InterfaceHub; + PUSB_BUS_INTERFACE_USBDI_V2 InterfaceDI; + UNICODE_STRING GuidBuffer; + NTSTATUS Status; + + if (IsEqualGUIDAligned(*IoStack->Parameters.QueryInterface.InterfaceType, USB_BUS_INTERFACE_HUB_GUID)) + { + // + // get request parameters + // + InterfaceHub = (PUSB_BUS_INTERFACE_HUB_V5)IoStack->Parameters.QueryInterface.Interface; + InterfaceHub->Version = IoStack->Parameters.QueryInterface.Version; + + // + // check version + // + if (IoStack->Parameters.QueryInterface.Version >= 6) + { + DPRINT1("USB_BUS_INTERFACE_HUB_GUID version %x not supported!\n", IoStack->Parameters.QueryInterface.Version); + + // + // version not supported + // + return STATUS_NOT_SUPPORTED; + } + + // + // Interface version 0 + // + if (IoStack->Parameters.QueryInterface.Version >= 0) + { + InterfaceHub->Size = IoStack->Parameters.QueryInterface.Size; + InterfaceHub->BusContext = PVOID(this); + InterfaceHub->InterfaceReference = USBI_InterfaceReference; + InterfaceHub->InterfaceDereference = USBI_InterfaceDereference; + } + + // + // Interface version 1 + // + if (IoStack->Parameters.QueryInterface.Version >= 1) + { + InterfaceHub->CreateUsbDevice = USBHI_CreateUsbDevice; + InterfaceHub->InitializeUsbDevice = USBHI_InitializeUsbDevice; + InterfaceHub->GetUsbDescriptors = USBHI_GetUsbDescriptors; + InterfaceHub->RemoveUsbDevice = USBHI_RemoveUsbDevice; + InterfaceHub->RestoreUsbDevice = USBHI_RestoreUsbDevice; + InterfaceHub->QueryDeviceInformation = USBHI_QueryDeviceInformation; + } + + // + // Interface version 2 + // + if (IoStack->Parameters.QueryInterface.Version >= 2) + { + InterfaceHub->GetControllerInformation = USBHI_GetControllerInformation; + InterfaceHub->ControllerSelectiveSuspend = USBHI_ControllerSelectiveSuspend; + InterfaceHub->GetExtendedHubInformation = USBHI_GetExtendedHubInformation; + InterfaceHub->GetRootHubSymbolicName = USBHI_GetRootHubSymbolicName; + InterfaceHub->GetDeviceBusContext = USBHI_GetDeviceBusContext; + } + + // + // Interface version 3 + // + if (IoStack->Parameters.QueryInterface.Version >= 3) + { + InterfaceHub->RootHubInitNotification = USBHI_RootHubInitNotification; + } + + // + // Interface version 4 + // + if (IoStack->Parameters.QueryInterface.Version >= 4) + { + InterfaceHub->FlushTransfers = USBHI_FlushTransfers; + } + + // + // Interface version 5 + // + if (IoStack->Parameters.QueryInterface.Version >= 5) + { + InterfaceHub->SetDeviceHandleData = USBHI_SetDeviceHandleData; + } + + // + // request completed + // + return STATUS_SUCCESS; + } + else if (IsEqualGUIDAligned(*IoStack->Parameters.QueryInterface.InterfaceType, USB_BUS_INTERFACE_USBDI_GUID)) + { + // + // get request parameters + // + InterfaceDI = (PUSB_BUS_INTERFACE_USBDI_V2) IoStack->Parameters.QueryInterface.Interface; + InterfaceDI->Version = IoStack->Parameters.QueryInterface.Version; + + // + // check version + // + if (IoStack->Parameters.QueryInterface.Version >= 3) + { + DPRINT1("USB_BUS_INTERFACE_USBDI_GUID version %x not supported!\n", IoStack->Parameters.QueryInterface.Version); + + // + // version not supported + // + return STATUS_NOT_SUPPORTED; + } + + // + // interface version 0 + // + if (IoStack->Parameters.QueryInterface.Version >= 0) + { + InterfaceDI->Size = IoStack->Parameters.QueryInterface.Size; + InterfaceDI->BusContext = PVOID(this); + InterfaceDI->InterfaceReference = USBI_InterfaceReference; + InterfaceDI->InterfaceDereference = USBI_InterfaceDereference; + InterfaceDI->GetUSBDIVersion = USBDI_GetUSBDIVersion; + InterfaceDI->QueryBusTime = USBDI_QueryBusTime; + InterfaceDI->SubmitIsoOutUrb = USBDI_SubmitIsoOutUrb; + InterfaceDI->QueryBusInformation = USBDI_QueryBusInformation; + } + + // + // interface version 1 + // + if (IoStack->Parameters.QueryInterface.Version >= 1) + { + InterfaceDI->IsDeviceHighSpeed = USBDI_IsDeviceHighSpeed; + } + + // + // interface version 2 + // + if (IoStack->Parameters.QueryInterface.Version >= 2) + { + InterfaceDI->EnumLogEntry = USBDI_EnumLogEntry; + } + + // + // request completed + // + return STATUS_SUCCESS; + } + else + { + // + // convert guid to string + // + Status = RtlStringFromGUID(*IoStack->Parameters.QueryInterface.InterfaceType, &GuidBuffer); + if (NT_SUCCESS(Status)) + { + // + // print interface + // + DPRINT1("HandleQueryInterface UNKNOWN INTERFACE GUID: %wZ Version %x\n", &GuidBuffer, IoStack->Parameters.QueryInterface.Version); + + // + // free guid buffer + // + RtlFreeUnicodeString(&GuidBuffer); + } + } + return STATUS_NOT_SUPPORTED; +} + +NTSTATUS +CHubController::SetDeviceInterface( + BOOLEAN Enable) +{ + NTSTATUS Status = STATUS_SUCCESS; + + if (Enable) + { + // + // register device interface + // + Status = IoRegisterDeviceInterface(m_HubControllerDeviceObject, &GUID_DEVINTERFACE_USB_HUB, 0, &m_HubDeviceInterfaceString); + + if (NT_SUCCESS(Status)) + { + // + // now enable the device interface + // + Status = IoSetDeviceInterfaceState(&m_HubDeviceInterfaceString, TRUE); + + // + // enable interface + // + m_InterfaceEnabled = TRUE; + } + } + else if (m_InterfaceEnabled) + { + // + // disable device interface + // + Status = IoSetDeviceInterfaceState(&m_HubDeviceInterfaceString, FALSE); + + if (NT_SUCCESS(Status)) + { + // + // now delete interface string + // + RtlFreeUnicodeString(&m_HubDeviceInterfaceString); + } + + // + // disable interface + // + m_InterfaceEnabled = FALSE; + } + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CHubController::CreatePDO( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT * OutDeviceObject) +{ + WCHAR CharDeviceName[64]; + NTSTATUS Status; + ULONG UsbDeviceNumber = 0; + UNICODE_STRING DeviceName; + + while (TRUE) + { + // + // construct device name + // + swprintf(CharDeviceName, L"\\Device\\USBPDO-%d", UsbDeviceNumber); + + // + // initialize device name + // + RtlInitUnicodeString(&DeviceName, CharDeviceName); + + // + // create device + // + Status = IoCreateDevice(DriverObject, + sizeof(COMMON_DEVICE_EXTENSION), + &DeviceName, + FILE_DEVICE_CONTROLLER, + 0, + FALSE, + OutDeviceObject); + + /* check for success */ + if (NT_SUCCESS(Status)) + break; + + // + // is there a device object with that same name + // + if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION)) + { + // + // Try the next name + // + UsbDeviceNumber++; + continue; + } + + // + // bail out on other errors + // + if (!NT_SUCCESS(Status)) + { + DPRINT1("CreatePDO: Failed to create %wZ, Status %x\n", &DeviceName, Status); + return Status; + } + } + + DPRINT("CHubController::CreatePDO: DeviceName %wZ\n", &DeviceName); + + // + // fixup device stack voodoo part #1 + // + (*OutDeviceObject)->StackSize++; + + /* done */ + return Status; +} + + + +NTSTATUS +CreateHubController( + PHUBCONTROLLER *OutHcdController) +{ + PHUBCONTROLLER This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBOHCI) CHubController(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutHcdController = (PHUBCONTROLLER)This; + + // + // done + // + return STATUS_SUCCESS; +} + +VOID StatusChangeEndpointCallBack(PVOID Context) +{ + CHubController* This; + PIRP Irp; + This = (CHubController*)Context; + + ASSERT(This); + + Irp = This->m_PendingSCEIrp; + if (!Irp) + { + DPRINT("There was no pending IRP for SCE. Did the usb hub 2.0 driver (usbhub2) load?\n"); + return; + } + + This->m_PendingSCEIrp = NULL; + This->QueryStatusChageEndpoint(Irp); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); +} diff --git a/reactos/drivers/usb/usbohci/interfaces.h b/reactos/drivers/usb/usbohci/interfaces.h new file mode 100644 index 00000000000..c8b50ffc54f --- /dev/null +++ b/reactos/drivers/usb/usbohci/interfaces.h @@ -0,0 +1,834 @@ + +#ifndef INTERFACES_HPP +#define INTERFACES_HPP + +//--------------------------------------------------------------------------- +// +// Object Hierachy +// -------------------------------------------------------------------- +// | IRootHCDController | +// | IHCDController Intel USB Universal Host Controller - 3A37 | +// | IHCDController - Intel USB Universal HostController - 3A38 | +// | IHCDController - Intel USB Universal HostController - 3A38 | +// |------------------------------------------------------------------| +// +// +// IHCDController Intel USB Universal Host Controller - 3A37 +// IHubController +// IUSBHardwareDevice +// IDMAMemoryManager +// IUSBQueue <- interacts with -> IUSBRequest +// +// +// Each IHCDController creates an IUSBHardwareDevice class upon initialization. The +// IUSBHardwardeDevice class is used to abstract usb controller specifics. The IHubController +// manages all attached devices and handles hub control ioctl requests. +// +// Each IUSBHardwareDevice has one IDMAMemoryManager and one IUSBQueue. The IDMAMemoryManager +// is used to handle dma memory allocations. The IUSBQueue manages requests which are send to the +// usb hardware. See IUSBRequest class for details. +// + + +//========================================================================================= +// +// class IRootHCDController +// +// Description: This class serves as the root host controller. The host controller mantains +// a list of registered controllers and provides support functions for the host controllers + +struct IHCDController; + +DECLARE_INTERFACE_(IRootHCDController, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: This function initializes the root host controller. It allocates the resources +// required to manage the registered controllers + + virtual NTSTATUS Initialize() = 0; + +//----------------------------------------------------------------------------------------- +// +// RegisterHCD +// +// Description: this function registers a host controller with the root host controller + + virtual NTSTATUS RegisterHCD(struct IHCDController * Controller) = 0; + +//----------------------------------------------------------------------------------------- +// +// UnregisterHCD +// +// Description: this function unregistes a host controller + + virtual NTSTATUS UnregisterHCD(struct IHCDController * Controller) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetControllerCount +// +// Description: returns the number of host controllers registered + + virtual ULONG GetControllerCount() = 0; + +}; + +typedef IRootHCDController *PROOTHDCCONTROLLER; + +//========================================================================================= +// +// class IHCDController +// +// Description: This class is used to manage a single USB host controller +// + +DECLARE_INTERFACE_(IHCDController, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: This function initializes the IHCDController implementation. +// It creates an IUSBHardwareDevice object and initializes it. It also registeres itself with +// the IRootHCDController +// + virtual NTSTATUS Initialize(IN PROOTHDCCONTROLLER RootHCDController, + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) = 0; + +}; + +typedef IHCDController *PHCDCONTROLLER; + +struct _OHCI_ENDPOINT_DESCRIPTOR; +//========================================================================================= +// +// class IUSBHardwareDevice +// +// Description: This class provides access to the usb hardware controller +// + +struct IDMAMemoryManager; +struct IUSBQueue; + +DECLARE_INTERFACE_(IUSBHardwareDevice, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: Initializes the usb device controller + + virtual NTSTATUS Initialize(PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT FunctionalDeviceObject, + PDEVICE_OBJECT PhysicalDeviceObject, + PDEVICE_OBJECT LowerDeviceObject) = 0; + +//----------------------------------------------------------------------------------------- +// +// PnpStart +// +// Description: handles pnp start request from device. It registeres the interrupt, +// sets up the ports and prepares the device. It then starts the controller + + virtual NTSTATUS PnpStart(PCM_RESOURCE_LIST RawResources, + PCM_RESOURCE_LIST TranslatedResources) = 0; + +//----------------------------------------------------------------------------------------- +// +// PnpStop +// +// Description: handles pnp stop request from device. It unregisteres the interrupt, releases ports and dma object. + + virtual NTSTATUS PnpStop(void) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetDeviceDetails +// +// Description: returns the device details such as vendor id, device id, number of ports and speed + + virtual NTSTATUS GetDeviceDetails(OUT OPTIONAL PUSHORT VendorId, + OUT OPTIONAL PUSHORT DeviceId, + OUT OPTIONAL PULONG NumberOfPorts, + OUT OPTIONAL PULONG Speed) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetUSBQueue +// +// Description: returns interface to internal IUSBQueue +// Interface is reference counted, you need to call release method when you are done with it +// Do not call Initialize on IUSBQueue, the object is already initialized + + virtual NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetBulkHeadEndpointDescriptor +// +// Description: returns the bulk head endpoint descriptor + + virtual NTSTATUS GetBulkHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetControlHeadEndpointDescriptor +// +// Description: returns the control head endpoint descriptor + + virtual NTSTATUS GetControlHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetIsochronousHeadEndpointDescriptor +// +// Description: returns the control head endpoint descriptor + + virtual NTSTATUS GetIsochronousHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) = 0; + + +//----------------------------------------------------------------------------------------- +// +// GetInterruptEndpointDescriptors +// +// Description: returns interrupt endpoint descriptors + + virtual NTSTATUS GetInterruptEndpointDescriptors(struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptorArray) = 0; + +//----------------------------------------------------------------------------------------- +// +// HeadEndpointDescriptorModified +// +// Description: notifies the hardware that an endpoint descriptor was added to head endpoint descriptor + + virtual VOID HeadEndpointDescriptorModified(ULONG HeadType) = 0; + + + + +//----------------------------------------------------------------------------------------- +// +// GetDMA +// +// Description: returns the DMA object which can be used to allocate memory from the common buffer + + virtual NTSTATUS GetDMA(OUT struct IDMAMemoryManager **OutDMAMemoryManager) = 0; + + +//----------------------------------------------------------------------------------------- +// +// ResetController() +// +// Description: this function resets the controller +// Returns STATUS_SUCCESS when the controller was successfully reset + + virtual NTSTATUS ResetController() = 0; + +//----------------------------------------------------------------------------------------- +// +// StartController +// +// Description: this functions starts controller allowing interrupts for device connects/removal, and execution of +// Periodic and Asynchronous Schedules. +// + + virtual NTSTATUS StartController() = 0; + +//----------------------------------------------------------------------------------------- +// +// StopController +// +// Description: this functions stops controller disabling interrupts for device connects/removal, and execution of +// Periodic and Asynchronous Schedules. +// + + virtual NTSTATUS StopController() = 0; + +//----------------------------------------------------------------------------------------- +// +// ResetPort +// +// Description: this functions resets the port on the controller +// + + virtual NTSTATUS ResetPort(ULONG PortNumber) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetPortStatus +// +// Description: this functions return status and change state of port +// + virtual NTSTATUS GetPortStatus(ULONG PortId, OUT USHORT *PortStatus, OUT USHORT *PortChange) = 0; + +//----------------------------------------------------------------------------------------- +// +// ClearPortStatus +// +// Description: Clears Status of Port, for example Connection, Enable and Reset +// + virtual NTSTATUS ClearPortStatus(ULONG PortId, ULONG Status) = 0; + +//----------------------------------------------------------------------------------------- +// +// SetPortFeature +// +// Description: this functions Sets Feature on Port, for example Enable, Power and Reset +// + virtual NTSTATUS SetPortFeature(ULONG PortId, ULONG Feature) = 0; + +//----------------------------------------------------------------------------------------- +// +// SetStatusChangeEndpointCallBack +// +// Description: Used to callback to the hub controller when SCE detected +// + virtual VOID SetStatusChangeEndpointCallBack(PVOID CallBack,PVOID Context) = 0; + +//----------------------------------------------------------------------------------------- +// +// AcquireDeviceLock +// +// Description: acquires the device lock + + virtual KIRQL AcquireDeviceLock(void) = 0; + +//----------------------------------------------------------------------------------------- +// +// ReleaseLock +// +// Description: releases the device lock + + virtual void ReleaseDeviceLock(KIRQL OldLevel) = 0; + +//---------------------------------------------------------------------------------------- +// +// GetCurrentFrameNumber +// +// Description: returns the current frame number + + virtual VOID GetCurrentFrameNumber(PULONG FrameNumber) = 0; +}; + +typedef IUSBHardwareDevice *PUSBHARDWAREDEVICE; + + +//========================================================================================= +// +// class IDMAMemoryManager +// +// Description: This class provides access to the dma buffer. It provides methods to +// allocate and free from the dma buffer +// + +DECLARE_INTERFACE_(IDMAMemoryManager, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: initializes the memory manager + + virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Device, + IN PKSPIN_LOCK Lock, + IN ULONG DmaBufferSize, + IN PVOID VirtualBase, + IN PHYSICAL_ADDRESS PhysicalAddress, + IN ULONG DefaultBlockSize) = 0; + +//----------------------------------------------------------------------------------------- +// +// Allocate +// +// Description: allocates block of memory from allocator + + virtual NTSTATUS Allocate(IN ULONG Size, + OUT PVOID *OutVirtualBase, + OUT PPHYSICAL_ADDRESS OutPhysicalAddress) = 0; + + +//----------------------------------------------------------------------------------------- +// +// Free +// +// Description: releases memory block + + virtual NTSTATUS Release(IN PVOID VirtualBase, + IN ULONG Size) = 0; + +}; + +typedef IDMAMemoryManager *PDMAMEMORYMANAGER; + + +//========================================================================================= +// +// class IUSBRequest +// +// Description: This class is used to issue request to usb controller. The class is +// initialized using InitializeXXX methods. You also need to call SetEndpoint to define the endpoint +// In addition you can call SetCompletionDetails if you need to wait for the end of +// the request or want to complete an irp. You call AddUSBRequest to add the request to the queue. +// Once the request is completed the CompletionCallback is invoked. The CompletionCallback +// will take care of any completion details which have been set. If the request is cancelled, the +// CancelCallback routine is invoked. +// + +DECLARE_INTERFACE_(IUSBRequest, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// InitializeWithSetupPacket +// +// Description: initializes the request packet with an setup packet +// If there is a TransferBuffer, the TransferBufferLength contains the length of the buffer + + + virtual NTSTATUS InitializeWithSetupPacket(IN PDMAMEMORYMANAGER DmaManager, + IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, + IN UCHAR DeviceAddress, + IN OPTIONAL PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor, + IN USB_DEVICE_SPEED DeviceSpeed, + IN OUT ULONG TransferBufferLength, + IN OUT PMDL TransferBuffer) = 0; + +//----------------------------------------------------------------------------------------- +// +// InitializeWithIrp +// +// Description: initializes the request with an IRP +// The irp contains an URB block which contains all necessary information +// contains the device speed (FullSpeed / LowSpeed) + + virtual NTSTATUS InitializeWithIrp(IN PDMAMEMORYMANAGER DmaManager, + IN OUT PIRP Irp, + IN USB_DEVICE_SPEED DeviceSpeed) = 0; + +//----------------------------------------------------------------------------------------- +// +// IsRequestComplete +// +// Description: returns true when the request has been completed +// Should be called after the CompletionCallback has been invoked +// This function is called by IUSBQueue after queue head has been completed +// If the function returns true, IUSBQueue will then call ShouldReleaseRequestAfterCompletion +// If that function returns also true, it calls Release() to delete the IUSBRequest + + virtual BOOLEAN IsRequestComplete() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetTransferType +// +// Description: returns the type of the request: control, bulk, iso, interrupt + + virtual ULONG GetTransferType() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetEndpointDescriptor +// +// Description: returns the general transfer descriptor + + virtual NTSTATUS GetEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetResultStatus +// +// Description: returns the status code of the result +// Note: this function will block the caller untill the request has been completed + + virtual VOID GetResultStatus(OUT OPTIONAL NTSTATUS * NtStatusCode, + OUT OPTIONAL PULONG UrbStatusCode) = 0; + +//----------------------------------------------------------------------------------------- +// +// IsRequestInitialized +// +// Description: returns true when the request has been successfully initialized using InitializeXXX methods + + virtual BOOLEAN IsRequestInitialized() = 0; + +//----------------------------------------------------------------------------------------- +// +// CompletionCallback +// +// Description: notifies request that the endpoint descriptor is complete + + virtual VOID CompletionCallback(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor) = 0; + +//----------------------------------------------------------------------------------------- +// +// FreeEndpointDescriptor +// +// Description: frees the associated endpoint descriptor and its general descriptors + + virtual VOID FreeEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetInterruptInterval +// +// Description: returns interval of the iso / interrupt + + virtual UCHAR GetInterval() = 0; + +}; + + +typedef IUSBRequest *PUSBREQUEST; + +//========================================================================================= +// +// class IUSBQueue +// +// Description: This class manages pending requests +// + +DECLARE_INTERFACE_(IUSBQueue, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: initializes the object + + virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Hardware, + IN PDMA_ADAPTER AdapterObject, + IN PDMAMEMORYMANAGER MemManager, + IN OPTIONAL PKSPIN_LOCK Lock) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetPendingRequestCount +// +// Description: returns the number of pending requests true from IsRequestComplete + + virtual ULONG GetPendingRequestCount() = 0; + +//----------------------------------------------------------------------------------------- +// +// AddUSBRequest +// +// Description: adds an usb request to the queue. +// Returns status success when successful + + virtual NTSTATUS AddUSBRequest(IUSBRequest * Request) = 0; + +//----------------------------------------------------------------------------------------- +// +// CancelRequests() +// +// Description: cancels all requests + + virtual NTSTATUS CancelRequests() = 0; + +//----------------------------------------------------------------------------------------- +// +// CreateUSBRequest +// +// Description: creates an usb request + + virtual NTSTATUS CreateUSBRequest(IUSBRequest **OutRequest) = 0; + +//----------------------------------------------------------------------------------------- +// +// TransferDescriptorCompletionCallback +// +// Description: notifies the queue that a transfer was completed + + virtual VOID TransferDescriptorCompletionCallback(ULONG TransferDescriptorLogicalAddress) = 0; + + +//----------------------------------------------------------------------------------------- +// +// AbortDevicePipe +// +// Description: aborts all pending requsts of an device + + virtual NTSTATUS AbortDevicePipe(UCHAR DeviceAddress, IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor) = 0; +}; + +typedef IUSBQueue *PUSBQUEUE; + +//========================================================================================= +// +// class IHubController +// +// Description: This class implements a hub controller +// + +DECLARE_INTERFACE_(IHubController, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//---------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: Initializes the hub controller + + virtual NTSTATUS Initialize(IN PDRIVER_OBJECT DriverObject, + IN PHCDCONTROLLER Controller, + IN PUSBHARDWAREDEVICE Device, + IN BOOLEAN IsRootHubDevice, + IN ULONG DeviceAddress) = 0; + +//---------------------------------------------------------------------------------------- +// +// GetHubControllerDeviceObject +// +// Description: Returns the hub controller device object + + virtual NTSTATUS GetHubControllerDeviceObject(PDEVICE_OBJECT * HubDeviceObject) = 0; + +//---------------------------------------------------------------------------------------- +// +// GetHubControllerSymbolicLink +// +// Description: Returns the symbolic link of the root hub + + virtual NTSTATUS GetHubControllerSymbolicLink(ULONG BufferLength, PVOID Buffer, PULONG RequiredLength) = 0; + + +}; + +typedef IHubController *PHUBCONTROLLER; + +//========================================================================================= +// +// class IDispatchIrp +// +// Description: This class is used to handle irp dispatch requests +// + +DECLARE_INTERFACE_(IDispatchIrp, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//----------------------------------------------------------------------------------------- +// +// HandlePnp +// +// Description: This function handles all pnp requests + + virtual NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) = 0; + +//----------------------------------------------------------------------------------------- +// +// HandlePower +// +// Description: This function handles all power pnp requests +// + virtual NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) = 0; + +//----------------------------------------------------------------------------------------- +// +// HandleDeviceControl +// +// Description: handles device io control requests + + virtual NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) = 0; +}; + +typedef IDispatchIrp *PDISPATCHIRP; + +//========================================================================================= +// +// class IUSBDevice +// +// Description: This class is used to abstract details of a usb device +// + +DECLARE_INTERFACE_(IUSBDevice, IUnknown) +{ + DEFINE_ABSTRACT_UNKNOWN() + +//---------------------------------------------------------------------------------------- +// +// Initialize +// +// Description: Initializes the usb device + + virtual NTSTATUS Initialize(IN PHUBCONTROLLER HubController, + IN PUSBHARDWAREDEVICE Device, + IN PVOID Parent, + IN ULONG Port, + IN ULONG PortStatus) = 0; + +//----------------------------------------------------------------------------------------- +// +// IsHub +// +// Description: returns true when device is a hub + + virtual BOOLEAN IsHub() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetParent +// +// Description: gets the parent device of the this device + + virtual NTSTATUS GetParent(PVOID * Parent) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetDeviceAddress +// +// Description: gets the device address of the this device + + virtual UCHAR GetDeviceAddress() = 0; + + +//----------------------------------------------------------------------------------------- +// +// GetPort +// +// Description: gets the port to which this device is connected + + virtual ULONG GetPort() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetSpeed +// +// Description: gets the speed of the device + + virtual USB_DEVICE_SPEED GetSpeed() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetType +// +// Description: gets the type of the device, either 1.1 or 2.0 device + + virtual USB_DEVICE_TYPE GetType() = 0; + +//----------------------------------------------------------------------------------------- +// +// GetState +// +// Description: gets the device state + + virtual ULONG GetState() = 0; + +//----------------------------------------------------------------------------------------- +// +// SetDeviceHandleData +// +// Description: sets device handle data + + virtual void SetDeviceHandleData(PVOID Data) = 0; + +//----------------------------------------------------------------------------------------- +// +// SetDeviceAddress +// +// Description: sets device handle data + + virtual NTSTATUS SetDeviceAddress(UCHAR DeviceAddress) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetDeviceDescriptor +// +// Description: sets device handle data + + virtual void GetDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetConfigurationValue +// +// Description: gets current selected configuration index + + virtual UCHAR GetConfigurationValue() = 0; + +//----------------------------------------------------------------------------------------- +// +// SubmitIrp +// +// Description: submits an irp containing an urb + + virtual NTSTATUS SubmitIrp(PIRP Irp) = 0; + +//----------------------------------------------------------------------------------------- +// +// GetConfigurationDescriptors +// +// Description: returns one or more configuration descriptors + + virtual VOID GetConfigurationDescriptors(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptorBuffer, + IN ULONG BufferLength, + OUT PULONG OutBufferLength) = 0; + +//----------------------------------------------------------------------------------------- +// +// Description: returns length of configuration descriptors +// + virtual ULONG GetConfigurationDescriptorsLength() = 0; + +//----------------------------------------------------------------------------------------- +// +// SubmitSetupPacket +// +// Description: submits an setup packet. The usb device will then create an usb request from it and submit it to the queue + + virtual NTSTATUS SubmitSetupPacket(IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, + IN OUT ULONG BufferLength, + OUT PVOID Buffer) = 0; + +//----------------------------------------------------------------------------------------- +// +// SelectConfiguration +// +// Description: selects a configuration + + virtual NTSTATUS SelectConfiguration(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + IN PUSBD_INTERFACE_INFORMATION Interface, + OUT USBD_CONFIGURATION_HANDLE *ConfigurationHandle) = 0; + +//----------------------------------------------------------------------------------------- +// +// SelectConfiguration +// +// Description: selects a interface of an configuration + + virtual NTSTATUS SelectInterface(IN USBD_CONFIGURATION_HANDLE ConfigurationHandle, + IN OUT PUSBD_INTERFACE_INFORMATION Interface) = 0; + + +//----------------------------------------------------------------------------------------- +// +// AbortPipe +// +// Description: aborts all pending requsts + + virtual NTSTATUS AbortPipe(IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor) = 0; + +}; + +typedef IUSBDevice *PUSBDEVICE; + +#endif diff --git a/reactos/drivers/usb/usbohci/memory_manager.cpp b/reactos/drivers/usb/usbohci/memory_manager.cpp new file mode 100644 index 00000000000..0f2885ab05a --- /dev/null +++ b/reactos/drivers/usb/usbohci/memory_manager.cpp @@ -0,0 +1,369 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbohci/memory_manager.cpp + * PURPOSE: USB OHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbohci.h" + +class CDMAMemoryManager : public IDMAMemoryManager +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + // IDMAMemoryManager interface functions + virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Device, IN PKSPIN_LOCK Lock, IN ULONG DmaBufferSize, IN PVOID VirtualBase, IN PHYSICAL_ADDRESS PhysicalAddress, IN ULONG DefaultBlockSize); + virtual NTSTATUS Allocate(IN ULONG Size, OUT PVOID *OutVirtualBase, OUT PPHYSICAL_ADDRESS OutPhysicalAddress); + virtual NTSTATUS Release(IN PVOID VirtualBase, IN ULONG Size); + + // constructor / destructor + CDMAMemoryManager(IUnknown *OuterUnknown){} + virtual ~CDMAMemoryManager(){} + +protected: + LONG m_Ref; + PUSBHARDWAREDEVICE m_Device; + PKSPIN_LOCK m_Lock; + LONG m_DmaBufferSize; + PVOID m_VirtualBase; + PHYSICAL_ADDRESS m_PhysicalAddress; + ULONG m_BlockSize; + + PULONG m_BitmapBuffer; + RTL_BITMAP m_Bitmap; +}; + +//---------------------------------------------------------------------------------------- +NTSTATUS +STDMETHODCALLTYPE +CDMAMemoryManager::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS +CDMAMemoryManager::Initialize( + IN PUSBHARDWAREDEVICE Device, + IN PKSPIN_LOCK Lock, + IN ULONG DmaBufferSize, + IN PVOID VirtualBase, + IN PHYSICAL_ADDRESS PhysicalAddress, + IN ULONG DefaultBlockSize) +{ + ULONG BitmapLength; + + // + // sanity checks + // + PC_ASSERT(DmaBufferSize >= PAGE_SIZE); + PC_ASSERT(DmaBufferSize % PAGE_SIZE == 0); + PC_ASSERT(DefaultBlockSize == 32 || DefaultBlockSize == 64 || DefaultBlockSize == 128); + + // + // calculate bitmap length + // + BitmapLength = (DmaBufferSize / DefaultBlockSize) / 8; + + // + // allocate bitmap buffer + // + m_BitmapBuffer = (PULONG)ExAllocatePoolWithTag(NonPagedPool, BitmapLength, TAG_USBOHCI); + if (!m_BitmapBuffer) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize bitmap + // + RtlInitializeBitMap(&m_Bitmap, m_BitmapBuffer, BitmapLength * 8); + + // + // clear all bits + // + RtlClearAllBits(&m_Bitmap); + + // + // initialize rest of memory allocator + // + m_PhysicalAddress = PhysicalAddress; + m_VirtualBase = VirtualBase; + m_DmaBufferSize = DmaBufferSize; + m_BitmapBuffer = m_BitmapBuffer; + m_Lock = Lock; + m_BlockSize = DefaultBlockSize; + + /* done */ + return STATUS_SUCCESS; +} + +NTSTATUS +CDMAMemoryManager::Allocate( + IN ULONG Size, + OUT PVOID *OutVirtualAddress, + OUT PPHYSICAL_ADDRESS OutPhysicalAddress) +{ + ULONG Length, BlockCount, FreeIndex, StartPage, EndPage; + KIRQL OldLevel; + ULONG BlocksPerPage; + + // + // sanity checks + // + ASSERT(Size <= PAGE_SIZE); + //ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); + + // + // align request + // + Length = (Size + m_BlockSize -1) & ~(m_BlockSize -1); + + // + // sanity check + // + ASSERT(Length); + + // + // convert to block count + // + BlockCount = Length / m_BlockSize; + + // + // acquire lock + // + KeAcquireSpinLock(m_Lock, &OldLevel); + + // + // helper variable + // + BlocksPerPage = PAGE_SIZE / m_BlockSize; + + // + // start search + // + FreeIndex = 0; + do + { + // + // search for an free index + // + FreeIndex = RtlFindClearBits(&m_Bitmap, BlockCount, FreeIndex); + + // + // check if there was a block found + // + if (FreeIndex == MAXULONG) + { + // + // no free block found + // + break; + } + + // + // check that the allocation does not spawn over page boundaries + // + StartPage = (FreeIndex * m_BlockSize); + StartPage = (StartPage != 0 ? StartPage / PAGE_SIZE : 0); + EndPage = ((FreeIndex + BlockCount) * m_BlockSize) / PAGE_SIZE; + + // + // does the request start and end on the same page + // + if (StartPage == EndPage) + { + // + // reserve block + // + RtlSetBits(&m_Bitmap, FreeIndex, BlockCount); + + // + // reserve block + // + break; + } + else if ((BlockCount == BlocksPerPage) && (FreeIndex % BlocksPerPage == 0)) + { + // + // the request equals PAGE_SIZE and is aligned at page boundary + // reserve block + // + RtlSetBits(&m_Bitmap, FreeIndex, BlockCount); + + // + // reserve block + // + break; + } + else + { + // + // request spawned over page boundary + // restart search on next page + // + FreeIndex = (EndPage * PAGE_SIZE) / m_BlockSize; + } + } + while(TRUE); + + // + // release lock + // + KeReleaseSpinLock(m_Lock, OldLevel); + + // + // did allocation succeed + // + if (FreeIndex == MAXULONG) + { + // + // failed to allocate block, requestor must retry + // + return STATUS_UNSUCCESSFUL; + } + + // + // return result + // + *OutVirtualAddress = (PVOID)((ULONG_PTR)m_VirtualBase + FreeIndex * m_BlockSize); + OutPhysicalAddress->QuadPart = m_PhysicalAddress.QuadPart + FreeIndex * m_BlockSize; + + // + // clear block + // + RtlZeroMemory(*OutVirtualAddress, Length); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CDMAMemoryManager::Release( + IN PVOID VirtualAddress, + IN ULONG Size) +{ + KIRQL OldLevel; + ULONG BlockOffset = 0, BlockLength, BlockCount; + + // + // sanity checks + // + PC_ASSERT(VirtualAddress); + PC_ASSERT((ULONG_PTR)VirtualAddress >= (ULONG_PTR)m_VirtualBase); + PC_ASSERT((ULONG_PTR)m_VirtualBase + m_DmaBufferSize > (ULONG_PTR)m_VirtualBase); + + // + // calculate block length + // + BlockLength = ((ULONG_PTR)VirtualAddress - (ULONG_PTR)m_VirtualBase); + + // + // check if its the first block + // + if (BlockLength) + { + // + // divide by base block size + // + BlockOffset = BlockLength / m_BlockSize; + } + + // + // align length to block size + // + Size = (Size + m_BlockSize - 1) & ~(m_BlockSize - 1); + + // + // convert to blocks + // + BlockCount = Size / m_BlockSize; + ASSERT(BlockCount); + + // + // acquire lock + // + KeAcquireSpinLock(m_Lock, &OldLevel); + + // + // sanity check + // + ASSERT(RtlAreBitsSet(&m_Bitmap, BlockOffset, BlockCount)); + + // + // release buffer + // + RtlClearBits(&m_Bitmap, BlockOffset, BlockCount); + + // + // release lock + // + KeReleaseSpinLock(m_Lock, OldLevel); + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CreateDMAMemoryManager( + PDMAMEMORYMANAGER *OutMemoryManager) +{ + CDMAMemoryManager* This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBOHCI) CDMAMemoryManager(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutMemoryManager = (PDMAMEMORYMANAGER)This; + + // + // done + // + return STATUS_SUCCESS; +} + diff --git a/reactos/drivers/usb/usbohci/misc.cpp b/reactos/drivers/usb/usbohci/misc.cpp new file mode 100644 index 00000000000..d4380da3eca --- /dev/null +++ b/reactos/drivers/usb/usbohci/misc.cpp @@ -0,0 +1,134 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbohci/misc.cpp + * PURPOSE: USB OHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbohci.h" + +// +// driver verifier +// +IO_COMPLETION_ROUTINE SyncForwardIrpCompletionRoutine; + +NTSTATUS +NTAPI +SyncForwardIrpCompletionRoutine( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context) +{ + if (Irp->PendingReturned) + { + KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE); + } + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +NTAPI +SyncForwardIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + KEVENT Event; + NTSTATUS Status; + + // + // initialize event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // copy irp stack location + // + IoCopyCurrentIrpStackLocationToNext(Irp); + + // + // set completion routine + // + IoSetCompletionRoutine(Irp, SyncForwardIrpCompletionRoutine, &Event, TRUE, TRUE, TRUE); + + + // + // call driver + // + Status = IoCallDriver(DeviceObject, Irp); + + + // + // check if pending + // + if (Status == STATUS_PENDING) + { + // + // wait for the request to finish + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + // + // copy status code + // + Status = Irp->IoStatus.Status; + } + + // + // done + // + return Status; +} + +NTSTATUS +NTAPI +GetBusInterface( + PDEVICE_OBJECT DeviceObject, + PBUS_INTERFACE_STANDARD busInterface) +{ + KEVENT Event; + NTSTATUS Status; + PIRP Irp; + IO_STATUS_BLOCK IoStatus; + PIO_STACK_LOCATION Stack; + + if ((!DeviceObject) || (!busInterface)) + return STATUS_UNSUCCESSFUL; + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, + DeviceObject, + NULL, + 0, + NULL, + &Event, + &IoStatus); + + if (Irp == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Stack=IoGetNextIrpStackLocation(Irp); + Stack->MajorFunction = IRP_MJ_PNP; + Stack->MinorFunction = IRP_MN_QUERY_INTERFACE; + Stack->Parameters.QueryInterface.Size = sizeof(BUS_INTERFACE_STANDARD); + Stack->Parameters.QueryInterface.InterfaceType = (LPGUID)&GUID_BUS_INTERFACE_STANDARD; + Stack->Parameters.QueryInterface.Version = 1; + Stack->Parameters.QueryInterface.Interface = (PINTERFACE)busInterface; + Stack->Parameters.QueryInterface.InterfaceSpecificData = NULL; + Irp->IoStatus.Status=STATUS_NOT_SUPPORTED ; + + Status=IoCallDriver(DeviceObject, Irp); + + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + Status=IoStatus.Status; + } + + return Status; +} + diff --git a/reactos/drivers/usb/usbohci/purecall.cpp b/reactos/drivers/usb/usbohci/purecall.cpp new file mode 100644 index 00000000000..75bc6f8e819 --- /dev/null +++ b/reactos/drivers/usb/usbohci/purecall.cpp @@ -0,0 +1,24 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbohci/purecall.cpp + * PURPOSE: USB OHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbohci.h" + + +extern "C" { + void + __cxa_pure_virtual() + { + // put error handling here + + DbgBreakPoint(); + + } +} + diff --git a/reactos/drivers/usb/usbohci/usb_device.cpp b/reactos/drivers/usb/usbohci/usb_device.cpp new file mode 100644 index 00000000000..09482f9ca09 --- /dev/null +++ b/reactos/drivers/usb/usbohci/usb_device.cpp @@ -0,0 +1,1228 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbohci/usb_device.cpp + * PURPOSE: USB OHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#define INITGUID +#include "usbohci.h" + +class CUSBDevice : public IUSBDevice +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + // IUSBDevice interface functions + virtual NTSTATUS Initialize(IN PHUBCONTROLLER HubController, IN PUSBHARDWAREDEVICE Device, IN PVOID Parent, IN ULONG Port, IN ULONG PortStatus); + virtual BOOLEAN IsHub(); + virtual NTSTATUS GetParent(PVOID * Parent); + virtual UCHAR GetDeviceAddress(); + virtual ULONG GetPort(); + virtual USB_DEVICE_SPEED GetSpeed(); + virtual USB_DEVICE_TYPE GetType(); + virtual ULONG GetState(); + virtual void SetDeviceHandleData(PVOID Data); + virtual NTSTATUS SetDeviceAddress(UCHAR DeviceAddress); + virtual void GetDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor); + virtual UCHAR GetConfigurationValue(); + virtual NTSTATUS SubmitIrp(PIRP Irp); + virtual VOID GetConfigurationDescriptors(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptorBuffer, IN ULONG BufferLength, OUT PULONG OutBufferLength); + virtual ULONG GetConfigurationDescriptorsLength(); + virtual NTSTATUS SubmitSetupPacket(IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, OUT ULONG BufferLength, OUT PVOID Buffer); + virtual NTSTATUS SelectConfiguration(IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, IN PUSBD_INTERFACE_INFORMATION Interface, OUT USBD_CONFIGURATION_HANDLE *ConfigurationHandle); + virtual NTSTATUS SelectInterface(IN USBD_CONFIGURATION_HANDLE ConfigurationHandle, IN OUT PUSBD_INTERFACE_INFORMATION Interface); + virtual NTSTATUS AbortPipe(IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor); + + // local function + virtual NTSTATUS CommitIrp(PIRP Irp); + virtual NTSTATUS CommitSetupPacket(PUSB_DEFAULT_PIPE_SETUP_PACKET Packet, IN OPTIONAL PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG BufferLength, IN OUT PMDL Mdl); + virtual NTSTATUS CreateConfigurationDescriptor(ULONG ConfigurationIndex); + virtual NTSTATUS CreateDeviceDescriptor(); + virtual VOID DumpDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor); + virtual VOID DumpConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor); + + // constructor / destructor + CUSBDevice(IUnknown *OuterUnknown){} + virtual ~CUSBDevice(){} + +protected: + LONG m_Ref; + PHUBCONTROLLER m_HubController; + PUSBHARDWAREDEVICE m_Device; + PVOID m_Parent; + ULONG m_Port; + UCHAR m_DeviceAddress; + PVOID m_Data; + UCHAR m_ConfigurationIndex; + KSPIN_LOCK m_Lock; + USB_DEVICE_DESCRIPTOR m_DeviceDescriptor; + ULONG m_PortStatus; + PUSBQUEUE m_Queue; + PDMAMEMORYMANAGER m_DmaManager; + PUSB_CONFIGURATION_DESCRIPTOR *m_ConfigurationDescriptors; + LIST_ENTRY m_IrpListHead; + +}; + +//---------------------------------------------------------------------------------------- +NTSTATUS +STDMETHODCALLTYPE +CUSBDevice::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::Initialize( + IN PHUBCONTROLLER HubController, + IN PUSBHARDWAREDEVICE Device, + IN PVOID Parent, + IN ULONG Port, + IN ULONG PortStatus) +{ + NTSTATUS Status; + + // + // initialize members + // + m_HubController = HubController; + m_Device = Device; + m_Parent = Parent; + m_Port = Port; + m_PortStatus = PortStatus; + + // + // initialize device lock + // + KeInitializeSpinLock(&m_Lock); + + // + // initialize irp list + // + InitializeListHead(&m_IrpListHead); + + // + // no device address has been set yet + // + m_DeviceAddress = 0; + + // + // get usb request queue + // + Status = m_Device->GetUSBQueue(&m_Queue); + if (!NT_SUCCESS(Status)) + { + // + // failed to get usb queue + // + DPRINT1("CUSBDevice::Initialize GetUsbQueue failed with %x\n", Status); + return Status; + } + + // + // get dma manager + // + Status = m_Device->GetDMA(&m_DmaManager); + if (!NT_SUCCESS(Status)) + { + // + // failed to get dma manager + // + DPRINT1("CUSBDevice::Initialize GetDMA failed with %x\n", Status); + return Status; + } + + // + // sanity check + // + PC_ASSERT(m_DmaManager); + + // + // get device descriptor + // + Status = CreateDeviceDescriptor(); + if (!NT_SUCCESS(Status)) + { + // + // failed to get device descriptor + // + DPRINT1("CUSBDevice::Initialize Failed to get device descriptor with %x\n", Status); + return Status; + } + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +BOOLEAN +CUSBDevice::IsHub() +{ + // + // USB Standard Device Class see http://www.usb.org/developers/defined_class/#BaseClass09h + // for details + // + return (m_DeviceDescriptor.bDeviceClass == 0x09 && m_DeviceDescriptor.bDeviceSubClass == 0x00); +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::GetParent( + PVOID * Parent) +{ + // + // returns parent + // + *Parent = m_Parent; + + // + // done + // + return STATUS_SUCCESS; +} + +//---------------------------------------------------------------------------------------- +UCHAR +CUSBDevice::GetDeviceAddress() +{ + // + // get device address + // + return m_DeviceAddress; +} + +//---------------------------------------------------------------------------------------- +ULONG +CUSBDevice::GetPort() +{ + // + // get port to which this device is connected to + // + return m_Port; +} + +//---------------------------------------------------------------------------------------- +USB_DEVICE_SPEED +CUSBDevice::GetSpeed() +{ + if (m_PortStatus & USB_PORT_STATUS_LOW_SPEED) + { + // + // low speed device + // + return UsbLowSpeed; + } + else if (m_PortStatus & USB_PORT_STATUS_HIGH_SPEED) + { + // + // high speed device + // + return UsbHighSpeed; + } + + // + // default to full speed + // + return UsbFullSpeed; +} + +//---------------------------------------------------------------------------------------- +USB_DEVICE_TYPE +CUSBDevice::GetType() +{ + // + // device is encoded into bcdUSB + // + if (m_DeviceDescriptor.bcdUSB == 0x110) + { + // + // USB 1.1 device + // + return Usb11Device; + } + else if (m_DeviceDescriptor.bcdUSB == 0x200) + { + // + // USB 2.0 device + // + return Usb20Device; + } + + DPRINT1("CUSBDevice::GetType Unknown bcdUSB Type %x\n", m_DeviceDescriptor.bcdUSB); + //PC_ASSERT(FALSE); + return Usb11Device; +} + +//---------------------------------------------------------------------------------------- +ULONG +CUSBDevice::GetState() +{ + UNIMPLEMENTED + return FALSE; +} + +//---------------------------------------------------------------------------------------- +void +CUSBDevice::SetDeviceHandleData( + PVOID Data) +{ + // + // set device data, for debugging issues + // + m_Data = Data; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::SetDeviceAddress( + UCHAR DeviceAddress) +{ + PUSB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + UCHAR OldAddress; + ULONG Index; + + DPRINT1("CUSBDevice::SetDeviceAddress Address %d\n", DeviceAddress); + + CtrlSetup = (PUSB_DEFAULT_PIPE_SETUP_PACKET)ExAllocatePoolWithTag(NonPagedPool, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET), TAG_USBOHCI); + if (!CtrlSetup) + return STATUS_INSUFFICIENT_RESOURCES; + + // + // zero request + // + RtlZeroMemory(CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + + // + // initialize request + // + CtrlSetup->bRequest = USB_REQUEST_SET_ADDRESS; + CtrlSetup->wValue.W = (USHORT)DeviceAddress; + + // + // set device address + // + Status = CommitSetupPacket(CtrlSetup, 0, 0, 0); + + // + // free setup packet + // + ExFreePoolWithTag(CtrlSetup, TAG_USBOHCI); + + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + // + // failed to set device address + // + DPRINT1("CUSBDevice::SetDeviceAddress> failed to set device address with %x Address %x\n", Status, DeviceAddress); + return Status; + } + + // + // lets have a short nap + // + KeStallExecutionProcessor(300); + + // + // back up old address + // + OldAddress = m_DeviceAddress; + + // + // store new device address + // + m_DeviceAddress = DeviceAddress; + + // + // check that setting device address succeeded by retrieving the device descriptor + // + Status = CreateDeviceDescriptor(); + if (!NT_SUCCESS(Status)) + { + // + // failed to retrieve device descriptor + // + DPRINT1("CUSBbDevice::SetDeviceAddress> failed to retrieve device descriptor with device address set Error %x\n", Status); + m_DeviceAddress = OldAddress; + + // + // return error status + // + return Status; + } + + // + // sanity checks + // + PC_ASSERT(m_DeviceDescriptor.bNumConfigurations); + + // + // allocate configuration descriptor + // + m_ConfigurationDescriptors = (PUSB_CONFIGURATION_DESCRIPTOR*) ExAllocatePoolWithTag(NonPagedPool, sizeof(PUSB_CONFIGURATION_DESCRIPTOR) * m_DeviceDescriptor.bNumConfigurations, TAG_USBOHCI); + + // + // zero configuration descriptor + // + RtlZeroMemory(m_ConfigurationDescriptors, sizeof(PUSB_CONFIGURATION_DESCRIPTOR) * m_DeviceDescriptor.bNumConfigurations); + + // + // retrieve the configuration descriptors + // + for(Index = 0; Index < m_DeviceDescriptor.bNumConfigurations; Index++) + { + // + // retrieve configuration descriptors from device + // + Status = CreateConfigurationDescriptor(Index); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CUSBDevice::SetDeviceAddress> failed to retrieve configuration %lu\n", Index); + break; + } + } + + // + // done + // + return Status; + +} + +//---------------------------------------------------------------------------------------- +void +CUSBDevice::GetDeviceDescriptor( + PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) +{ + RtlMoveMemory(DeviceDescriptor, &m_DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); +} + +//---------------------------------------------------------------------------------------- +UCHAR +CUSBDevice::GetConfigurationValue() +{ + // + // return configuration index + // + return m_ConfigurationIndex; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::CommitIrp( + PIRP Irp) +{ + NTSTATUS Status; + PUSBREQUEST Request; + + if (!m_Queue || !m_DmaManager) + { + // + // no queue, wtf? + // + DPRINT1("CUSBDevice::CommitUrb> no queue / dma !!!\n"); + return STATUS_UNSUCCESSFUL; + } + + // + // build usb request + // + Status = m_Queue->CreateUSBRequest(&Request); + if (!NT_SUCCESS(Status)) + { + // + // failed to build request + // + DPRINT1("CUSBDevice::CommitSetupPacket> CreateUSBRequest failed with %x\n", Status); + return Status; + } + + // + // initialize request + // + Status = Request->InitializeWithIrp(m_DmaManager, Irp, GetSpeed()); + + // + // mark irp as pending + // + IoMarkIrpPending(Irp); + + // + // now add the request + // + Status = m_Queue->AddUSBRequest(Request); + if (!NT_SUCCESS(Status)) + { + // + // failed to add request + // + DPRINT1("CUSBDevice::CommitSetupPacket> failed add request to queue with %x\n", Status); + Request->Release(); + return Status; + } + + // + // done + // + return STATUS_PENDING; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::SubmitIrp( + PIRP Irp) +{ + KIRQL OldLevel; + NTSTATUS Status; + + // + // acquire device lock + // + KeAcquireSpinLock(&m_Lock, &OldLevel); + + // + // commit urb + // + Status = CommitIrp(Irp); + + // + // release lock + // + KeReleaseSpinLock(&m_Lock, OldLevel); + + return Status; +} +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::CommitSetupPacket( + IN PUSB_DEFAULT_PIPE_SETUP_PACKET Packet, + IN OPTIONAL PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor, + IN ULONG BufferLength, + IN OUT PMDL Mdl) +{ + NTSTATUS Status; + PUSBREQUEST Request; + + if (!m_Queue) + { + // + // no queue, wtf? + // + DPRINT1("CUSBDevice::CommitSetupPacket> no queue!!!\n"); + return STATUS_UNSUCCESSFUL; + } + + // + // build usb request + // + Status = m_Queue->CreateUSBRequest(&Request); + if (!NT_SUCCESS(Status)) + { + // + // failed to build request + // + DPRINT1("CUSBDevice::CommitSetupPacket> CreateUSBRequest failed with %x\n", Status); + return Status; + } + + // + // initialize request + // + Status = Request->InitializeWithSetupPacket(m_DmaManager, Packet, m_DeviceAddress, EndpointDescriptor, GetSpeed(), BufferLength, Mdl); + if (!NT_SUCCESS(Status)) + { + // + // failed to initialize request + // + DPRINT1("CUSBDevice::CommitSetupPacket> failed to initialize usb request with %x\n", Status); + Request->Release(); + return Status; + } + + // + // now add the request + // + Status = m_Queue->AddUSBRequest(Request); + if (!NT_SUCCESS(Status)) + { + // + // failed to add request + // + DPRINT1("CUSBDevice::CommitSetupPacket> failed add request to queue with %x\n", Status); + Request->Release(); + return Status; + } + + // + // get the result code when the operation has been finished + // + Request->GetResultStatus(&Status, NULL); + + // + // release request + // + Request->Release(); + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::CreateDeviceDescriptor() +{ + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + PMDL Mdl; + NTSTATUS Status; + PVOID DeviceDescriptor; + + // + // allocate descriptor page aligned + // + DeviceDescriptor = ExAllocatePool(NonPagedPool, PAGE_SIZE); + if (!DeviceDescriptor) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // zero descriptor + // + RtlZeroMemory(DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); + RtlZeroMemory(&CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + + // + // setup request + // + CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; + CtrlSetup.wValue.HiByte = USB_DEVICE_DESCRIPTOR_TYPE; + CtrlSetup.wLength = sizeof(USB_DEVICE_DESCRIPTOR); + CtrlSetup.bmRequestType.B = 0x80; + + // + // allocate mdl describing the device descriptor + // + Mdl = IoAllocateMdl(DeviceDescriptor, PAGE_SIZE, FALSE, FALSE, 0); + if (!Mdl) + { + // + // failed to allocate mdl + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build mdl for non paged pool + // + MmBuildMdlForNonPagedPool(Mdl); + + // + // commit setup packet + // + Status = CommitSetupPacket(&CtrlSetup, 0, sizeof(USB_DEVICE_DESCRIPTOR), Mdl); + + // + // now free the mdl + // + IoFreeMdl(Mdl); + + if (NT_SUCCESS(Status)) + { + // + // copy back device descriptor + // + RtlCopyMemory(&m_DeviceDescriptor, DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR)); + + // + // informal dbg print + // + DumpDeviceDescriptor(&m_DeviceDescriptor); + } + + // + // free item + // + ExFreePool(DeviceDescriptor); + + // + // done + // + return Status; + +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::CreateConfigurationDescriptor( + ULONG Index) +{ + PVOID Buffer; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PMDL Mdl; + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; + + + // + // sanity checks + // + PC_ASSERT(m_ConfigurationDescriptors); + + // + // first allocate a buffer which should be enough to store all different interfaces and endpoints + // + Buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, TAG_USBOHCI); + if (!Buffer) + { + // + // failed to allocate buffer + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // zero buffer + // + RtlZeroMemory(Buffer, PAGE_SIZE); + + // + // build setup packet + // + CtrlSetup.bmRequestType._BM.Recipient = BMREQUEST_TO_DEVICE; + CtrlSetup.bmRequestType._BM.Type = BMREQUEST_STANDARD; + CtrlSetup.bmRequestType._BM.Reserved = 0; + CtrlSetup.bmRequestType._BM.Dir = BMREQUEST_DEVICE_TO_HOST; + CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR; + CtrlSetup.wValue.LowByte = 0; + CtrlSetup.wValue.HiByte = USB_CONFIGURATION_DESCRIPTOR_TYPE; + CtrlSetup.wIndex.W = 0; + CtrlSetup.wLength = PAGE_SIZE; + + // + // FIXME: where put configuration index? + // + + // + // now build MDL describing the buffer + // + Mdl = IoAllocateMdl(Buffer, PAGE_SIZE, FALSE, FALSE, 0); + if (!Mdl) + { + // + // failed to allocate mdl + // + ExFreePoolWithTag(Buffer, TAG_USBOHCI); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build mdl for non paged pool + // + MmBuildMdlForNonPagedPool(Mdl); + + // + // commit packet + // + Status = CommitSetupPacket(&CtrlSetup, 0, PAGE_SIZE, Mdl); + if (!NT_SUCCESS(Status)) + { + // + // failed to issue request, cleanup + // + IoFreeMdl(Mdl); + ExFreePool(Buffer); + return Status; + } + + // + // now free the mdl + // + IoFreeMdl(Mdl); + + // + // get configuration descriptor + // + ConfigurationDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)Buffer; + + // + // informal debug print + // + DumpConfigurationDescriptor(ConfigurationDescriptor); + + // + // sanity check + // + PC_ASSERT(ConfigurationDescriptor->bLength == sizeof(USB_CONFIGURATION_DESCRIPTOR)); + PC_ASSERT(ConfigurationDescriptor->wTotalLength <= PAGE_SIZE); + PC_ASSERT(ConfigurationDescriptor->bNumInterfaces); + + // + // store configuration descriptor + // + m_ConfigurationDescriptors[Index] = ConfigurationDescriptor; + + // + // done + // + return Status; +} +//---------------------------------------------------------------------------------------- +VOID +CUSBDevice::GetConfigurationDescriptors( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptorBuffer, + IN ULONG BufferLength, + OUT PULONG OutBufferLength) +{ + // + // sanity check + // + PC_ASSERT(BufferLength >= sizeof(USB_CONFIGURATION_DESCRIPTOR)); + PC_ASSERT(ConfigDescriptorBuffer); + PC_ASSERT(OutBufferLength); + + // + // reset copied length + // + *OutBufferLength = 0; + + // + // FIXME: support multiple configurations + // + PC_ASSERT(m_DeviceDescriptor.bNumConfigurations == 1); + + // + // copy configuration descriptor + // + RtlCopyMemory(ConfigDescriptorBuffer, m_ConfigurationDescriptors[0], min(m_ConfigurationDescriptors[0]->wTotalLength, BufferLength)); + *OutBufferLength = m_ConfigurationDescriptors[0]->wTotalLength; +} + +//---------------------------------------------------------------------------------------- +ULONG +CUSBDevice::GetConfigurationDescriptorsLength() +{ + // + // FIXME: support multiple configurations + // + PC_ASSERT(m_DeviceDescriptor.bNumConfigurations == 1); + + ASSERT(m_ConfigurationDescriptors[0]); + ASSERT(m_ConfigurationDescriptors[0]->wTotalLength); + + return m_ConfigurationDescriptors[0]->wTotalLength; +} +//---------------------------------------------------------------------------------------- +VOID +CUSBDevice::DumpDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) +{ + DPRINT1("Dumping Device Descriptor %x\n", DeviceDescriptor); + DPRINT1("bLength %x\n", DeviceDescriptor->bLength); + DPRINT1("bDescriptorType %x\n", DeviceDescriptor->bDescriptorType); + DPRINT1("bcdUSB %x\n", DeviceDescriptor->bcdUSB); + DPRINT1("bDeviceClass %x\n", DeviceDescriptor->bDeviceClass); + DPRINT1("bDeviceSubClass %x\n", DeviceDescriptor->bDeviceSubClass); + DPRINT1("bDeviceProtocol %x\n", DeviceDescriptor->bDeviceProtocol); + DPRINT1("bMaxPacketSize0 %x\n", DeviceDescriptor->bMaxPacketSize0); + DPRINT1("idVendor %x\n", DeviceDescriptor->idVendor); + DPRINT1("idProduct %x\n", DeviceDescriptor->idProduct); + DPRINT1("bcdDevice %x\n", DeviceDescriptor->bcdDevice); + DPRINT1("iManufacturer %x\n", DeviceDescriptor->iManufacturer); + DPRINT1("iProduct %x\n", DeviceDescriptor->iProduct); + DPRINT1("iSerialNumber %x\n", DeviceDescriptor->iSerialNumber); + DPRINT1("bNumConfigurations %x\n", DeviceDescriptor->bNumConfigurations); +} + +//---------------------------------------------------------------------------------------- +VOID +CUSBDevice::DumpConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) +{ + DPRINT1("Dumping ConfigurationDescriptor %x\n", ConfigurationDescriptor); + DPRINT1("bLength %x\n", ConfigurationDescriptor->bLength); + DPRINT1("bDescriptorType %x\n", ConfigurationDescriptor->bDescriptorType); + DPRINT1("wTotalLength %x\n", ConfigurationDescriptor->wTotalLength); + DPRINT1("bNumInterfaces %x\n", ConfigurationDescriptor->bNumInterfaces); + DPRINT1("bConfigurationValue %x\n", ConfigurationDescriptor->bConfigurationValue); + DPRINT1("iConfiguration %x\n", ConfigurationDescriptor->iConfiguration); + DPRINT1("bmAttributes %x\n", ConfigurationDescriptor->bmAttributes); + DPRINT1("MaxPower %x\n", ConfigurationDescriptor->MaxPower); +} +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::SubmitSetupPacket( + IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, + IN OUT ULONG BufferLength, + OUT PVOID Buffer) +{ + NTSTATUS Status; + PMDL Mdl = NULL; + + if (BufferLength) + { + // + // allocate mdl + // + Mdl = IoAllocateMdl(Buffer, BufferLength, FALSE, FALSE, 0); + if (!Mdl) + return STATUS_INSUFFICIENT_RESOURCES; + + // + // HACK HACK HACK: assume the buffer is build from non paged pool + // + MmBuildMdlForNonPagedPool(Mdl); + } + + // + // commit setup packet + // + Status = CommitSetupPacket(SetupPacket, 0, BufferLength, Mdl); + + if (Mdl != NULL) + { + // + // free mdl + // + IoFreeMdl(Mdl); + } + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::SelectConfiguration( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + IN PUSBD_INTERFACE_INFORMATION InterfaceInfo, + OUT USBD_CONFIGURATION_HANDLE *ConfigurationHandle) +{ + ULONG InterfaceIndex, PipeIndex; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSB_CONFIGURATION_DESCRIPTOR CurrentConfigurationDescriptor; + PUSB_INTERFACE_DESCRIPTOR CurrentInterfaceDescriptor; + PUSB_ENDPOINT_DESCRIPTOR CurrentEndpointDescriptor; + PVOID StartPosition; + + // + // FIXME: support multiple configurations + // + ASSERT(m_DeviceDescriptor.bNumConfigurations == 1); + ASSERT(m_ConfigurationDescriptors[0]); + CurrentConfigurationDescriptor = m_ConfigurationDescriptors[0]; + + // + // sanity check + // + PC_ASSERT(ConfigurationDescriptor->iConfiguration == CurrentConfigurationDescriptor->iConfiguration); + PC_ASSERT(ConfigurationDescriptor->bNumInterfaces <= CurrentConfigurationDescriptor->bNumInterfaces); + DPRINT1("CUSBDevice::SelectConfiguration NumInterfaces %lu\n", ConfigurationDescriptor->bNumInterfaces); + + + // + // copy interface info and pipe info + // + for(InterfaceIndex = 0; InterfaceIndex < ConfigurationDescriptor->bNumInterfaces; InterfaceIndex++) + { + // + // find interface descriptor + // + CurrentInterfaceDescriptor = USBD_ParseConfigurationDescriptor(CurrentConfigurationDescriptor, InterfaceInfo->InterfaceNumber, InterfaceInfo->AlternateSetting); + + // + // sanity check + // + ASSERT(CurrentInterfaceDescriptor); + ASSERT(CurrentInterfaceDescriptor->bLength != 0); + ASSERT(InterfaceInfo->NumberOfPipes == CurrentInterfaceDescriptor->bNumEndpoints); + ASSERT(InterfaceInfo->Length != 0); +#ifdef _MSC_VER + PC_ASSERT(InterfaceInfo->Length == FIELD_OFFSET(USBD_INTERFACE_INFORMATION, Pipes[InterfaceInfo->NumberOfPipes])); +#endif + + DPRINT1("CUSBDevice::SelectConfiguration InterfaceNumber %lu AlternativeSetting %lu bNumEndpoints %lu\n", InterfaceInfo->InterfaceNumber, InterfaceInfo->AlternateSetting, CurrentInterfaceDescriptor->bNumEndpoints); + + // + // copy interface info + // + InterfaceInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)CurrentInterfaceDescriptor; + InterfaceInfo->Class = CurrentInterfaceDescriptor->bInterfaceClass; + InterfaceInfo->SubClass = CurrentInterfaceDescriptor->bInterfaceSubClass; + InterfaceInfo->Protocol = CurrentInterfaceDescriptor->bInterfaceProtocol; + InterfaceInfo->Reserved = 0; + + // + // copy endpoint info + // + StartPosition = CurrentInterfaceDescriptor; + for(PipeIndex = 0; PipeIndex < InterfaceInfo->NumberOfPipes; PipeIndex++) + { + // + // find corresponding endpoint descriptor + // + CurrentEndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)USBD_ParseDescriptors(CurrentConfigurationDescriptor, CurrentConfigurationDescriptor->wTotalLength, StartPosition, USB_ENDPOINT_DESCRIPTOR_TYPE); + + // + // sanity checks + // + ASSERT(CurrentEndpointDescriptor); + ASSERT(CurrentEndpointDescriptor->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE); + + // + // copy pipe info + // + InterfaceInfo->Pipes[PipeIndex].MaximumPacketSize = CurrentEndpointDescriptor->wMaxPacketSize; + InterfaceInfo->Pipes[PipeIndex].EndpointAddress = CurrentEndpointDescriptor->bEndpointAddress; + InterfaceInfo->Pipes[PipeIndex].Interval = CurrentEndpointDescriptor->bInterval; + InterfaceInfo->Pipes[PipeIndex].PipeType = (USBD_PIPE_TYPE)CurrentEndpointDescriptor->bmAttributes; + InterfaceInfo->Pipes[PipeIndex].PipeHandle = (PVOID)CurrentEndpointDescriptor; + + // + // move start position beyond the current endpoint descriptor + // + StartPosition = (PVOID)(CurrentEndpointDescriptor + 1); + } + + // + // move offset + // + InterfaceInfo = (PUSBD_INTERFACE_INFORMATION)((ULONG_PTR)PtrToUlong(InterfaceInfo) + InterfaceInfo->Length); + } + + // + // now build setup packet + // + RtlZeroMemory(&CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + CtrlSetup.bRequest = USB_REQUEST_SET_CONFIGURATION; + CtrlSetup.wValue.W = ConfigurationDescriptor->bConfigurationValue; + + // + // select configuration + // + Status = CommitSetupPacket(&CtrlSetup, 0, 0, 0); + + // + // informal debug print + // + DPRINT1("CUsbDevice::SelectConfiguration New Configuration %x Old Configuration %x Result %x\n", ConfigurationDescriptor->iConfiguration, m_ConfigurationIndex, Status); + + if (NT_SUCCESS(Status)) + { + // + // store configuration device index + // + m_ConfigurationIndex = ConfigurationDescriptor->iConfiguration; + + // + // store configuration handle + // + *ConfigurationHandle = m_ConfigurationDescriptors[0]; + } + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBDevice::SelectInterface( + IN USBD_CONFIGURATION_HANDLE ConfigurationHandle, + IN OUT PUSBD_INTERFACE_INFORMATION InterfaceInfo) +{ + PUSB_CONFIGURATION_DESCRIPTOR Configuration; + ULONG PipeIndex; + USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup; + NTSTATUS Status; + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + PUSB_ENDPOINT_DESCRIPTOR CurrentEndpointDescriptor; + PVOID StartPosition; + + // + // FIXME support multiple configurations + // + PC_ASSERT(m_ConfigurationDescriptors[0] == (PUSB_CONFIGURATION_DESCRIPTOR)ConfigurationHandle); + + // + // get configuration struct + // + Configuration = (PUSB_CONFIGURATION_DESCRIPTOR)ConfigurationHandle; + + // + // sanity checks + // + PC_ASSERT(Configuration->bNumInterfaces > InterfaceInfo->InterfaceNumber); +#ifdef _MSC_VER + //PC_ASSERT(InterfaceInfo->Length == FIELD_OFFSET(USBD_INTERFACE_INFORMATION, Pipes[InterfaceInfo->NumberOfPipes])); +#endif + + // + // FIXME: check bandwidth + // + + // + // find interface number + // + InterfaceDescriptor = USBD_ParseConfigurationDescriptor(Configuration, InterfaceInfo->InterfaceNumber, InterfaceInfo->AlternateSetting); + ASSERT(InterfaceDescriptor); + + // + // initialize setup packet + // + RtlZeroMemory(&CtrlSetup, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + CtrlSetup.bRequest = USB_REQUEST_SET_INTERFACE; + CtrlSetup.wValue.W = InterfaceDescriptor->bAlternateSetting; + CtrlSetup.wIndex.W = InterfaceDescriptor->bInterfaceNumber; + CtrlSetup.bmRequestType.B = 0x01; + + // + // issue request + // + Status = CommitSetupPacket(&CtrlSetup, 0, 0, 0); + + // + // informal debug print + // + DPRINT1("CUSBDevice::SelectInterface AlternateSetting %x InterfaceNumber %x Status %x\n", InterfaceInfo->AlternateSetting, InterfaceInfo->InterfaceNumber, Status); + DPRINT1("CUSBDevice::SelectInterface bInterfaceNumber %u bAlternateSetting %u NumberOfPipes %u Length %lu\n", + InterfaceDescriptor->bInterfaceNumber, InterfaceDescriptor->bAlternateSetting, InterfaceInfo->NumberOfPipes, InterfaceInfo->Length); + InterfaceInfo->InterfaceHandle = InterfaceDescriptor; + InterfaceInfo->NumberOfPipes = InterfaceDescriptor->bNumEndpoints; + + // + // are there end points + // + if (InterfaceDescriptor->bNumEndpoints) + { + // + // sanity check + // + ASSERT(InterfaceInfo->Length == sizeof(USBD_INTERFACE_INFORMATION) + (InterfaceDescriptor->bNumEndpoints > 1 ? sizeof(USBD_PIPE_INFORMATION) * (InterfaceDescriptor->bNumEndpoints - 1) : 0)); + + // + // store number of pipes + // + InterfaceInfo->NumberOfPipes = InterfaceDescriptor->bNumEndpoints; + + StartPosition = InterfaceDescriptor; + for(PipeIndex = 0; PipeIndex < InterfaceInfo->NumberOfPipes; PipeIndex++) + { + // + // find corresponding endpoint descriptor + // + CurrentEndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)USBD_ParseDescriptors(Configuration, Configuration->wTotalLength, StartPosition, USB_ENDPOINT_DESCRIPTOR_TYPE); + + // + // sanity checks + // + ASSERT(CurrentEndpointDescriptor); + ASSERT(CurrentEndpointDescriptor->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE); + + // + // copy pipe info + // + InterfaceInfo->Pipes[PipeIndex].MaximumPacketSize = CurrentEndpointDescriptor->wMaxPacketSize; + InterfaceInfo->Pipes[PipeIndex].EndpointAddress = CurrentEndpointDescriptor->bEndpointAddress; + InterfaceInfo->Pipes[PipeIndex].Interval = CurrentEndpointDescriptor->bInterval; + InterfaceInfo->Pipes[PipeIndex].PipeType = (USBD_PIPE_TYPE)CurrentEndpointDescriptor->bmAttributes; + InterfaceInfo->Pipes[PipeIndex].PipeHandle = (PVOID)CurrentEndpointDescriptor; + + // + // move start position beyond the current endpoint descriptor + // + StartPosition = (PVOID)(CurrentEndpointDescriptor + 1); + } + } + // + // done + // + return Status; +} + +NTSTATUS +CUSBDevice::AbortPipe( + IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor) +{ + // + // let it handle usb queue + // + ASSERT(m_Queue); + ASSERT(m_DeviceAddress); + + // + // done + // + return m_Queue->AbortDevicePipe(m_DeviceAddress, EndpointDescriptor); +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CreateUSBDevice( + PUSBDEVICE *OutDevice) +{ + CUSBDevice * This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBOHCI) CUSBDevice(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutDevice = (PUSBDEVICE)This; + + // + // done + // + return STATUS_SUCCESS; +} + diff --git a/reactos/drivers/usb/usbohci/usb_queue.cpp b/reactos/drivers/usb/usbohci/usb_queue.cpp new file mode 100644 index 00000000000..2132bec89ca --- /dev/null +++ b/reactos/drivers/usb/usbohci/usb_queue.cpp @@ -0,0 +1,894 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbohci/usb_queue.cpp + * PURPOSE: USB OHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbohci.h" +#include "hardware.h" + +class CUSBQueue : public IUSBQueue +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + // com + virtual NTSTATUS Initialize(IN PUSBHARDWAREDEVICE Hardware, PDMA_ADAPTER AdapterObject, IN PDMAMEMORYMANAGER MemManager, IN OPTIONAL PKSPIN_LOCK Lock); + virtual ULONG GetPendingRequestCount(); + virtual NTSTATUS AddUSBRequest(IUSBRequest * Request); + virtual NTSTATUS CancelRequests(); + virtual NTSTATUS CreateUSBRequest(IUSBRequest **OutRequest); + virtual VOID TransferDescriptorCompletionCallback(ULONG TransferDescriptorLogicalAddress); + virtual NTSTATUS AbortDevicePipe(UCHAR DeviceAddress, IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor); + + // local functions + BOOLEAN IsTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG TransferDescriptorLogicalAddress); + BOOLEAN IsTransferDescriptorInIsoEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG TransferDescriptorLogicalAddress); + NTSTATUS FindTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor); + NTSTATUS FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor); + NTSTATUS FindTransferDescriptorInIsochronousHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor); + + VOID CleanupEndpointDescriptor(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor); + POHCI_ENDPOINT_DESCRIPTOR FindInterruptEndpointDescriptor(UCHAR InterruptInterval); + VOID PrintEndpointList(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor); + VOID LinkEndpoint(POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor, POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor); + + // constructor / destructor + CUSBQueue(IUnknown *OuterUnknown){} + virtual ~CUSBQueue(){} + +protected: + LONG m_Ref; // reference count + KSPIN_LOCK m_Lock; // list lock + PUSBHARDWAREDEVICE m_Hardware; // hardware + POHCI_ENDPOINT_DESCRIPTOR m_BulkHeadEndpointDescriptor; // bulk head descriptor + POHCI_ENDPOINT_DESCRIPTOR m_ControlHeadEndpointDescriptor; // control head descriptor + POHCI_ENDPOINT_DESCRIPTOR m_IsoHeadEndpointDescriptor; // isochronous head descriptor + POHCI_ENDPOINT_DESCRIPTOR * m_InterruptEndpoints; +}; + +//================================================================================================= +// COM +// +NTSTATUS +STDMETHODCALLTYPE +CUSBQueue::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + if (IsEqualGUIDAligned(refiid, IID_IUnknown)) + { + *Output = PVOID(PUNKNOWN(this)); + PUNKNOWN(*Output)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS +CUSBQueue::Initialize( + IN PUSBHARDWAREDEVICE Hardware, + IN PDMA_ADAPTER AdapterObject, + IN PDMAMEMORYMANAGER MemManager, + IN OPTIONAL PKSPIN_LOCK Lock) +{ + // + // get bulk endpoint descriptor + // + Hardware->GetBulkHeadEndpointDescriptor(&m_BulkHeadEndpointDescriptor); + + // + // get control endpoint descriptor + // + Hardware->GetControlHeadEndpointDescriptor(&m_ControlHeadEndpointDescriptor); + + // + // get isochronous endpoint + // + Hardware->GetIsochronousHeadEndpointDescriptor(&m_IsoHeadEndpointDescriptor); + + // + // get interrupt endpoints + // + Hardware->GetInterruptEndpointDescriptors(&m_InterruptEndpoints); + + // + // initialize spinlock + // + KeInitializeSpinLock(&m_Lock); + + // + // store hardware + // + m_Hardware = Hardware; + + return STATUS_SUCCESS; +} + +ULONG +CUSBQueue::GetPendingRequestCount() +{ + // + // Loop through the pending list and iterrate one for each QueueHead that + // has a IRP to complete. + // + + return 0; +} + +VOID +CUSBQueue::LinkEndpoint( + POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor, + POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor) +{ + POHCI_ENDPOINT_DESCRIPTOR CurrentEndpointDescriptor = HeadEndpointDescriptor; + + // + // get last descriptor in queue + // + while(CurrentEndpointDescriptor->NextDescriptor) + { + // + // move to last descriptor + // + CurrentEndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)CurrentEndpointDescriptor->NextDescriptor; + } + + // + // link endpoints + // + CurrentEndpointDescriptor->NextPhysicalEndpoint = EndpointDescriptor->PhysicalAddress.LowPart; + CurrentEndpointDescriptor->NextDescriptor = EndpointDescriptor; + +} + +NTSTATUS +CUSBQueue::AddUSBRequest( + IUSBRequest * Request) +{ + NTSTATUS Status; + ULONG Type; + POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor; + POHCI_ENDPOINT_DESCRIPTOR Descriptor; + POHCI_ISO_TD CurrentDescriptor; + ULONG FrameNumber; + USHORT Frame; + + DPRINT("CUSBQueue::AddUSBRequest\n"); + + // + // sanity check + // + ASSERT(Request != NULL); + + // + // get request type + // + Type = Request->GetTransferType(); + + // + // add extra reference which is released when the request is completed + // + Request->AddRef(); + + // + // get transfer descriptors + // + Status = Request->GetEndpointDescriptor(&Descriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to get transfer descriptor + // + DPRINT1("CUSBQueue::AddUSBRequest GetEndpointDescriptor failed with %x\n", Status); + + // + // release reference + // + Request->Release(); + return Status; + } + + // + // check type + // + if (Type == USB_ENDPOINT_TYPE_BULK) + { + // + // get head descriptor + // + HeadDescriptor = m_BulkHeadEndpointDescriptor; + } + else if (Type == USB_ENDPOINT_TYPE_CONTROL) + { + // + // get head descriptor + // + HeadDescriptor = m_ControlHeadEndpointDescriptor; + } + else if (Type == USB_ENDPOINT_TYPE_INTERRUPT) + { + // + // get head descriptor + // + HeadDescriptor = FindInterruptEndpointDescriptor(Request->GetInterval()); + ASSERT(HeadDescriptor); + } + else if (Type == USB_ENDPOINT_TYPE_ISOCHRONOUS) + { + // + // get head descriptor + // + HeadDescriptor = m_IsoHeadEndpointDescriptor; + + // + // get current frame number + // + m_Hardware->GetCurrentFrameNumber(&FrameNumber); + + // + // FIXME: increment frame number + // + FrameNumber += 300; + + // + // apply frame number to iso transfer descriptors + // + CurrentDescriptor = (POHCI_ISO_TD)Descriptor->HeadLogicalDescriptor; + + DPRINT("ISO: NextFrameNumber %x\n", FrameNumber); + Frame = (FrameNumber & 0xFFFF); + + while(CurrentDescriptor) + { + // + // set current frame number + // + CurrentDescriptor->Flags |= OHCI_ITD_SET_STARTING_FRAME(Frame); + + // + // move to next frame number + // + Frame += OHCI_ITD_GET_FRAME_COUNT(CurrentDescriptor->Flags); + + // + // move to next descriptor + // + CurrentDescriptor = CurrentDescriptor->NextLogicalDescriptor; + } + + // + // get current frame number + // + m_Hardware->GetCurrentFrameNumber(&FrameNumber); + + DPRINT("Hardware 1ms %p Iso %p\n",m_InterruptEndpoints[0], m_IsoHeadEndpointDescriptor); + ASSERT(m_InterruptEndpoints[0]->NextPhysicalEndpoint == m_IsoHeadEndpointDescriptor->PhysicalAddress.LowPart); + + PrintEndpointList(m_IsoHeadEndpointDescriptor); + } + else + { + // + // bad request type + // + Request->Release(); + return STATUS_INVALID_PARAMETER; + } + + // + // set descriptor active + // + Descriptor->Flags &= ~OHCI_ENDPOINT_SKIP; + + // + // insert endpoint at end + // + LinkEndpoint(HeadDescriptor, Descriptor); + + if (Type == USB_ENDPOINT_TYPE_CONTROL || Type == USB_ENDPOINT_TYPE_BULK) + { + // + // notify hardware of our request + // + m_Hardware->HeadEndpointDescriptorModified(Type); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBQueue::CancelRequests() +{ + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +CUSBQueue::CreateUSBRequest( + IUSBRequest **OutRequest) +{ + PUSBREQUEST UsbRequest; + NTSTATUS Status; + + *OutRequest = NULL; + Status = InternalCreateUSBRequest(&UsbRequest); + + if (NT_SUCCESS(Status)) + { + *OutRequest = UsbRequest; + } + + return Status; +} + +NTSTATUS +CUSBQueue::FindTransferDescriptorInEndpoint( + IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, + IN ULONG TransferDescriptorLogicalAddress, + OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, + OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor) +{ + POHCI_ENDPOINT_DESCRIPTOR LastDescriptor = EndpointDescriptor; + + + // + // skip first endpoint head + // + EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor; + + while(EndpointDescriptor) + { + // + // check if the transfer descriptor is inside the list + // + if ((EndpointDescriptor->HeadPhysicalDescriptor & OHCI_ENDPOINT_HEAD_MASK) == EndpointDescriptor->TailPhysicalDescriptor || (EndpointDescriptor->HeadPhysicalDescriptor & OHCI_ENDPOINT_HALTED)) + { + // + // found endpoint + // + *OutEndpointDescriptor = EndpointDescriptor; + *OutPreviousEndpointDescriptor = LastDescriptor; + + // + // done + // + return STATUS_SUCCESS; + } + + // + // store last endpoint + // + LastDescriptor = EndpointDescriptor; + + // + // move to next + // + EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor; + } + + // + // failed to endpoint + // + return STATUS_NOT_FOUND; +} + +NTSTATUS +CUSBQueue::FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor) +{ + ULONG Index; + NTSTATUS Status; + + // + // search descriptor in endpoint list + // + for(Index = 0; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++) + { + // + // is it in current endpoint + // + Status = FindTransferDescriptorInEndpoint(m_InterruptEndpoints[Index], TransferDescriptorLogicalAddress, OutEndpointDescriptor, OutPreviousEndpointDescriptor); + if (NT_SUCCESS(Status)) + { + // + // found transfer descriptor + // + return STATUS_SUCCESS; + } + } + + // + // not found + // + return STATUS_NOT_FOUND; +} + +NTSTATUS +CUSBQueue::FindTransferDescriptorInIsochronousHeadEndpoints( + IN ULONG TransferDescriptorLogicalAddress, + OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, + OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor) +{ + POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor; + POHCI_ENDPOINT_DESCRIPTOR LastDescriptor = m_IsoHeadEndpointDescriptor; + + + // + // skip first endpoint head + // + EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)m_IsoHeadEndpointDescriptor->NextDescriptor; + + while(EndpointDescriptor) + { + // + // check if the transfer descriptor is inside the list + // + if (IsTransferDescriptorInIsoEndpoint(EndpointDescriptor, TransferDescriptorLogicalAddress)) + { + // + // found endpoint + // + *OutEndpointDescriptor = EndpointDescriptor; + *OutPreviousEndpointDescriptor = LastDescriptor; + + // + // done + // + return STATUS_SUCCESS; + } + + // + // store last endpoint + // + LastDescriptor = EndpointDescriptor; + + // + // move to next + // + EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor; + } + + // + // failed to endpoint + // + return STATUS_NOT_FOUND; +} + +BOOLEAN +CUSBQueue::IsTransferDescriptorInIsoEndpoint( + IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, + IN ULONG TransferDescriptorLogicalAddress) +{ + POHCI_ISO_TD Descriptor; + + // + // get first general transfer descriptor + // + Descriptor = (POHCI_ISO_TD)EndpointDescriptor->HeadLogicalDescriptor; + + // + // sanity check + // + ASSERT(Descriptor); + + do + { + if (Descriptor->PhysicalAddress.LowPart == TransferDescriptorLogicalAddress) + { + // + // found descriptor + // + return TRUE; + } + + // + // move to next + // + Descriptor = (POHCI_ISO_TD)Descriptor->NextLogicalDescriptor; + }while(Descriptor); + + // + // no descriptor found + // + return FALSE; +} + + +BOOLEAN +CUSBQueue::IsTransferDescriptorInEndpoint( + IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, + IN ULONG TransferDescriptorLogicalAddress) +{ + POHCI_GENERAL_TD Descriptor; + + // + // get first general transfer descriptor + // + Descriptor = (POHCI_GENERAL_TD)EndpointDescriptor->HeadLogicalDescriptor; + + // + // sanity check + // + ASSERT(Descriptor); + + do + { + if (Descriptor->PhysicalAddress.LowPart == TransferDescriptorLogicalAddress) + { + // + // found descriptor + // + return TRUE; + } + + // + // move to next + // + Descriptor = (POHCI_GENERAL_TD)Descriptor->NextLogicalDescriptor; + }while(Descriptor); + + + // + // no descriptor found + // + return FALSE; +} + +VOID +CUSBQueue::CleanupEndpointDescriptor( + POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, + POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor) +{ + PUSBREQUEST Request; + + // + // FIXME: verify unlinking process + // + PreviousEndpointDescriptor->NextDescriptor = EndpointDescriptor->NextDescriptor; + PreviousEndpointDescriptor->NextPhysicalEndpoint = EndpointDescriptor->NextPhysicalEndpoint; + + // + // get corresponding request + // + Request = PUSBREQUEST(EndpointDescriptor->Request); + ASSERT(Request); + + // + // notify of completion + // + Request->CompletionCallback(EndpointDescriptor); + + // + // free endpoint descriptor + // + Request->FreeEndpointDescriptor(EndpointDescriptor); + + // + // release request + // + Request->Release(); + +} +VOID +CUSBQueue::PrintEndpointList( + POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor) +{ + DPRINT1("CUSBQueue::PrintEndpointList HeadEndpoint %p Logical %x\n", EndpointDescriptor, EndpointDescriptor->PhysicalAddress.LowPart); + + // + // get first general transfer descriptor + // + EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor; + + while(EndpointDescriptor) + { + DPRINT1(" CUSBQueue::PrintEndpointList Endpoint %p Logical %x\n", EndpointDescriptor, EndpointDescriptor->PhysicalAddress.LowPart); + + // + // move to next + // + EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor; + } +} + +VOID +CUSBQueue::TransferDescriptorCompletionCallback( + ULONG TransferDescriptorLogicalAddress) +{ + POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, PreviousEndpointDescriptor; + NTSTATUS Status; + + DPRINT("CUSBQueue::TransferDescriptorCompletionCallback transfer descriptor %x\n", TransferDescriptorLogicalAddress); + + do + { + // + // find transfer descriptor in control list + // + Status = FindTransferDescriptorInEndpoint(m_ControlHeadEndpointDescriptor, TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor); + if (NT_SUCCESS(Status)) + { + // + // cleanup endpoint + // + CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor); + + // + // done + // + continue; + } + + // + // find transfer descriptor in bulk list + // + Status = FindTransferDescriptorInEndpoint(m_BulkHeadEndpointDescriptor, TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor); + if (NT_SUCCESS(Status)) + { + // + // cleanup endpoint + // + CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor); + + // + // done + // + continue; + } + + // + // find transfer descriptor in interrupt list + // + Status = FindTransferDescriptorInInterruptHeadEndpoints(TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor); + if (NT_SUCCESS(Status)) + { + // + // cleanup endpoint + // + CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor); + + // + // done + // + continue; + } + + // + // last try: find the descriptor in isochronous list + // + Status = FindTransferDescriptorInIsochronousHeadEndpoints(TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor); + if (NT_SUCCESS(Status)) + { + // + // cleanup endpoint + // + DPRINT("ISO endpoint complete\n"); + //ASSERT(FALSE); + CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor); + + // + // done + // + continue; + } + + // + // no more completed descriptors found + // + return; + + }while(TRUE); + + // + // hardware reported dead endpoint completed + // + DPRINT1("CUSBQueue::TransferDescriptorCompletionCallback invalid transfer descriptor %x\n", TransferDescriptorLogicalAddress); + ASSERT(FALSE); +} + +POHCI_ENDPOINT_DESCRIPTOR +CUSBQueue::FindInterruptEndpointDescriptor( + UCHAR InterruptInterval) +{ + ULONG Index = 0; + ULONG Power = 1; + + // + // sanity check + // + ASSERT(InterruptInterval <= OHCI_BIGGEST_INTERVAL); + + // + // find interrupt index + // + while (Power <= OHCI_BIGGEST_INTERVAL / 2) + { + // + // is current interval greater + // + if (Power * 2 > InterruptInterval) + break; + + // + // increment power + // + Power *= 2; + + // + // move to next interrupt + // + Index++; + } + + DPRINT("InterruptInterval %lu Selected InterruptIndex %lu Choosen Interval %lu\n", InterruptInterval, Index, Power); + + // + // return endpoint + // + return m_InterruptEndpoints[Index]; +} + +NTSTATUS +CUSBQueue::AbortDevicePipe( + IN UCHAR DeviceAddress, + IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor) +{ + POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor, CurrentDescriptor, PreviousDescriptor, TempDescriptor; + ULONG Type; + POHCI_GENERAL_TD TransferDescriptor; + + // + // get type + // + Type = (EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK); + + // + // check type + // + if (Type == USB_ENDPOINT_TYPE_BULK) + { + // + // get head descriptor + // + HeadDescriptor = m_BulkHeadEndpointDescriptor; + } + else if (Type == USB_ENDPOINT_TYPE_CONTROL) + { + // + // get head descriptor + // + HeadDescriptor = m_ControlHeadEndpointDescriptor; + } + else if (Type == USB_ENDPOINT_TYPE_INTERRUPT) + { + // + // get head descriptor + // + HeadDescriptor = FindInterruptEndpointDescriptor(EndpointDescriptor->bInterval); + ASSERT(HeadDescriptor); + } + else if (Type == USB_ENDPOINT_TYPE_ISOCHRONOUS) + { + UNIMPLEMENTED + return STATUS_NOT_IMPLEMENTED; + } + + // + // FIXME should disable list processing + // + + // + // now remove all endpoints + // + ASSERT(HeadDescriptor); + CurrentDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)HeadDescriptor->NextDescriptor; + PreviousDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)HeadDescriptor; + + while(CurrentDescriptor) + { + if ((CurrentDescriptor->HeadPhysicalDescriptor & OHCI_ENDPOINT_HEAD_MASK) == CurrentDescriptor->TailPhysicalDescriptor || (CurrentDescriptor->HeadPhysicalDescriptor & OHCI_ENDPOINT_HALTED)) + { + // + // cleanup endpoint + // + TempDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)CurrentDescriptor->NextDescriptor; + CleanupEndpointDescriptor(CurrentDescriptor, PreviousDescriptor); + + // + // use next descriptor + // + CurrentDescriptor = TempDescriptor; + } + + if (!CurrentDescriptor) + break; + + if (CurrentDescriptor->HeadPhysicalDescriptor) + { + TransferDescriptor = (POHCI_GENERAL_TD)CurrentDescriptor->HeadLogicalDescriptor; + ASSERT(TransferDescriptor); + + if ((OHCI_ENDPOINT_GET_ENDPOINT_NUMBER(TransferDescriptor->Flags) == (EndpointDescriptor->bEndpointAddress & 0xF)) && + (OHCI_ENDPOINT_GET_DEVICE_ADDRESS(TransferDescriptor->Flags) == DeviceAddress)) + { + // + // cleanup endpoint + // + TempDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)CurrentDescriptor->NextDescriptor; + CleanupEndpointDescriptor(CurrentDescriptor, PreviousDescriptor); + // + // use next descriptor + // + CurrentDescriptor = TempDescriptor; + } + } + + if (!CurrentDescriptor) + break; + + PreviousDescriptor = CurrentDescriptor; + CurrentDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)CurrentDescriptor->NextDescriptor; + } + + // + // done + // + return STATUS_SUCCESS; +} + + +NTSTATUS +CreateUSBQueue( + PUSBQUEUE *OutUsbQueue) +{ + PUSBQUEUE This; + + // + // allocate controller + // + This = new(NonPagedPool, TAG_USBOHCI) CUSBQueue(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutUsbQueue = (PUSBQUEUE)This; + + // + // done + // + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/usb/usbohci/usb_request.cpp b/reactos/drivers/usb/usbohci/usb_request.cpp new file mode 100644 index 00000000000..b667554068f --- /dev/null +++ b/reactos/drivers/usb/usbohci/usb_request.cpp @@ -0,0 +1,1895 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbohci/usb_request.cpp + * PURPOSE: USB OHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#define INITGUID + +#include "usbohci.h" +#include "hardware.h" + +class CUSBRequest : public IUSBRequest +{ +public: + STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); + + STDMETHODIMP_(ULONG) AddRef() + { + InterlockedIncrement(&m_Ref); + return m_Ref; + } + STDMETHODIMP_(ULONG) Release() + { + InterlockedDecrement(&m_Ref); + + if (!m_Ref) + { + delete this; + return 0; + } + return m_Ref; + } + + // IUSBRequest interface functions + virtual NTSTATUS InitializeWithSetupPacket(IN PDMAMEMORYMANAGER DmaManager, IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, IN UCHAR DeviceAddress, IN OPTIONAL PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN USB_DEVICE_SPEED DeviceSpeed, IN OUT ULONG TransferBufferLength, IN OUT PMDL TransferBuffer); + virtual NTSTATUS InitializeWithIrp(IN PDMAMEMORYMANAGER DmaManager, IN OUT PIRP Irp, IN USB_DEVICE_SPEED DeviceSpeed); + virtual BOOLEAN IsRequestComplete(); + virtual ULONG GetTransferType(); + virtual NTSTATUS GetEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutEndpointDescriptor); + virtual VOID GetResultStatus(OUT OPTIONAL NTSTATUS *NtStatusCode, OUT OPTIONAL PULONG UrbStatusCode); + virtual BOOLEAN IsRequestInitialized(); + virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead); + virtual VOID CompletionCallback(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor); + virtual VOID FreeEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor); + virtual UCHAR GetInterval(); + + + // local functions + ULONG InternalGetTransferType(); + UCHAR InternalGetPidDirection(); + UCHAR GetDeviceAddress(); + NTSTATUS BuildSetupPacket(); + NTSTATUS BuildSetupPacketFromURB(); + NTSTATUS BuildControlTransferDescriptor(POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor); + NTSTATUS BuildBulkInterruptEndpoint(POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor); + NTSTATUS BuildIsochronousEndpoint(POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor); + NTSTATUS CreateGeneralTransferDescriptor(POHCI_GENERAL_TD* OutDescriptor, ULONG BufferSize); + VOID FreeDescriptor(POHCI_GENERAL_TD Descriptor); + NTSTATUS AllocateEndpointDescriptor(OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor); + NTSTATUS CreateIsochronousTransferDescriptor(OUT POHCI_ISO_TD *OutDescriptor, ULONG FrameCount); + UCHAR GetEndpointAddress(); + USHORT GetMaxPacketSize(); + VOID CheckError(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor); + + + // constructor / destructor + CUSBRequest(IUnknown *OuterUnknown){} + virtual ~CUSBRequest(){} + +protected: + LONG m_Ref; + + // + // memory manager for allocating setup packet / queue head / transfer descriptors + // + PDMAMEMORYMANAGER m_DmaManager; + + // + // caller provided irp packet containing URB request + // + PIRP m_Irp; + + // + // transfer buffer length + // + ULONG m_TransferBufferLength; + + // + // current transfer length + // + ULONG m_TransferBufferLengthCompleted; + + // + // Total Transfer Length + // + ULONG m_TotalBytesTransferred; + + // + // transfer buffer MDL + // + PMDL m_TransferBufferMDL; + + // + // caller provided setup packet + // + PUSB_DEFAULT_PIPE_SETUP_PACKET m_SetupPacket; + + // + // completion event for callers who initialized request with setup packet + // + PKEVENT m_CompletionEvent; + + // + // device address for callers who initialized it with device address + // + UCHAR m_DeviceAddress; + + // + // store end point address + // + PUSB_ENDPOINT_DESCRIPTOR m_EndpointDescriptor; + + // + // allocated setup packet from the DMA pool + // + PUSB_DEFAULT_PIPE_SETUP_PACKET m_DescriptorPacket; + PHYSICAL_ADDRESS m_DescriptorSetupPacket; + + // + // stores the result of the operation + // + NTSTATUS m_NtStatusCode; + ULONG m_UrbStatusCode; + + // + // device speed + // + USB_DEVICE_SPEED m_DeviceSpeed; + + // + // store urb + // + PURB m_Urb; +}; + +//---------------------------------------------------------------------------------------- +NTSTATUS +STDMETHODCALLTYPE +CUSBRequest::QueryInterface( + IN REFIID refiid, + OUT PVOID* Output) +{ + return STATUS_UNSUCCESSFUL; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::InitializeWithSetupPacket( + IN PDMAMEMORYMANAGER DmaManager, + IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, + IN UCHAR DeviceAddress, + IN OPTIONAL PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor, + IN USB_DEVICE_SPEED DeviceSpeed, + IN OUT ULONG TransferBufferLength, + IN OUT PMDL TransferBuffer) +{ + // + // sanity checks + // + PC_ASSERT(DmaManager); + PC_ASSERT(SetupPacket); + + // + // initialize packet + // + m_DmaManager = DmaManager; + m_SetupPacket = SetupPacket; + m_TransferBufferLength = TransferBufferLength; + m_TransferBufferMDL = TransferBuffer; + m_DeviceAddress = DeviceAddress; + m_EndpointDescriptor = EndpointDescriptor; + m_TotalBytesTransferred = 0; + m_DeviceSpeed = DeviceSpeed; + + // + // Set Length Completed to 0 + // + m_TransferBufferLengthCompleted = 0; + + // + // allocate completion event + // + m_CompletionEvent = (PKEVENT)ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_USBOHCI); + if (!m_CompletionEvent) + { + // + // failed to allocate completion event + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize completion event + // + KeInitializeEvent(m_CompletionEvent, NotificationEvent, FALSE); + + // + // done + // + return STATUS_SUCCESS; +} +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::InitializeWithIrp( + IN PDMAMEMORYMANAGER DmaManager, + IN OUT PIRP Irp, + IN USB_DEVICE_SPEED DeviceSpeed) +{ + PIO_STACK_LOCATION IoStack; + + // + // sanity checks + // + PC_ASSERT(DmaManager); + PC_ASSERT(Irp); + + m_DmaManager = DmaManager; + m_TotalBytesTransferred = 0; + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // sanity check + // + PC_ASSERT(IoStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL); + PC_ASSERT(IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_SUBMIT_URB); + PC_ASSERT(IoStack->Parameters.Others.Argument1 != 0); + + // + // get urb + // + m_Urb = (PURB)IoStack->Parameters.Others.Argument1; + + // + // store irp + // + m_Irp = Irp; + + // + // store speed + // + m_DeviceSpeed = DeviceSpeed; + + // + // check function type + // + switch (m_Urb->UrbHeader.Function) + { + case URB_FUNCTION_ISOCH_TRANSFER: + { + // + // there must be at least one packet + // + ASSERT(m_Urb->UrbIsochronousTransfer.NumberOfPackets); + + // + // is there data to be transferred + // + if (m_Urb->UrbIsochronousTransfer.TransferBufferLength) + { + // + // Check if there is a MDL + // + if (!m_Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL) + { + // + // sanity check + // + PC_ASSERT(m_Urb->UrbBulkOrInterruptTransfer.TransferBuffer); + + // + // Create one using TransferBuffer + // + DPRINT("Creating Mdl from Urb Buffer %p Length %lu\n", m_Urb->UrbBulkOrInterruptTransfer.TransferBuffer, m_Urb->UrbBulkOrInterruptTransfer.TransferBufferLength); + m_TransferBufferMDL = IoAllocateMdl(m_Urb->UrbBulkOrInterruptTransfer.TransferBuffer, + m_Urb->UrbBulkOrInterruptTransfer.TransferBufferLength, + FALSE, + FALSE, + NULL); + + if (!m_TransferBufferMDL) + { + // + // failed to allocate mdl + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build mdl for non paged pool + // FIXME: Does hub driver already do this when passing MDL? + // + MmBuildMdlForNonPagedPool(m_TransferBufferMDL); + } + else + { + // + // use provided mdl + // + m_TransferBufferMDL = m_Urb->UrbIsochronousTransfer.TransferBufferMDL; + } + } + + // + // save buffer length + // + m_TransferBufferLength = m_Urb->UrbIsochronousTransfer.TransferBufferLength; + + // + // Set Length Completed to 0 + // + m_TransferBufferLengthCompleted = 0; + + // + // get endpoint descriptor + // + m_EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)m_Urb->UrbIsochronousTransfer.PipeHandle; + + // + // completed initialization + // + break; + } + // + // luckily those request have the same structure layout + // + case URB_FUNCTION_CLASS_INTERFACE: + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + { + // + // bulk interrupt transfer + // + if (m_Urb->UrbBulkOrInterruptTransfer.TransferBufferLength) + { + // + // Check if there is a MDL + // + if (!m_Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL) + { + // + // sanity check + // + PC_ASSERT(m_Urb->UrbBulkOrInterruptTransfer.TransferBuffer); + + // + // Create one using TransferBuffer + // + DPRINT("Creating Mdl from Urb Buffer %p Length %lu\n", m_Urb->UrbBulkOrInterruptTransfer.TransferBuffer, m_Urb->UrbBulkOrInterruptTransfer.TransferBufferLength); + m_TransferBufferMDL = IoAllocateMdl(m_Urb->UrbBulkOrInterruptTransfer.TransferBuffer, + m_Urb->UrbBulkOrInterruptTransfer.TransferBufferLength, + FALSE, + FALSE, + NULL); + + if (!m_TransferBufferMDL) + { + // + // failed to allocate mdl + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build mdl for non paged pool + // FIXME: Does hub driver already do this when passing MDL? + // + MmBuildMdlForNonPagedPool(m_TransferBufferMDL); + + // + // Keep that ehci created the MDL and needs to free it. + // + } + else + { + m_TransferBufferMDL = m_Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL; + } + + // + // save buffer length + // + m_TransferBufferLength = m_Urb->UrbBulkOrInterruptTransfer.TransferBufferLength; + + // + // Set Length Completed to 0 + // + m_TransferBufferLengthCompleted = 0; + + // + // get endpoint descriptor + // + m_EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)m_Urb->UrbBulkOrInterruptTransfer.PipeHandle; + + } + break; + } + default: + DPRINT1("URB Function: not supported %x\n", m_Urb->UrbHeader.Function); + PC_ASSERT(FALSE); + } + + // + // done + // + return STATUS_SUCCESS; + +} + +//---------------------------------------------------------------------------------------- +BOOLEAN +CUSBRequest::IsRequestComplete() +{ + // + // FIXME: check if request was split + // + + // + // Check if the transfer was completed, only valid for Bulk Transfers + // + if ((m_TransferBufferLengthCompleted < m_TransferBufferLength) + && (GetTransferType() == USB_ENDPOINT_TYPE_BULK)) + { + // + // Transfer not completed + // + return FALSE; + } + return TRUE; +} +//---------------------------------------------------------------------------------------- +ULONG +CUSBRequest::GetTransferType() +{ + // + // call internal implementation + // + return InternalGetTransferType(); +} + +USHORT +CUSBRequest::GetMaxPacketSize() +{ + if (!m_EndpointDescriptor) + { + // + // control request + // + return 0; + } + + ASSERT(m_Irp); + ASSERT(m_EndpointDescriptor); + + // + // return max packet size + // + return m_EndpointDescriptor->wMaxPacketSize; +} + +UCHAR +CUSBRequest::GetInterval() +{ + ASSERT(m_EndpointDescriptor); + ASSERT((m_EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT); + + // + // return interrupt interval + // + return m_EndpointDescriptor->bInterval; +} + +UCHAR +CUSBRequest::GetEndpointAddress() +{ + if (!m_EndpointDescriptor) + { + // + // control request + // + return 0; + } + + ASSERT(m_Irp); + ASSERT(m_EndpointDescriptor); + + // + // endpoint number is between 1-15 + // + return (m_EndpointDescriptor->bEndpointAddress & 0xF); +} + +//---------------------------------------------------------------------------------------- +ULONG +CUSBRequest::InternalGetTransferType() +{ + ULONG TransferType; + + // + // check if an irp is provided + // + if (m_Irp) + { + ASSERT(m_EndpointDescriptor); + + // + // end point is defined in the low byte of bmAttributes + // + TransferType = (m_EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK); + } + else + { + // + // initialized with setup packet, must be a control transfer + // + TransferType = USB_ENDPOINT_TYPE_CONTROL; + } + + // + // done + // + return TransferType; +} + +UCHAR +CUSBRequest::InternalGetPidDirection() +{ + ASSERT(m_Irp); + ASSERT(m_EndpointDescriptor); + + // + // end point is defined in the low byte of bEndpointAddress + // + return (m_EndpointDescriptor->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) >> 7; +} + + +//---------------------------------------------------------------------------------------- +UCHAR +CUSBRequest::GetDeviceAddress() +{ + PIO_STACK_LOCATION IoStack; + PURB Urb; + PUSBDEVICE UsbDevice; + + // + // check if there is an irp provided + // + if (!m_Irp) + { + // + // used provided address + // + return m_DeviceAddress; + } + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(m_Irp); + + // + // get contained urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + + // + // check if there is a pipe handle provided + // + if (Urb->UrbHeader.UsbdDeviceHandle) + { + // + // there is a device handle provided + // + UsbDevice = (PUSBDEVICE)Urb->UrbHeader.UsbdDeviceHandle; + + // + // return device address + // + return UsbDevice->GetDeviceAddress(); + } + + // + // no device handle provided, it is the host root bus + // + return 0; +} + +VOID +CUSBRequest::FreeDescriptor( + POHCI_GENERAL_TD Descriptor) +{ + if (Descriptor->BufferSize) + { + // + // free buffer + // + m_DmaManager->Release(Descriptor->BufferLogical, Descriptor->BufferSize); + } + + // + // release descriptor + // + m_DmaManager->Release(Descriptor, sizeof(OHCI_GENERAL_TD)); + +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::CreateIsochronousTransferDescriptor( + POHCI_ISO_TD* OutDescriptor, + ULONG FrameCount) +{ + POHCI_ISO_TD Descriptor; + PHYSICAL_ADDRESS DescriptorAddress; + NTSTATUS Status; + + // + // allocate transfer descriptor + // + Status = m_DmaManager->Allocate(sizeof(OHCI_ISO_TD), (PVOID*)&Descriptor, &DescriptorAddress); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + // + // initialize descriptor, hardware part + // + Descriptor->Flags = OHCI_ITD_SET_FRAME_COUNT(FrameCount) | OHCI_ITD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE);// | OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED); + Descriptor->BufferPhysical = 0; + Descriptor->NextPhysicalDescriptor = 0; + Descriptor->LastPhysicalByteAddress = 0; + + // + // software part + // + Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart; + Descriptor->NextLogicalDescriptor = 0; + + // + // store result + // + *OutDescriptor = Descriptor; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBRequest::BuildIsochronousEndpoint( + POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor) +{ + POHCI_ISO_TD FirstDescriptor = NULL, PreviousDescriptor = NULL, CurrentDescriptor = NULL; + POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor; + ULONG Index = 0, SubIndex, NumberOfPackets, PageOffset, Page; + NTSTATUS Status; + PVOID Buffer; + PIO_STACK_LOCATION IoStack; + PURB Urb; + PHYSICAL_ADDRESS Address; + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(m_Irp); + + // + // sanity check + // + PC_ASSERT(IoStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL); + PC_ASSERT(IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_SUBMIT_URB); + PC_ASSERT(IoStack->Parameters.Others.Argument1 != 0); + + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + ASSERT(Urb); + + // + // allocate endpoint descriptor + // + Status = AllocateEndpointDescriptor(&EndpointDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to create setup descriptor + // + ASSERT(FALSE); + return Status; + } + + // + // get buffer + // + Buffer = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority); + ASSERT(Buffer); + + // + // FIXME: support requests which spans serveral pages + // + ASSERT(ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(m_TransferBufferMDL), MmGetMdlByteCount(m_TransferBufferMDL)) <= 2); + + Status = m_DmaManager->Allocate(m_TransferBufferLength, &Buffer, &Address); + ASSERT(Status == STATUS_SUCCESS); + + + while(Index < Urb->UrbIsochronousTransfer.NumberOfPackets) + { + // + // get number of packets remaining + // + NumberOfPackets = min(Urb->UrbIsochronousTransfer.NumberOfPackets - Index, OHCI_ITD_NOFFSET); + // + // allocate iso descriptor + // + Status = CreateIsochronousTransferDescriptor(&CurrentDescriptor, NumberOfPackets); + if (!NT_SUCCESS(Status)) + { + // + // FIXME: cleanup + // failed to allocate descriptor + // + ASSERT(FALSE); + return Status; + } + + // + // get physical page + // + Page = MmGetPhysicalAddress(Buffer).LowPart; + + // + // get page offset + // + PageOffset = BYTE_OFFSET(Page); + + // + // initialize descriptor + // + CurrentDescriptor->BufferPhysical = Page - PageOffset; + + for(SubIndex = 0; SubIndex < NumberOfPackets; SubIndex++) + { + // + // store buffer offset + // + CurrentDescriptor->Offset[SubIndex] = Urb->UrbIsochronousTransfer.IsoPacket[Index+SubIndex].Offset + PageOffset; + DPRINT("Index %lu PacketOffset %lu FinalOffset %lu\n", SubIndex+Index, Urb->UrbIsochronousTransfer.IsoPacket[Index+SubIndex].Offset, CurrentDescriptor->Offset[SubIndex]); + } + + // + // increment packet offset + // + Index += NumberOfPackets; + + // + // check if this is the last descriptor + // + if (Index == Urb->UrbIsochronousTransfer.NumberOfPackets) + { + // + // end of transfer + // + CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + PageOffset + m_TransferBufferLength - 1; + } + else + { + // + // use start address of next packet - 1 + // + CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + PageOffset + Urb->UrbIsochronousTransfer.IsoPacket[Index].Offset - 1; + } + + // + // is there a previous descriptor + // + if (PreviousDescriptor) + { + // + // link descriptors + // + PreviousDescriptor->NextLogicalDescriptor = CurrentDescriptor; + PreviousDescriptor->NextPhysicalDescriptor = CurrentDescriptor->PhysicalAddress.LowPart; + } + else + { + // + // first descriptor + // + FirstDescriptor = CurrentDescriptor; + } + + // + // store as previous descriptor + // + PreviousDescriptor = CurrentDescriptor; + DPRINT("Current Descriptor %p Logical %lx StartAddress %x EndAddress %x\n", CurrentDescriptor, CurrentDescriptor->PhysicalAddress.LowPart, CurrentDescriptor->BufferPhysical, CurrentDescriptor->LastPhysicalByteAddress); + + // + // fire interrupt as soon transfer is finished + // + CurrentDescriptor->Flags |= OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE); + } + + // + // clear interrupt mask for last transfer descriptor + // + CurrentDescriptor->Flags &= ~OHCI_TD_INTERRUPT_MASK; + + // + // fire interrupt as soon transfer is finished + // + CurrentDescriptor->Flags |= OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE); + + // + // set isochronous type + // + EndpointDescriptor->Flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT; + + // + // now link descriptor to endpoint + // + EndpointDescriptor->HeadPhysicalDescriptor = FirstDescriptor->PhysicalAddress.LowPart; + EndpointDescriptor->TailPhysicalDescriptor = CurrentDescriptor->PhysicalAddress.LowPart; + EndpointDescriptor->HeadLogicalDescriptor = FirstDescriptor; + + // + // store result + // + *OutEndpointDescriptor = EndpointDescriptor; + + // + // done + // + return STATUS_SUCCESS; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::CreateGeneralTransferDescriptor( + POHCI_GENERAL_TD* OutDescriptor, + ULONG BufferSize) +{ + POHCI_GENERAL_TD Descriptor; + PHYSICAL_ADDRESS DescriptorAddress; + NTSTATUS Status; + + // + // allocate transfer descriptor + // + Status = m_DmaManager->Allocate(sizeof(OHCI_GENERAL_TD), (PVOID*)&Descriptor, &DescriptorAddress); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + return Status; + } + + // + // initialize descriptor, hardware part + // + Descriptor->Flags = 0; + Descriptor->BufferPhysical = 0; + Descriptor->NextPhysicalDescriptor = 0; + Descriptor->LastPhysicalByteAddress = 0; + + // + // software part + // + Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart; + Descriptor->BufferSize = BufferSize; + + if (BufferSize > 0) + { + // + // allocate buffer from dma + // + Status = m_DmaManager->Allocate(BufferSize, &Descriptor->BufferLogical, &DescriptorAddress); + if (!NT_SUCCESS(Status)) + { + // + // no memory + // + m_DmaManager->Release(Descriptor, sizeof(OHCI_GENERAL_TD)); + return Status; + } + + // + // set physical address of buffer + // + Descriptor->BufferPhysical = DescriptorAddress.LowPart; + Descriptor->LastPhysicalByteAddress = Descriptor->BufferPhysical + BufferSize - 1; + } + + // + // store result + // + *OutDescriptor = Descriptor; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBRequest::AllocateEndpointDescriptor( + OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor) +{ + POHCI_ENDPOINT_DESCRIPTOR Descriptor; + PHYSICAL_ADDRESS DescriptorAddress; + NTSTATUS Status; + + // + // allocate descriptor + // + Status = m_DmaManager->Allocate(sizeof(OHCI_ENDPOINT_DESCRIPTOR), (PVOID*)&Descriptor, &DescriptorAddress); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate descriptor + // + return Status; + } + + // + // intialize descriptor + // + Descriptor->Flags = OHCI_ENDPOINT_SKIP; + + // + // append device address and endpoint number + // + Descriptor->Flags |= OHCI_ENDPOINT_SET_DEVICE_ADDRESS(GetDeviceAddress()); + Descriptor->Flags |= OHCI_ENDPOINT_SET_ENDPOINT_NUMBER(GetEndpointAddress()); + Descriptor->Flags |= OHCI_ENDPOINT_SET_MAX_PACKET_SIZE(GetMaxPacketSize()); + + DPRINT("Flags %x DeviceAddress %x EndpointAddress %x PacketSize %x\n", Descriptor->Flags, GetDeviceAddress(), GetEndpointAddress(), GetMaxPacketSize()); + + // + // is there an endpoint descriptor + // + if (m_EndpointDescriptor) + { + // + // check direction + // + if (USB_ENDPOINT_DIRECTION_OUT(m_EndpointDescriptor->bEndpointAddress)) + { + // + // direction out + // + Descriptor->Flags |= OHCI_ENDPOINT_DIRECTION_OUT; + } + else + { + // + // direction in + // + Descriptor->Flags |= OHCI_ENDPOINT_DIRECTION_IN; + } + + } + + // + // set type + // + if (m_DeviceSpeed == UsbFullSpeed) + { + // + // device is full speed + // + Descriptor->Flags |= OHCI_ENDPOINT_FULL_SPEED; + } + else if (m_DeviceSpeed == UsbLowSpeed) + { + // + // device is full speed + // + Descriptor->Flags |= OHCI_ENDPOINT_LOW_SPEED; + } + else + { + // + // error + // + ASSERT(FALSE); + } + + Descriptor->HeadPhysicalDescriptor = 0; + Descriptor->NextPhysicalEndpoint = 0; + Descriptor->TailPhysicalDescriptor = 0; + Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart; + + // + // store result + // + *OutDescriptor = Descriptor; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +CUSBRequest::BuildBulkInterruptEndpoint( + POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor) +{ + POHCI_GENERAL_TD FirstDescriptor, PreviousDescriptor = NULL, CurrentDescriptor, LastDescriptor; + POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor; + ULONG BufferSize, CurrentSize, Direction, MaxLengthInPage; + NTSTATUS Status; + PVOID Buffer; + + // + // allocate endpoint descriptor + // + Status = AllocateEndpointDescriptor(&EndpointDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to create setup descriptor + // + return Status; + } + + // + // allocate transfer descriptor for last descriptor + // + Status = CreateGeneralTransferDescriptor(&LastDescriptor, 0); + if (!NT_SUCCESS(Status)) + { + // + // failed to create transfer descriptor + // + m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR)); + return Status; + } + + // + // get buffer size + // + BufferSize = m_TransferBufferLength; + ASSERT(BufferSize); + ASSERT(m_TransferBufferMDL); + + // + // get buffer + // + Buffer = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority); + ASSERT(Buffer); + + if (InternalGetPidDirection()) + { + // + // input direction + // + Direction = OHCI_TD_DIRECTION_PID_IN; + } + else + { + // + // output direction + // + Direction = OHCI_TD_DIRECTION_PID_OUT; + } + + do + { + // + // get current buffersize + // + CurrentSize = min(8192, BufferSize); + + // + // get page offset + // + MaxLengthInPage = PAGE_SIZE - BYTE_OFFSET(Buffer); + + // + // get minimum from current page size + // + CurrentSize = min(CurrentSize, MaxLengthInPage); + ASSERT(CurrentSize); + + // + // allocate transfer descriptor + // + Status = CreateGeneralTransferDescriptor(&CurrentDescriptor, 0); + if (!NT_SUCCESS(Status)) + { + // + // failed to create transfer descriptor + // TODO: cleanup + // + ASSERT(FALSE); + m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR)); + FreeDescriptor(LastDescriptor); + return Status; + } + + // + // initialize descriptor + // + CurrentDescriptor->Flags = Direction | OHCI_TD_BUFFER_ROUNDING | OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE) | OHCI_TD_TOGGLE_CARRY; + + // + // store physical address of buffer + // + CurrentDescriptor->BufferPhysical = MmGetPhysicalAddress(Buffer).LowPart; + CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + CurrentSize - 1; + +#if 0 + if (m_Urb != NULL) + { + if (m_Urb->UrbBulkOrInterruptTransfer.TransferFlags & USBD_SHORT_TRANSFER_OK) + { + // + // indicate short packet support + // + CurrentDescriptor->Flags |= OHCI_TD_BUFFER_ROUNDING; + } + } +#endif + + // + // is there a previous descriptor + // + if (PreviousDescriptor) + { + // + // link descriptors + // + PreviousDescriptor->NextLogicalDescriptor = (PVOID)CurrentDescriptor; + PreviousDescriptor->NextPhysicalDescriptor = CurrentDescriptor->PhysicalAddress.LowPart; + } + else + { + // + // it is the first descriptor + // + FirstDescriptor = CurrentDescriptor; + } + + DPRINT("PreviousDescriptor %p CurrentDescriptor %p Logical %x Buffer Logical %p Physical %x Last Physical %x CurrentSize %lu\n", PreviousDescriptor, CurrentDescriptor, CurrentDescriptor->PhysicalAddress.LowPart, CurrentDescriptor->BufferLogical, CurrentDescriptor->BufferPhysical, CurrentDescriptor->LastPhysicalByteAddress, CurrentSize); + + // + // set previous descriptor + // + PreviousDescriptor = CurrentDescriptor; + + // + // subtract buffer size + // + BufferSize -= CurrentSize; + + // + // increment buffer offset + // + Buffer = (PVOID)((ULONG_PTR)Buffer + CurrentSize); + + }while(BufferSize); + + // + // first descriptor has no carry bit + // + FirstDescriptor->Flags &= ~OHCI_TD_TOGGLE_CARRY; + + // + // fixme: toggle + // + FirstDescriptor->Flags |= OHCI_TD_TOGGLE_0; + + // + // clear interrupt mask for last transfer descriptor + // + CurrentDescriptor->Flags &= ~OHCI_TD_INTERRUPT_MASK; + + // + // fire interrupt as soon transfer is finished + // + CurrentDescriptor->Flags |= OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE); + + // + // link last data descriptor to last descriptor + // + CurrentDescriptor->NextLogicalDescriptor = LastDescriptor; + CurrentDescriptor->NextPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart; + + // + // now link descriptor to endpoint + // + EndpointDescriptor->HeadPhysicalDescriptor = FirstDescriptor->PhysicalAddress.LowPart; + EndpointDescriptor->TailPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart; + EndpointDescriptor->HeadLogicalDescriptor = FirstDescriptor; + + // + // store result + // + *OutEndpointDescriptor = EndpointDescriptor; + + // + // done + // + return STATUS_SUCCESS; +} + + +NTSTATUS +CUSBRequest::BuildControlTransferDescriptor( + POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor) +{ + POHCI_GENERAL_TD SetupDescriptor, StatusDescriptor, DataDescriptor = NULL, LastDescriptor; + POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor; + NTSTATUS Status; + + // + // allocate endpoint descriptor + // + Status = AllocateEndpointDescriptor(&EndpointDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to create setup descriptor + // + return Status; + } + + // + // first allocate setup descriptor + // + Status = CreateGeneralTransferDescriptor(&SetupDescriptor, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + if (!NT_SUCCESS(Status)) + { + // + // failed to create setup descriptor + // + m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR)); + return Status; + } + + // + // now create the status descriptor + // + Status = CreateGeneralTransferDescriptor(&StatusDescriptor, 0); + if (!NT_SUCCESS(Status)) + { + // + // failed to create status descriptor + // + FreeDescriptor(SetupDescriptor); + m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR)); + return Status; + } + + // + // finally create the last descriptor + // + Status = CreateGeneralTransferDescriptor(&LastDescriptor, 0); + if (!NT_SUCCESS(Status)) + { + // + // failed to create status descriptor + // + FreeDescriptor(SetupDescriptor); + FreeDescriptor(StatusDescriptor); + m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR)); + return Status; + } + + if (m_TransferBufferLength) + { + // + // FIXME: support more than one data descriptor + // + ASSERT(m_TransferBufferLength < 8192); + + // + // now create the data descriptor + // + Status = CreateGeneralTransferDescriptor(&DataDescriptor, 0); + if (!NT_SUCCESS(Status)) + { + // + // failed to create status descriptor + // + m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR)); + FreeDescriptor(SetupDescriptor); + FreeDescriptor(StatusDescriptor); + FreeDescriptor(LastDescriptor); + return Status; + } + + // + // initialize data descriptor + // + DataDescriptor->Flags = OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE) | OHCI_TD_TOGGLE_CARRY | OHCI_TD_TOGGLE_1; + + if (m_EndpointDescriptor) + { + if (USB_ENDPOINT_DIRECTION_OUT(m_EndpointDescriptor->bEndpointAddress)) + { + // + // direction out + // + DataDescriptor->Flags |= OHCI_TD_DIRECTION_PID_OUT; + } + else + { + // + // direction in + // + DataDescriptor->Flags |= OHCI_TD_DIRECTION_PID_IN; + } + } + else + { + // + // no end point address provided - assume its an in direction + // + DataDescriptor->Flags |= OHCI_TD_DIRECTION_PID_IN; + } + + // + // use short packets + // + DataDescriptor->Flags |= OHCI_TD_BUFFER_ROUNDING; + + // + // store physical address of buffer + // + DataDescriptor->BufferPhysical = MmGetPhysicalAddress(MmGetMdlVirtualAddress(m_TransferBufferMDL)).LowPart; + DataDescriptor->LastPhysicalByteAddress = DataDescriptor->BufferPhysical + m_TransferBufferLength - 1; + } + + // + // initialize setup descriptor + // + SetupDescriptor->Flags = OHCI_TD_BUFFER_ROUNDING | OHCI_TD_DIRECTION_PID_SETUP | OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_TOGGLE_0 | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE); + + if (m_SetupPacket) + { + // + // copy setup packet + // + RtlCopyMemory(SetupDescriptor->BufferLogical, m_SetupPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET)); + } + else + { + // + // generate setup packet from urb + // + ASSERT(FALSE); + } + + // + // initialize status descriptor + // + StatusDescriptor->Flags = OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE); + if (m_TransferBufferLength == 0) + { + // + // input direction is flipped for the status descriptor + // + StatusDescriptor->Flags |= OHCI_TD_DIRECTION_PID_IN; + } + else + { + // + // output direction is flipped for the status descriptor + // + StatusDescriptor->Flags |= OHCI_TD_DIRECTION_PID_OUT; + } + + // + // now link the descriptors + // + if (m_TransferBufferLength) + { + // + // link setup descriptor to data descriptor + // + SetupDescriptor->NextPhysicalDescriptor = DataDescriptor->PhysicalAddress.LowPart; + SetupDescriptor->NextLogicalDescriptor = DataDescriptor; + + // + // link data descriptor to status descriptor + // FIXME: check if there are more data descriptors + // + DataDescriptor->NextPhysicalDescriptor = StatusDescriptor->PhysicalAddress.LowPart; + DataDescriptor->NextLogicalDescriptor = StatusDescriptor; + + // + // link status descriptor to last descriptor + // + StatusDescriptor->NextPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart; + StatusDescriptor->NextLogicalDescriptor = LastDescriptor; + } + else + { + // + // link setup descriptor to status descriptor + // + SetupDescriptor->NextPhysicalDescriptor = StatusDescriptor->PhysicalAddress.LowPart; + SetupDescriptor->NextLogicalDescriptor = StatusDescriptor; + + // + // link status descriptor to last descriptor + // + StatusDescriptor->NextPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart; + StatusDescriptor->NextLogicalDescriptor = LastDescriptor; + } + + // + // now link descriptor to endpoint + // + EndpointDescriptor->HeadPhysicalDescriptor = SetupDescriptor->PhysicalAddress.LowPart; + EndpointDescriptor->TailPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart; + EndpointDescriptor->HeadLogicalDescriptor = SetupDescriptor; + + // + // store result + // + *OutEndpointDescriptor = EndpointDescriptor; + + // + // done + // + return STATUS_SUCCESS; +} + +//---------------------------------------------------------------------------------------- +NTSTATUS +CUSBRequest::GetEndpointDescriptor( + struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) +{ + ULONG TransferType; + NTSTATUS Status; + + // + // get transfer type + // + TransferType = InternalGetTransferType(); + + // + // build request depending on type + // + switch(TransferType) + { + case USB_ENDPOINT_TYPE_CONTROL: + Status = BuildControlTransferDescriptor((POHCI_ENDPOINT_DESCRIPTOR*)OutDescriptor); + break; + case USB_ENDPOINT_TYPE_BULK: + case USB_ENDPOINT_TYPE_INTERRUPT: + Status = BuildBulkInterruptEndpoint(OutDescriptor); + break; + case USB_ENDPOINT_TYPE_ISOCHRONOUS: + Status = BuildIsochronousEndpoint((POHCI_ENDPOINT_DESCRIPTOR*)OutDescriptor); + break; + default: + PC_ASSERT(FALSE); + Status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (NT_SUCCESS(Status)) + { + // + // store queue head + // + //m_QueueHead = *OutDescriptor; + + // + // store request object + // + (*OutDescriptor)->Request = PVOID(this); + } + + // + // done + // + return Status; +} + +//---------------------------------------------------------------------------------------- +VOID +CUSBRequest::GetResultStatus( + OUT OPTIONAL NTSTATUS * NtStatusCode, + OUT OPTIONAL PULONG UrbStatusCode) +{ + // + // sanity check + // + PC_ASSERT(m_CompletionEvent); + + // + // wait for the operation to complete + // + KeWaitForSingleObject(m_CompletionEvent, Executive, KernelMode, FALSE, NULL); + + // + // copy status + // + if (NtStatusCode) + { + *NtStatusCode = m_NtStatusCode; + } + + // + // copy urb status + // + if (UrbStatusCode) + { + *UrbStatusCode = m_UrbStatusCode; + } + +} + +VOID +CUSBRequest::FreeEndpointDescriptor( + struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor) +{ + POHCI_GENERAL_TD TransferDescriptor, NextTransferDescriptor; + POHCI_ISO_TD IsoTransferDescriptor, IsoNextTransferDescriptor; + ULONG Index, PacketCount; + + DPRINT("CUSBRequest::FreeEndpointDescriptor EndpointDescriptor %p Logical %x\n", OutDescriptor, OutDescriptor->PhysicalAddress.LowPart); + + if (OutDescriptor->Flags & OHCI_ENDPOINT_ISOCHRONOUS_FORMAT) + { + // + // get first iso transfer descriptor + // + IsoTransferDescriptor = (POHCI_ISO_TD)OutDescriptor->HeadLogicalDescriptor; + + // + // release endpoint descriptor + // + m_DmaManager->Release(OutDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR)); + + while(IsoTransferDescriptor) + { + // + // get next + // + IsoNextTransferDescriptor = IsoTransferDescriptor->NextLogicalDescriptor; + + // + // get packet count + // + PacketCount = OHCI_ITD_GET_FRAME_COUNT(IsoTransferDescriptor->Flags); + + DPRINT("CUSBRequest::FreeEndpointDescriptor Descriptor %p Logical %x Buffer Physical %x EndAddress %x PacketCount %lu\n", IsoTransferDescriptor, IsoTransferDescriptor->PhysicalAddress.LowPart, IsoTransferDescriptor->BufferPhysical, IsoTransferDescriptor->LastPhysicalByteAddress, PacketCount); + + for(Index = 0; Index < PacketCount; Index++) + { + DPRINT("PSW Index %lu Value %x\n", Index, IsoTransferDescriptor->Offset[Index]); + } + + // + // release descriptor + // + m_DmaManager->Release(IsoTransferDescriptor, sizeof(OHCI_ISO_TD)); + + // + // move to next + // + IsoTransferDescriptor = IsoNextTransferDescriptor; + } + } + else + { + // + // get first general transfer descriptor + // + TransferDescriptor = (POHCI_GENERAL_TD)OutDescriptor->HeadLogicalDescriptor; + + // + // release endpoint descriptor + // + m_DmaManager->Release(OutDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR)); + + while(TransferDescriptor) + { + // + // get next + // + NextTransferDescriptor = (POHCI_GENERAL_TD)TransferDescriptor->NextLogicalDescriptor; + + // + // is there a buffer associated + // + if (TransferDescriptor->BufferSize) + { + // + // release buffer + // + m_DmaManager->Release(TransferDescriptor->BufferLogical, TransferDescriptor->BufferSize); + } + + DPRINT("CUSBRequest::FreeEndpointDescriptor Descriptor %p Logical %x Buffer Physical %x EndAddress %x\n", TransferDescriptor, TransferDescriptor->PhysicalAddress.LowPart, TransferDescriptor->BufferPhysical, TransferDescriptor->LastPhysicalByteAddress); + + // + // release descriptor + // + m_DmaManager->Release(TransferDescriptor, sizeof(OHCI_GENERAL_TD)); + + // + // move to next + // + TransferDescriptor = NextTransferDescriptor; + } + } + +} + +VOID +CUSBRequest::CheckError( + struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor) +{ + POHCI_GENERAL_TD TransferDescriptor; + ULONG ConditionCode; + PURB Urb; + PIO_STACK_LOCATION IoStack; + + + // + // set status code + // + m_NtStatusCode = STATUS_SUCCESS; + m_UrbStatusCode = USBD_STATUS_SUCCESS; + + + if (OutDescriptor->Flags & OHCI_ENDPOINT_ISOCHRONOUS_FORMAT) + { + // + // FIXME: handle isochronous support + // + ASSERT(FALSE); + } + else + { + // + // get first general transfer descriptor + // + TransferDescriptor = (POHCI_GENERAL_TD)OutDescriptor->HeadLogicalDescriptor; + + while(TransferDescriptor) + { + // + // get condition code + // + ConditionCode = OHCI_TD_GET_CONDITION_CODE(TransferDescriptor->Flags); + if (ConditionCode != OHCI_TD_CONDITION_NO_ERROR) + { + // + // FIXME status code + // + m_NtStatusCode = STATUS_UNSUCCESSFUL; + + switch(ConditionCode) + { + case OHCI_TD_CONDITION_CRC_ERROR: + DPRINT1("OHCI_TD_CONDITION_CRC_ERROR detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor); + m_UrbStatusCode = USBD_STATUS_CRC; + break; + case OHCI_TD_CONDITION_BIT_STUFFING: + DPRINT1("OHCI_TD_CONDITION_BIT_STUFFING detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor); + m_UrbStatusCode = USBD_STATUS_BTSTUFF; + break; + case OHCI_TD_CONDITION_TOGGLE_MISMATCH: + DPRINT1("OHCI_TD_CONDITION_TOGGLE_MISMATCH detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor); + m_UrbStatusCode = USBD_STATUS_DATA_TOGGLE_MISMATCH; + break; + case OHCI_TD_CONDITION_STALL: + DPRINT1("OHCI_TD_CONDITION_STALL detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor); + m_UrbStatusCode = USBD_STATUS_STALL_PID; + break; + case OHCI_TD_CONDITION_NO_RESPONSE: + DPRINT1("OHCI_TD_CONDITION_NO_RESPONSE detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor); + m_UrbStatusCode = USBD_STATUS_DEV_NOT_RESPONDING; + break; + case OHCI_TD_CONDITION_PID_CHECK_FAILURE: + DPRINT1("OHCI_TD_CONDITION_PID_CHECK_FAILURE detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor); + m_UrbStatusCode = USBD_STATUS_PID_CHECK_FAILURE; + break; + case OHCI_TD_CONDITION_UNEXPECTED_PID: + DPRINT1("OHCI_TD_CONDITION_UNEXPECTED_PID detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor); + m_UrbStatusCode = USBD_STATUS_UNEXPECTED_PID; + break; + case OHCI_TD_CONDITION_DATA_OVERRUN: + DPRINT1("OHCI_TD_CONDITION_DATA_OVERRUN detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor); + m_UrbStatusCode = USBD_STATUS_DATA_OVERRUN; + break; + case OHCI_TD_CONDITION_DATA_UNDERRUN: + if (m_Irp) + { + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(m_Irp); + + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + + if(Urb->UrbBulkOrInterruptTransfer.TransferFlags & USBD_SHORT_TRANSFER_OK) + { + // + // short packets are ok + // + ASSERT(Urb->UrbHeader.Function == URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER); + m_NtStatusCode = STATUS_SUCCESS; + break; + } + } + DPRINT1("OHCI_TD_CONDITION_DATA_UNDERRUN detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor); + m_UrbStatusCode = USBD_STATUS_DATA_UNDERRUN; + break; + case OHCI_TD_CONDITION_BUFFER_OVERRUN: + DPRINT1("OHCI_TD_CONDITION_BUFFER_OVERRUN detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor); + m_UrbStatusCode = USBD_STATUS_BUFFER_OVERRUN; + break; + case OHCI_TD_CONDITION_BUFFER_UNDERRUN: + DPRINT1("OHCI_TD_CONDITION_BUFFER_UNDERRUN detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor); + m_UrbStatusCode = USBD_STATUS_BUFFER_UNDERRUN; + break; + } + } + + // + // get next + // + TransferDescriptor = (POHCI_GENERAL_TD)TransferDescriptor->NextLogicalDescriptor; + } + } +} + +VOID +CUSBRequest::CompletionCallback( + struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor) +{ + PIO_STACK_LOCATION IoStack; + PURB Urb; + + DPRINT("CUSBRequest::CompletionCallback Descriptor %p PhysicalAddress %x\n", OutDescriptor, OutDescriptor->PhysicalAddress.LowPart); + + // + // check for errors + // + CheckError(OutDescriptor); + + if (m_Irp) + { + // + // set irp completion status + // + m_Irp->IoStatus.Status = m_NtStatusCode; + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(m_Irp); + + // + // get urb + // + Urb = (PURB)IoStack->Parameters.Others.Argument1; + + // + // store urb status + // + Urb->UrbHeader.Status = m_UrbStatusCode; + + // + // Check if the MDL was created + // + if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL) + { + // + // Free Mdl + // + IoFreeMdl(m_TransferBufferMDL); + } + + // + // FIXME: support status and calculate length + // + + // + // FIXME: check if the transfer was split + // if yes dont complete irp yet + // + IoCompleteRequest(m_Irp, IO_NO_INCREMENT); + } + else + { + // + // signal completion event + // + PC_ASSERT(m_CompletionEvent); + KeSetEvent(m_CompletionEvent, 0, FALSE); + } +} + + +//----------------------------------------------------------------------------------------- +BOOLEAN +CUSBRequest::IsRequestInitialized() +{ + if (m_Irp || m_SetupPacket) + { + // + // request is initialized + // + return TRUE; + } + + // + // request is not initialized + // + return FALSE; +} + +//----------------------------------------------------------------------------------------- +BOOLEAN +CUSBRequest::IsQueueHeadComplete( + struct _QUEUE_HEAD * QueueHead) +{ + UNIMPLEMENTED + return TRUE; +} + + + +//----------------------------------------------------------------------------------------- +NTSTATUS +InternalCreateUSBRequest( + PUSBREQUEST *OutRequest) +{ + PUSBREQUEST This; + + // + // allocate requests + // + This = new(NonPagedPool, TAG_USBOHCI) CUSBRequest(0); + if (!This) + { + // + // failed to allocate + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add reference count + // + This->AddRef(); + + // + // return result + // + *OutRequest = (PUSBREQUEST)This; + + // + // done + // + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/usb/usbohci/usbohci.cpp b/reactos/drivers/usb/usbohci/usbohci.cpp new file mode 100644 index 00000000000..caf3067974b --- /dev/null +++ b/reactos/drivers/usb/usbohci/usbohci.cpp @@ -0,0 +1,144 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbohci/usbohci.cpp + * PURPOSE: USB OHCI device driver. + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbohci.h" + +// +// driver verifier +// +DRIVER_ADD_DEVICE OHCI_AddDevice; + +NTSTATUS +NTAPI +OHCI_AddDevice( + PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT PhysicalDeviceObject) +{ + NTSTATUS Status; + PHCDCONTROLLER HcdController; + + DPRINT("OHCI_AddDevice\n"); + + /* first create the controller object */ + Status = CreateHCDController(&HcdController); + if (!NT_SUCCESS(Status)) + { + /* failed to create hcd */ + DPRINT1("AddDevice: Failed to create hcd with %x\n", Status); + return Status; + } + + /* initialize the hcd */ + Status = HcdController->Initialize(NULL, // FIXME + DriverObject, + PhysicalDeviceObject); + + /* check for success */ + if (!NT_SUCCESS(Status)) + { + /* failed to initialize device */ + DPRINT1("AddDevice: failed to initialize\n"); + + /* release object */ + HcdController->Release(); + } + + return Status; + +} + +NTSTATUS +NTAPI +OHCI_Dispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PCOMMON_DEVICE_EXTENSION DeviceExtension; + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + + // + // get common device extension + // + DeviceExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // sanity checks + // + PC_ASSERT(DeviceExtension->Dispatcher); + + switch(IoStack->MajorFunction) + { + case IRP_MJ_PNP: + { + // + // dispatch pnp + // + return DeviceExtension->Dispatcher->HandlePnp(DeviceObject, Irp); + } + + case IRP_MJ_POWER: + { + // + // dispatch pnp + // + return DeviceExtension->Dispatcher->HandlePower(DeviceObject, Irp); + } + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + case IRP_MJ_DEVICE_CONTROL: + { + // + // dispatch pnp + // + return DeviceExtension->Dispatcher->HandleDeviceControl(DeviceObject, Irp); + } + default: + { + DPRINT1("OHCI_Dispatch> Major %lu Minor %lu unhandeled\n", IoStack->MajorFunction, IoStack->MinorFunction); + Status = STATUS_SUCCESS; + } + } + + // + // complete request + // + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} + +extern +"C" +NTSTATUS +NTAPI +DriverEntry( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath) +{ + + /* initialize driver object*/ + DriverObject->DriverExtension->AddDevice = OHCI_AddDevice; + + DriverObject->MajorFunction[IRP_MJ_CREATE] = OHCI_Dispatch; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = OHCI_Dispatch; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = OHCI_Dispatch; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = OHCI_Dispatch; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = OHCI_Dispatch; + DriverObject->MajorFunction[IRP_MJ_PNP] = OHCI_Dispatch; + + return STATUS_SUCCESS; +} diff --git a/reactos/drivers/usb/usbohci/usbohci.h b/reactos/drivers/usb/usbohci/usbohci.h new file mode 100644 index 00000000000..6eab4ba6045 --- /dev/null +++ b/reactos/drivers/usb/usbohci/usbohci.h @@ -0,0 +1,111 @@ +#ifndef USBOHCI_H__ +#define USBOHCI_H__ + +#include +#define NDEBUG +#include +#include +#include +#include +#include + +extern +"C" +{ +#include +} + +// +// FIXME: +// #include +// +#include +#include +#include + +// +// FIXME: +// the following includes are required to get kcom to compile +// +#include +#include +#include + +#include "interfaces.h" + +// +// flags for handling USB_REQUEST_SET_FEATURE / USB_REQUEST_GET_FEATURE +// +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVER_CURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define C_PORT_CONNECTION 16 +#define C_PORT_ENABLE 17 +#define C_PORT_SUSPEND 18 +#define C_PORT_OVER_CURRENT 19 +#define C_PORT_RESET 20 + +typedef struct +{ + BOOLEAN IsFDO; // is device a FDO or PDO + BOOLEAN IsHub; // is device a hub / child - not yet used + PDISPATCHIRP Dispatcher; // dispatches the code +}COMMON_DEVICE_EXTENSION, *PCOMMON_DEVICE_EXTENSION; + +// +// tag for allocations +// +#define TAG_USBOHCI 'ICHO' + +// +// assert for c++ - taken from portcls +// +#define PC_ASSERT(exp) \ + (VOID)((!(exp)) ? \ + RtlAssert((PVOID) #exp, (PVOID)__FILE__, __LINE__, NULL ), FALSE : TRUE) + +// +// hcd_controller.cpp +// +NTSTATUS CreateHCDController(PHCDCONTROLLER *HcdController); + +// +// hardware.cpp +// +NTSTATUS CreateUSBHardware(PUSBHARDWAREDEVICE *OutHardware); + +// +// misc.cpp +// +NTSTATUS NTAPI SyncForwardIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS NTAPI GetBusInterface(PDEVICE_OBJECT DeviceObject, PBUS_INTERFACE_STANDARD busInterface); + +// +// root_hub_controller.cpp +// +NTSTATUS CreateHubController(PHUBCONTROLLER * OutHubController); + +// +// memory_manager.cpp +// +NTSTATUS CreateDMAMemoryManager(PDMAMEMORYMANAGER *OutMemoryManager); + + +// +// usb_device.cpp +// +NTSTATUS CreateUSBDevice(PUSBDEVICE *OutDevice); + +// +// usb_queue.cpp +// +NTSTATUS CreateUSBQueue(PUSBQUEUE *OutUsbQueue); + +// +// usb_request.cpp +// +NTSTATUS InternalCreateUSBRequest(PUSBREQUEST *OutRequest); + +#endif diff --git a/reactos/drivers/usb/usbohci/usbohci.rbuild b/reactos/drivers/usb/usbohci/usbohci.rbuild new file mode 100644 index 00000000000..1b96cc7fece --- /dev/null +++ b/reactos/drivers/usb/usbohci/usbohci.rbuild @@ -0,0 +1,23 @@ + + + + + 0x600 + ntoskrnl + hal + usbd + pseh + usbohci.cpp + usb_device.cpp + usb_request.cpp + usb_queue.cpp + hcd_controller.cpp + hardware.cpp + misc.cpp + purecall.cpp + hub_controller.cpp + memory_manager.cpp + usbohci.rc + -fno-rtti + -fno-exceptions + diff --git a/reactos/drivers/usb/usbohci/usbohci.rc b/reactos/drivers/usb/usbohci/usbohci.rc new file mode 100644 index 00000000000..000ffdee9f9 --- /dev/null +++ b/reactos/drivers/usb/usbohci/usbohci.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "USBOHCI Driver API\0" +#define REACTOS_STR_INTERNAL_NAME "usbohci\0" +#define REACTOS_STR_ORIGINAL_FILENAME "usbohci.sys\0" +#include diff --git a/reactos/drivers/usb/usbstor/CMakeLists.txt b/reactos/drivers/usb/usbstor/CMakeLists.txt index 041ce3947b1..04a35004942 100644 --- a/reactos/drivers/usb/usbstor/CMakeLists.txt +++ b/reactos/drivers/usb/usbstor/CMakeLists.txt @@ -3,8 +3,9 @@ add_definitions(-DDEBUG_MODE) include_directories(${REACTOS_SOURCE_DIR}/ntoskrnl/include) -add_library(usbstor SHARED usbstor.c usbstor.rc) +add_library(usbstor SHARED descriptor.c disk.c fdo.c misc.c pdo.c queue.c error.c scsi.c usbstor.c usbstor.rc) set_module_type(usbstor kernelmodedriver) -add_importlibs(usbstor ntoskrnl hal) -add_cab_target(usbstor 2) +add_importlibs(usbstor ntoskrnl hal usbd) + +add_cd_file(TARGET usbstor DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/reactos/drivers/usb/usbstor/descriptor.c b/reactos/drivers/usb/usbstor/descriptor.c new file mode 100644 index 00000000000..28b7ead3332 --- /dev/null +++ b/reactos/drivers/usb/usbstor/descriptor.c @@ -0,0 +1,546 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbstor/descriptor.c + * PURPOSE: USB block storage device driver. + * PROGRAMMERS: + * James Tabor + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbstor.h" + +NTSTATUS +NTAPI +USBSTOR_GetDescriptor( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR DescriptorType, + IN ULONG DescriptorLength, + IN UCHAR DescriptorIndex, + IN LANGID LanguageId, + OUT PVOID *OutDescriptor) +{ + PURB Urb; + NTSTATUS Status; + PVOID Descriptor; + + // + // sanity checks + // + ASSERT(DeviceObject); + ASSERT(OutDescriptor); + ASSERT(DescriptorLength); + + // + // first allocate descriptor buffer + // + Descriptor = AllocateItem(NonPagedPool, DescriptorLength); + if (!Descriptor) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // allocate urb + // + Urb = (PURB) AllocateItem(NonPagedPool, sizeof(URB)); + if (!Urb) + { + // + // no memory + // + FreeItem(Descriptor); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize urb + // + UsbBuildGetDescriptorRequest(Urb, + sizeof(Urb->UrbControlDescriptorRequest), + DescriptorType, + DescriptorIndex, + LanguageId, + Descriptor, + NULL, + DescriptorLength, + NULL); + + // + // submit urb + // + Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb); + + // + // free urb + // + FreeItem(Urb); + + if (NT_SUCCESS(Status)) + { + // + // store result + // + *OutDescriptor = Descriptor; + } + + // + // done + // + return Status; +} + + +NTSTATUS +USBSTOR_GetDescriptors( + IN PDEVICE_OBJECT DeviceObject) +{ + NTSTATUS Status; + PFDO_DEVICE_EXTENSION DeviceExtension; + USHORT DescriptorLength; + + // + // get device extension + // + DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // first get device descriptor + // + Status = USBSTOR_GetDescriptor(DeviceExtension->LowerDeviceObject, USB_DEVICE_DESCRIPTOR_TYPE, sizeof(USB_DEVICE_DESCRIPTOR), 0, 0, (PVOID*)&DeviceExtension->DeviceDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to get device descriptor + // + DeviceExtension->DeviceDescriptor = NULL; + return Status; + } + + // + // now get basic configuration descriptor + // + Status = USBSTOR_GetDescriptor(DeviceExtension->LowerDeviceObject, USB_CONFIGURATION_DESCRIPTOR_TYPE, sizeof(USB_CONFIGURATION_DESCRIPTOR), 0, 0, (PVOID*)&DeviceExtension->ConfigurationDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to get configuration descriptor + // + FreeItem(DeviceExtension->DeviceDescriptor); + DeviceExtension->DeviceDescriptor = NULL; + return Status; + } + + // + // backup length + // + DescriptorLength = DeviceExtension->ConfigurationDescriptor->wTotalLength; + + // + // release basic descriptor + // + FreeItem(DeviceExtension->ConfigurationDescriptor); + DeviceExtension->ConfigurationDescriptor = NULL; + + // + // allocate full descriptor + // + Status = USBSTOR_GetDescriptor(DeviceExtension->LowerDeviceObject, USB_CONFIGURATION_DESCRIPTOR_TYPE, DescriptorLength, 0, 0, (PVOID*)&DeviceExtension->ConfigurationDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to get configuration descriptor + // + FreeItem(DeviceExtension->DeviceDescriptor); + DeviceExtension->DeviceDescriptor = NULL; + return Status; + } + + // + // check if there is a serial number provided + // + if (DeviceExtension->DeviceDescriptor->iSerialNumber) + { + // + // get serial number + // + Status = USBSTOR_GetDescriptor(DeviceExtension->LowerDeviceObject, USB_STRING_DESCRIPTOR_TYPE, 100 * sizeof(WCHAR), DeviceExtension->DeviceDescriptor->iSerialNumber, 0x0409, (PVOID*)&DeviceExtension->SerialNumber); + if (!NT_SUCCESS(Status)) + { + // + // failed to get serial number descriptor, free device descriptor + // + FreeItem(DeviceExtension->DeviceDescriptor); + DeviceExtension->DeviceDescriptor = NULL; + + // + // free configuration descriptor + // + FreeItem(DeviceExtension->ConfigurationDescriptor); + DeviceExtension->ConfigurationDescriptor = NULL; + + // + // set serial number to zero + // + DeviceExtension->SerialNumber = NULL; + return Status; + } + } + + return Status; +} + +NTSTATUS +NTAPI +USBSTOR_ScanConfigurationDescriptor( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, + OUT PUSB_INTERFACE_DESCRIPTOR * OutInterfaceDescriptor, + OUT PUSB_ENDPOINT_DESCRIPTOR * InEndpointDescriptor, + OUT PUSB_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor) +{ + PUSB_CONFIGURATION_DESCRIPTOR CurrentDescriptor; + PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + + // + // sanity checks + // + ASSERT(ConfigurationDescriptor); + ASSERT(OutInterfaceDescriptor); + ASSERT(InEndpointDescriptor); + ASSERT(OutEndpointDescriptor); + + // + // nullify pointers + // + *OutInterfaceDescriptor = NULL; + *InEndpointDescriptor = NULL; + *OutEndpointDescriptor = NULL; + + // + // start scanning + // + CurrentDescriptor = ConfigurationDescriptor; + + do + { + // + // check current descriptor type + // + if (CurrentDescriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE) + { + // + // found interface descriptor + // + if (*OutInterfaceDescriptor) + { + // + // we only process the first interface descriptor as ms does -> see documentation + // + break; + } + + // + // store interface descriptor + // + *OutInterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR)CurrentDescriptor; + } + else if (CurrentDescriptor->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE) + { + // + // convert to endpoint descriptor + // + EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)CurrentDescriptor; + + // + // sanity check + // + ASSERT(*OutInterfaceDescriptor); + + // + // get endpoint type + // + if ((EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) + { + // + // bulk endpoint type + // + if (USB_ENDPOINT_DIRECTION_IN(EndpointDescriptor->bEndpointAddress)) + { + // + // bulk in + // + *InEndpointDescriptor = EndpointDescriptor; + } + else + { + // + // bulk out + // + *OutEndpointDescriptor = EndpointDescriptor; + } + } + else if ((EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT) + { + // + // interrupt endpoint type + // + UNIMPLEMENTED + } + } + + // + // move to next descriptor + // + CurrentDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)((ULONG_PTR)CurrentDescriptor + CurrentDescriptor->bLength); + + // + // was it the last descriptor + // + if ((ULONG_PTR)CurrentDescriptor >= ((ULONG_PTR)ConfigurationDescriptor + ConfigurationDescriptor->wTotalLength)) + { + // + // reached last descriptor + // + break; + } + + }while(TRUE); + + // + // check if everything has been found + // + if (*OutInterfaceDescriptor == NULL || *InEndpointDescriptor == NULL || *OutEndpointDescriptor == NULL) + { + // + // failed to find interface / endpoint descriptor + // + DPRINT1("USBSTOR_ScanConfigurationDescriptor: Failed to find InterfaceDescriptor %p InEndpointDescriptor %p OutEndpointDescriptor %p\n", *OutInterfaceDescriptor, *InEndpointDescriptor, *OutEndpointDescriptor); + return STATUS_UNSUCCESSFUL; + } + + // + // completed successfully + // + return STATUS_SUCCESS; +} + +VOID +DumpConfigurationDescriptor(PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor) +{ + DPRINT1("Dumping ConfigurationDescriptor %x\n", ConfigurationDescriptor); + DPRINT1("bLength %x\n", ConfigurationDescriptor->bLength); + DPRINT1("bDescriptorType %x\n", ConfigurationDescriptor->bDescriptorType); + DPRINT1("wTotalLength %x\n", ConfigurationDescriptor->wTotalLength); + DPRINT1("bNumInterfaces %x\n", ConfigurationDescriptor->bNumInterfaces); + DPRINT1("bConfigurationValue %x\n", ConfigurationDescriptor->bConfigurationValue); + DPRINT1("iConfiguration %x\n", ConfigurationDescriptor->iConfiguration); + DPRINT1("bmAttributes %x\n", ConfigurationDescriptor->bmAttributes); + DPRINT1("MaxPower %x\n", ConfigurationDescriptor->MaxPower); +} + +NTSTATUS +USBSTOR_SelectConfigurationAndInterface( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension) +{ + PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + PUSB_ENDPOINT_DESCRIPTOR InEndpointDescriptor, OutEndpointDescriptor; + NTSTATUS Status; + PURB Urb; + PUSBD_INTERFACE_LIST_ENTRY InterfaceList; + + // + // now scan configuration descriptors + // + Status = USBSTOR_ScanConfigurationDescriptor(DeviceExtension->ConfigurationDescriptor, &InterfaceDescriptor, &InEndpointDescriptor, &OutEndpointDescriptor); + if (!NT_SUCCESS(Status)) + { + // + // failed to scan + // + return Status; + } + + // + // now allocate one interface entry and terminating null entry + // + InterfaceList = (PUSBD_INTERFACE_LIST_ENTRY)AllocateItem(PagedPool, sizeof(USBD_INTERFACE_LIST_ENTRY) * 2); + if (!InterfaceList) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize interface list entry + // + InterfaceList[0].InterfaceDescriptor = InterfaceDescriptor; + + // + // now allocate the urb + // + Urb = USBD_CreateConfigurationRequestEx(DeviceExtension->ConfigurationDescriptor, InterfaceList); + if (!Urb) + { + // + // no memory + // + FreeItem(InterfaceList); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // sanity check + // + ASSERT(InterfaceList[0].Interface); + + // + // submit urb + // + Status = USBSTOR_SyncUrbRequest(DeviceExtension->LowerDeviceObject, Urb); + if (!NT_SUCCESS(Status)) + { + // + // failed to set configuration + // + DPRINT1("USBSTOR_SelectConfiguration failed to set interface %x\n", Status); + FreeItem(InterfaceList); + ExFreePool(Urb); + return Status; + } + + // + // backup interface information + // + DeviceExtension->InterfaceInformation = (PUSBD_INTERFACE_INFORMATION)AllocateItem(NonPagedPool, Urb->UrbSelectConfiguration.Interface.Length); + if (!NT_SUCCESS(Status)) + { + // + // failed to allocate interface information structure + // + FreeItem(InterfaceList); + ExFreePool(Urb); + return Status; + } + + // + // copy interface information + // + RtlCopyMemory(DeviceExtension->InterfaceInformation, &Urb->UrbSelectConfiguration.Interface, Urb->UrbSelectConfiguration.Interface.Length); + + // + // store pipe handle + // + DeviceExtension->ConfigurationHandle = Urb->UrbSelectConfiguration.ConfigurationHandle; + + // + // now prepare interface urb + // + UsbBuildSelectInterfaceRequest(Urb, GET_SELECT_INTERFACE_REQUEST_SIZE(InterfaceDescriptor->bNumEndpoints), DeviceExtension->ConfigurationHandle, InterfaceDescriptor->bInterfaceNumber, InterfaceDescriptor->bAlternateSetting); + + // + // copy interface information structure back - as offset for SelectConfiguration / SelectInterface request do differ + // + RtlCopyMemory(&Urb->UrbSelectInterface.Interface, DeviceExtension->InterfaceInformation, DeviceExtension->InterfaceInformation->Length); + + // + // now select the interface + // + Status = USBSTOR_SyncUrbRequest(DeviceExtension->LowerDeviceObject, Urb); + + // + // did it succeeed + // + if (NT_SUCCESS(Status)) + { + // + // update configuration info + // + ASSERT(Urb->UrbSelectInterface.Interface.Length == DeviceExtension->InterfaceInformation->Length); + RtlCopyMemory(DeviceExtension->InterfaceInformation, &Urb->UrbSelectInterface.Interface, Urb->UrbSelectInterface.Interface.Length); + } + + // + // free interface list & urb + // + FreeItem(InterfaceList); + ExFreePool(Urb); + + // + // done + // + return Status; +} + +NTSTATUS +USBSTOR_GetPipeHandles( + IN PFDO_DEVICE_EXTENSION DeviceExtension) +{ + ULONG Index; + BOOLEAN BulkInFound = FALSE, BulkOutFound = FALSE; + + // + // no enumerate all pipes and extract bulk-in / bulk-out pipe handle + // + for(Index = 0; Index < DeviceExtension->InterfaceInformation->NumberOfPipes; Index++) + { + // + // check pipe type + // + if (DeviceExtension->InterfaceInformation->Pipes[Index].PipeType == UsbdPipeTypeBulk) + { + // + // check direction + // + if (USB_ENDPOINT_DIRECTION_IN(DeviceExtension->InterfaceInformation->Pipes[Index].EndpointAddress)) + { + // + // bulk in pipe + // + DeviceExtension->BulkInPipeIndex = Index; + + // + // there should not be another bulk in pipe + // + ASSERT(BulkInFound == FALSE); + BulkInFound = TRUE; + } + else + { + // + // bulk out pipe + // + DeviceExtension->BulkOutPipeIndex = Index; + + // + // there should not be another bulk out pipe + // + ASSERT(BulkOutFound == FALSE); + BulkOutFound = TRUE; + } + } + } + + // + // check if both bulk pipes have been found + // + if (!BulkInFound || !BulkOutFound) + { + // + // WTF? usb port driver does not give us bulk pipe access + // + DPRINT1("USBSTOR_GetPipeHandles> BulkInFound %d BulkOutFound %d missing!!!\n", BulkInFound, BulkOutFound); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + // + // device is configured + // + return STATUS_SUCCESS; +} \ No newline at end of file diff --git a/reactos/drivers/usb/usbstor/disk.c b/reactos/drivers/usb/usbstor/disk.c new file mode 100644 index 00000000000..8a11d828258 --- /dev/null +++ b/reactos/drivers/usb/usbstor/disk.c @@ -0,0 +1,684 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbstor/disk.c + * PURPOSE: USB block storage device driver. + * PROGRAMMERS: + * James Tabor + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbstor.h" + +NTSTATUS +USBSTOR_HandleInternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PSCSI_REQUEST_BLOCK Request; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + NTSTATUS Status; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get request block + // + Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1; + + // + // sanity check + // + ASSERT(Request); + + // + // get device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + switch(Request->Function) + { + case SRB_FUNCTION_EXECUTE_SCSI: + { + DPRINT("SRB_FUNCTION_EXECUTE_SCSI\n"); + + // + // check if request is valid + // + if (Request->SrbFlags & (SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT)) + { + // + // data is transferred with this irp + // + if ((Request->SrbFlags & (SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT)) == (SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT) || + Request->DataTransferLength == 0 || + Irp->MdlAddress == NULL) + { + // + // invalid parameter + // + Status = STATUS_INVALID_PARAMETER; + break; + } + } + else + { + // + // sense buffer request + // + if (Request->DataTransferLength || + Request->DataBuffer || + Irp->MdlAddress) + { + // + // invalid parameter + // + Status = STATUS_INVALID_PARAMETER; + break; + } + } + + // + // add the request + // + if (!USBSTOR_QueueAddIrp(PDODeviceExtension->LowerDeviceObject, Irp)) + { + // + // irp was not added to the queue + // + IoStartPacket(PDODeviceExtension->LowerDeviceObject, Irp, &Request->QueueSortKey, USBSTOR_CancelIo); + } + + // + // irp pending + // + return STATUS_PENDING; + } + case SRB_FUNCTION_RELEASE_DEVICE: + { + DPRINT1("SRB_FUNCTION_RELEASE_DEVICE\n"); + // + // sanity check + // + ASSERT(PDODeviceExtension->Claimed == TRUE); + + // + // release claim + // + PDODeviceExtension->Claimed = FALSE; + Status = STATUS_SUCCESS; + break; + } + case SRB_FUNCTION_CLAIM_DEVICE: + { + DPRINT1("SRB_FUNCTION_CLAIM_DEVICE\n"); + // + // check if the device has been claimed + // + if (PDODeviceExtension->Claimed) + { + // + // device has already been claimed + // + Status = STATUS_DEVICE_BUSY; + Request->SrbStatus = SRB_STATUS_BUSY; + break; + } + + // + // claim device + // + PDODeviceExtension->Claimed = TRUE; + + // + // output device object + // + Request->DataBuffer = DeviceObject; + + // + // completed successfully + // + Status = STATUS_SUCCESS; + break; + } + case SRB_FUNCTION_RELEASE_QUEUE: + { + DPRINT1("SRB_FUNCTION_RELEASE_QUEUE\n"); + + // + // release queue + // + USBSTOR_QueueRelease(PDODeviceExtension->LowerDeviceObject); + + // + // set status success + // + Request->SrbStatus = SRB_STATUS_SUCCESS; + Status = STATUS_SUCCESS; + break; + } + + case SRB_FUNCTION_SHUTDOWN: + case SRB_FUNCTION_FLUSH: + case SRB_FUNCTION_FLUSH_QUEUE: + { + DPRINT1("SRB_FUNCTION_FLUSH / SRB_FUNCTION_FLUSH_QUEUE / SRB_FUNCTION_SHUTDOWN\n"); + + // + // wait for pending requests to finish + // + USBSTOR_QueueWaitForPendingRequests(PDODeviceExtension->LowerDeviceObject); + + // + // set status success + // + Request->SrbStatus = SRB_STATUS_SUCCESS; + Status = STATUS_SUCCESS; + break; + } + default: + { + // + // not supported + // + Status = STATUS_NOT_SUPPORTED; + Request->SrbStatus = SRB_STATUS_ERROR; + } + } + + // + // complete request + // + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Status; +} + +ULONG +USBSTOR_GetFieldLength( + IN PUCHAR Name, + IN ULONG MaxLength) +{ + ULONG Index; + ULONG LastCharacterPosition = 0; + + // + // scan the field and return last positon which contains a valid character + // + for(Index = 0; Index < MaxLength; Index++) + { + if (Name[Index] != ' ') + { + // + // trim white spaces from field + // + LastCharacterPosition = Index; + } + } + + // + // convert from zero based index to length + // + return LastCharacterPosition + 1; +} + +NTSTATUS +USBSTOR_HandleQueryProperty( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PSTORAGE_PROPERTY_QUERY PropertyQuery; + PSTORAGE_DESCRIPTOR_HEADER DescriptorHeader; + PSTORAGE_ADAPTER_DESCRIPTOR AdapterDescriptor; + ULONG FieldLengthVendor, FieldLengthProduct, FieldLengthRevision, TotalLength, FieldLengthSerialNumber; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PUFI_INQUIRY_RESPONSE InquiryData; + PSTORAGE_DEVICE_DESCRIPTOR DeviceDescriptor; + PUCHAR Buffer; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + UNICODE_STRING SerialNumber; + ANSI_STRING AnsiString; + NTSTATUS Status; + + DPRINT("USBSTOR_HandleQueryProperty\n"); + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // sanity check + // + ASSERT(IoStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(STORAGE_PROPERTY_QUERY)); + ASSERT(Irp->AssociatedIrp.SystemBuffer); + + // + // get property query + // + PropertyQuery = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer; + + // + // check property type + // + if (PropertyQuery->PropertyId != StorageDeviceProperty && + PropertyQuery->PropertyId != StorageAdapterProperty) + { + // + // only device property / adapter property are supported + // + return STATUS_INVALID_PARAMETER_1; + } + + // + // check query type + // + if (PropertyQuery->QueryType == PropertyExistsQuery) + { + // + // device property / adapter property is supported + // + return STATUS_SUCCESS; + } + + if (PropertyQuery->QueryType != PropertyStandardQuery) + { + // + // only standard query and exists query are supported + // + return STATUS_INVALID_PARAMETER_2; + } + + // + // check if it is a device property + // + if (PropertyQuery->PropertyId == StorageDeviceProperty) + { + DPRINT("USBSTOR_HandleQueryProperty StorageDeviceProperty OutputBufferLength %lu\n", IoStack->Parameters.DeviceIoControl.OutputBufferLength); + + // + // get device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(PDODeviceExtension); + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + // + // get device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension; + ASSERT(FDODeviceExtension); + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // get inquiry data + // + InquiryData = (PUFI_INQUIRY_RESPONSE)PDODeviceExtension->InquiryData; + ASSERT(InquiryData); + + // + // compute extra parameters length + // + FieldLengthVendor = USBSTOR_GetFieldLength(InquiryData->Vendor, 8); + FieldLengthProduct = USBSTOR_GetFieldLength(InquiryData->Product, 16); + FieldLengthRevision = USBSTOR_GetFieldLength(InquiryData->Revision, 4); + + // + // is there a serial number + // + if (FDODeviceExtension->SerialNumber) + { + // + // get length + // + FieldLengthSerialNumber = wcslen(FDODeviceExtension->SerialNumber->bString); + } + else + { + // + // no serial number + // + FieldLengthSerialNumber = 0; + } + + // + // total length required is sizeof(STORAGE_DEVICE_DESCRIPTOR) + FieldLength + 4 extra null bytes - 1 + // -1 due STORAGE_DEVICE_DESCRIPTOR contains one byte length of parameter data + // + TotalLength = sizeof(STORAGE_DEVICE_DESCRIPTOR) + FieldLengthVendor + FieldLengthProduct + FieldLengthRevision + FieldLengthSerialNumber + 3; + + // + // check if output buffer is long enough + // + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < TotalLength) + { + // + // buffer too small + // + DescriptorHeader = (PSTORAGE_DESCRIPTOR_HEADER)Irp->AssociatedIrp.SystemBuffer; + ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(STORAGE_DESCRIPTOR_HEADER)); + + // + // return required size + // + DescriptorHeader->Version = TotalLength; + DescriptorHeader->Size = TotalLength; + + Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER); + return STATUS_SUCCESS; + } + + // + // get device descriptor + // + DeviceDescriptor = (PSTORAGE_DEVICE_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer; + + // + // initialize device descriptor + // + DeviceDescriptor->Version = TotalLength; + DeviceDescriptor->Size = TotalLength; + DeviceDescriptor->DeviceType = InquiryData->DeviceType; + DeviceDescriptor->DeviceTypeModifier = (InquiryData->RMB & 0x7F); + DeviceDescriptor->RemovableMedia = (InquiryData->RMB & 0x80) ? TRUE : FALSE; + DeviceDescriptor->CommandQueueing = FALSE; + DeviceDescriptor->BusType = BusTypeUsb; + DeviceDescriptor->VendorIdOffset = sizeof(STORAGE_DEVICE_DESCRIPTOR) - sizeof(UCHAR); + DeviceDescriptor->ProductIdOffset = DeviceDescriptor->VendorIdOffset + FieldLengthVendor + 1; + DeviceDescriptor->ProductRevisionOffset = DeviceDescriptor->ProductIdOffset + FieldLengthProduct + 1; + DeviceDescriptor->SerialNumberOffset = (FieldLengthSerialNumber > 0 ? DeviceDescriptor->ProductRevisionOffset + FieldLengthRevision + 1 : 0); + DeviceDescriptor->RawPropertiesLength = FieldLengthVendor + FieldLengthProduct + FieldLengthRevision + FieldLengthSerialNumber + 3 + (FieldLengthSerialNumber > 0 ? + 1 : 0); + + // + // copy descriptors + // + Buffer = (PUCHAR)((ULONG_PTR)DeviceDescriptor + sizeof(STORAGE_DEVICE_DESCRIPTOR) - sizeof(UCHAR)); + + // + // copy vendor + // + RtlCopyMemory(Buffer, InquiryData->Vendor, FieldLengthVendor); + Buffer[FieldLengthVendor] = '\0'; + Buffer += FieldLengthVendor + 1; + + // + // copy product + // + RtlCopyMemory(Buffer, InquiryData->Product, FieldLengthProduct); + Buffer[FieldLengthProduct] = '\0'; + Buffer += FieldLengthProduct + 1; + + // + // copy revision + // + RtlCopyMemory(Buffer, InquiryData->Revision, FieldLengthRevision); + Buffer[FieldLengthRevision] = '\0'; + Buffer += FieldLengthRevision + 1; + + // + // copy serial number + // + if (FieldLengthSerialNumber) + { + // + // init unicode string + // + RtlInitUnicodeString(&SerialNumber, FDODeviceExtension->SerialNumber->bString); + + // + // init ansi string + // + AnsiString.Buffer = (PCHAR)Buffer; + AnsiString.Length = 0; + AnsiString.MaximumLength = FieldLengthSerialNumber * sizeof(WCHAR); + + // + // convert to ansi code + // + Status = RtlUnicodeStringToAnsiString(&AnsiString, &SerialNumber, FALSE); + ASSERT(Status == STATUS_SUCCESS); + } + + + DPRINT("Vendor %s\n", (LPCSTR)((ULONG_PTR)DeviceDescriptor + DeviceDescriptor->VendorIdOffset)); + DPRINT("Product %s\n", (LPCSTR)((ULONG_PTR)DeviceDescriptor + DeviceDescriptor->ProductIdOffset)); + DPRINT("Revision %s\n", (LPCSTR)((ULONG_PTR)DeviceDescriptor + DeviceDescriptor->ProductRevisionOffset)); + DPRINT("Serial %s\n", (LPCSTR)((ULONG_PTR)DeviceDescriptor + DeviceDescriptor->SerialNumberOffset)); + + // + // done + // + Irp->IoStatus.Information = TotalLength; + return STATUS_SUCCESS; + } + else + { + // + // adapter property query request + // + DPRINT("USBSTOR_HandleQueryProperty StorageAdapterProperty OutputBufferLength %lu\n", IoStack->Parameters.DeviceIoControl.OutputBufferLength); + + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_ADAPTER_DESCRIPTOR)) + { + // + // buffer too small + // + DescriptorHeader = (PSTORAGE_DESCRIPTOR_HEADER)Irp->AssociatedIrp.SystemBuffer; + ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(STORAGE_DESCRIPTOR_HEADER)); + + // + // return required size + // + DescriptorHeader->Version = sizeof(STORAGE_ADAPTER_DESCRIPTOR); + DescriptorHeader->Size = sizeof(STORAGE_ADAPTER_DESCRIPTOR); + + Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER); + return STATUS_SUCCESS; + } + + // + // get adapter descriptor, information is returned in the same buffer + // + AdapterDescriptor = (PSTORAGE_ADAPTER_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer; + + // + // fill out descriptor + // + AdapterDescriptor->Version = sizeof(STORAGE_ADAPTER_DESCRIPTOR); + AdapterDescriptor->Size = sizeof(STORAGE_ADAPTER_DESCRIPTOR); + AdapterDescriptor->MaximumTransferLength = MAXULONG; //FIXME compute some sane value + AdapterDescriptor->MaximumPhysicalPages = 25; //FIXME compute some sane value + AdapterDescriptor->AlignmentMask = 0; + AdapterDescriptor->AdapterUsesPio = FALSE; + AdapterDescriptor->AdapterScansDown = FALSE; + AdapterDescriptor->CommandQueueing = FALSE; + AdapterDescriptor->AcceleratedTransfer = FALSE; + AdapterDescriptor->BusType = BusTypeUsb; + AdapterDescriptor->BusMajorVersion = 0x2; //FIXME verify + AdapterDescriptor->BusMinorVersion = 0x00; //FIXME + + // + // store returned length + // + Irp->IoStatus.Information = sizeof(STORAGE_ADAPTER_DESCRIPTOR); + + // + // done + // + return STATUS_SUCCESS; + } +} + +NTSTATUS +USBSTOR_HandleDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PSCSI_ADAPTER_BUS_INFO BusInfo; + PSCSI_INQUIRY_DATA InquiryData; + PINQUIRYDATA ScsiInquiryData; + PUFI_INQUIRY_RESPONSE UFIInquiryResponse; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_QUERY_PROPERTY) + { + // + // query property + // + Status = USBSTOR_HandleQueryProperty(DeviceObject, Irp); + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH) + { + // + // query scsi pass through + // + DPRINT1("USBSTOR_HandleDeviceControl IOCTL_SCSI_PASS_THROUGH NOT implemented\n"); + Status = STATUS_NOT_SUPPORTED; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) + { + // + // query scsi pass through direct + // + DPRINT1("USBSTOR_HandleDeviceControl IOCTL_SCSI_PASS_THROUGH_DIRECT NOT implemented\n"); + Status = STATUS_NOT_SUPPORTED; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER) + { + // + // query serial number + // + DPRINT1("USBSTOR_HandleDeviceControl IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER NOT implemented\n"); + Status = STATUS_NOT_SUPPORTED; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_GET_CAPABILITIES) + { + PIO_SCSI_CAPABILITIES Capabilities; + + /* Legacy port capability query */ + if (IoStack->Parameters.DeviceIoControl.OutputBufferLength == sizeof(PVOID)) + { + Capabilities = *((PVOID *)Irp->AssociatedIrp.SystemBuffer) = ExAllocatePool(NonPagedPool, sizeof(IO_SCSI_CAPABILITIES)); + Irp->IoStatus.Information = sizeof(PVOID); + } + else + { + Capabilities = Irp->AssociatedIrp.SystemBuffer; + Irp->IoStatus.Information = sizeof(IO_SCSI_CAPABILITIES); + } + + if (Capabilities) + { + Capabilities->MaximumTransferLength = MAXULONG; + Capabilities->MaximumPhysicalPages = 25; + Capabilities->SupportedAsynchronousEvents = 0; + Capabilities->AlignmentMask = 0; + Capabilities->TaggedQueuing = FALSE; + Capabilities->AdapterScansDown = FALSE; + Capabilities->AdapterUsesPio = FALSE; + Status = STATUS_SUCCESS; + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_GET_INQUIRY_DATA) + { + // + // get device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(PDODeviceExtension); + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + // + // get parameters + // + BusInfo = Irp->AssociatedIrp.SystemBuffer; + InquiryData = (PSCSI_INQUIRY_DATA)(BusInfo + 1); + ScsiInquiryData = (PINQUIRYDATA)InquiryData->InquiryData; + + + // + // get inquiry data + // + UFIInquiryResponse = (PUFI_INQUIRY_RESPONSE)PDODeviceExtension->InquiryData; + ASSERT(UFIInquiryResponse); + + + BusInfo->NumberOfBuses = 1; + BusInfo->BusData[0].NumberOfLogicalUnits = 1; //FIXME + BusInfo->BusData[0].InitiatorBusId = 0; + BusInfo->BusData[0].InquiryDataOffset = sizeof(SCSI_ADAPTER_BUS_INFO); + + InquiryData->PathId = 0; + InquiryData->TargetId = 0; + InquiryData->Lun = PDODeviceExtension->LUN & MAX_LUN; + InquiryData->DeviceClaimed = PDODeviceExtension->Claimed; + InquiryData->InquiryDataLength = sizeof(INQUIRYDATA); + InquiryData->NextInquiryDataOffset = 0; + + RtlZeroMemory(ScsiInquiryData, sizeof(INQUIRYDATA)); + ScsiInquiryData->DeviceType = UFIInquiryResponse->DeviceType; + ScsiInquiryData->DeviceTypeQualifier = (UFIInquiryResponse->RMB & 0x7F); + + /* Hack for IoReadPartitionTable call in disk.sys */ + ScsiInquiryData->RemovableMedia = ((ScsiInquiryData->DeviceType != DIRECT_ACCESS_DEVICE) ? ((UFIInquiryResponse->RMB & 0x80) ? 1 : 0) : 0); + + ScsiInquiryData->Versions = 0x04; + ScsiInquiryData->ResponseDataFormat = 0x02; + ScsiInquiryData->AdditionalLength = 31; + ScsiInquiryData->SoftReset = 0; + ScsiInquiryData->CommandQueue = 0; + ScsiInquiryData->LinkedCommands = 0; + ScsiInquiryData->RelativeAddressing = 0; + + RtlCopyMemory(&ScsiInquiryData->VendorId, UFIInquiryResponse->Vendor, USBSTOR_GetFieldLength(UFIInquiryResponse->Vendor, 8)); + RtlCopyMemory(&ScsiInquiryData->ProductId, UFIInquiryResponse->Product, USBSTOR_GetFieldLength(UFIInquiryResponse->Product, 16)); + + Irp->IoStatus.Information = sizeof(SCSI_ADAPTER_BUS_INFO) + sizeof(SCSI_INQUIRY_DATA) + sizeof(INQUIRYDATA) - 1; + Status = STATUS_SUCCESS; + } + else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_GET_ADDRESS) + { + PSCSI_ADDRESS Address = Irp->AssociatedIrp.SystemBuffer; + + Address->Length = sizeof(SCSI_ADDRESS); + Address->PortNumber = 0; + Address->PathId = 0; + Address->TargetId = 0; + Address->Lun = (((PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->LUN & MAX_LUN); + Irp->IoStatus.Information = sizeof(SCSI_ADDRESS); + + Status = STATUS_SUCCESS; + } + else + { + // + // unsupported + // + DPRINT("USBSTOR_HandleDeviceControl IoControl %x not supported\n", IoStack->Parameters.DeviceIoControl.IoControlCode); + Status = STATUS_NOT_SUPPORTED; + } + + return Status; +} diff --git a/reactos/drivers/usb/usbstor/error.c b/reactos/drivers/usb/usbstor/error.c new file mode 100644 index 00000000000..23d7f88c671 --- /dev/null +++ b/reactos/drivers/usb/usbstor/error.c @@ -0,0 +1,227 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbstor/error.c + * PURPOSE: USB block storage device driver. + * PROGRAMMERS: + * James Tabor + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbstor.h" + +NTSTATUS +USBSTOR_GetEndpointStatus( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR bEndpointAddress, + OUT PUSHORT Value) +{ + PURB Urb; + NTSTATUS Status; + + // + // allocate urb + // + DPRINT("Allocating URB\n"); + Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST)); + if (!Urb) + { + // + // out of memory + // + DPRINT1("OutofMemory!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build status + // + UsbBuildGetStatusRequest(Urb, URB_FUNCTION_GET_STATUS_FROM_ENDPOINT, bEndpointAddress & 0x0F, Value, NULL, NULL); + + // + // send the request + // + DPRINT1("Sending Request DeviceObject %x, Urb %x\n", DeviceObject, Urb); + Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb); + + // + // free urb + // + FreeItem(Urb); + + // + // done + // + return Status; +} + + + +NTSTATUS +USBSTOR_ResetPipeWithHandle( + IN PDEVICE_OBJECT DeviceObject, + IN USBD_PIPE_HANDLE PipeHandle) +{ + PURB Urb; + NTSTATUS Status; + + // + // allocate urb + // + DPRINT("Allocating URB\n"); + Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST)); + if (!Urb) + { + // + // out of memory + // + DPRINT1("OutofMemory!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize the urb + // + Urb->UrbPipeRequest.Hdr.Length = sizeof(struct _URB_PIPE_REQUEST); + Urb->UrbPipeRequest.Hdr.Function = URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL; + Urb->UrbPipeRequest.PipeHandle = PipeHandle; + + // + // send the request + // + DPRINT1("Sending Request DeviceObject %x, Urb %x\n", DeviceObject, Urb); + Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb); + + // + // free urb + // + FreeItem(Urb); + + // + // done + // + return Status; +} + + +NTSTATUS +USBSTOR_HandleTransferError( + PDEVICE_OBJECT DeviceObject, + PIRP_CONTEXT Context) +{ + NTSTATUS Status; + PIO_STACK_LOCATION Stack; + USBD_PIPE_HANDLE PipeHandle; + PSCSI_REQUEST_BLOCK Request; + PCDB pCDB; + + DPRINT1("Entered Handle Transfer Error\n"); + // + // Determine pipehandle + // + if (Context->cbw->CommandBlock[0] == SCSIOP_WRITE) + { + // + // write request used bulk out pipe + // + PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle; + } + else + { + // + // default bulk in pipe + // + PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle; + } + + switch (Context->Urb.UrbHeader.Status) + { + case USBD_STATUS_STALL_PID: + { + // + // First attempt to reset the pipe + // + DPRINT1("Resetting Pipe\n"); + Status = USBSTOR_ResetPipeWithHandle(Context->FDODeviceExtension->LowerDeviceObject, PipeHandle); + if (NT_SUCCESS(Status)) + { + Status = STATUS_SUCCESS; + break; + } + + DPRINT1("Failed to reset pipe %x\n", Status); + + // + // FIXME: Reset of pipe failed, attempt to reset port + // + + Status = STATUS_UNSUCCESSFUL; + break; + } + // + // FIXME: Handle more errors + // + default: + { + DPRINT1("Error not handled\n"); + Status = STATUS_UNSUCCESSFUL; + } + } + + Stack = IoGetCurrentIrpStackLocation(Context->Irp); + Request = (PSCSI_REQUEST_BLOCK)Stack->Parameters.Others.Argument1; + pCDB = (PCDB)Request->Cdb; + if (Status != STATUS_SUCCESS) + { + /* Complete the master IRP */ + Context->Irp->IoStatus.Status = Status; + Context->Irp->IoStatus.Information = 0; + USBSTOR_QueueTerminateRequest(Context->PDODeviceExtension->LowerDeviceObject, Context->Irp); + IoCompleteRequest(Context->Irp, IO_NO_INCREMENT); + + /* Start the next request */ + USBSTOR_QueueNextRequest(Context->PDODeviceExtension->LowerDeviceObject); + + /* Signal the context event */ + if (Context->Event) + KeSetEvent(Context->Event, 0, FALSE); + + /* Cleanup the IRP context */ + if (pCDB->AsByte[0] == SCSIOP_READ_CAPACITY) + FreeItem(Context->TransferData); + FreeItem(Context->cbw); + FreeItem(Context); + } + else + { + + DPRINT1("Retrying\n"); + Status = USBSTOR_HandleExecuteSCSI(*Context->PDODeviceExtension->PDODeviceObject, Context->Irp); + + /* Cleanup the old IRP context */ + if (pCDB->AsByte[0] == SCSIOP_READ_CAPACITY) + FreeItem(Context->TransferData); + FreeItem(Context->cbw); + FreeItem(Context); + } + + DPRINT1("USBSTOR_HandleTransferError returning with Status %x\n", Status); + return Status; +} + +VOID +NTAPI +ErrorHandlerWorkItemRoutine( + PVOID Context) +{ + NTSTATUS Status; + PERRORHANDLER_WORKITEM_DATA WorkItemData = (PERRORHANDLER_WORKITEM_DATA)Context; + + Status = USBSTOR_HandleTransferError(WorkItemData->DeviceObject, WorkItemData->Context); + + // + // Free Work Item Data + // + ExFreePool(WorkItemData); +} diff --git a/reactos/drivers/usb/usbstor/fdo.c b/reactos/drivers/usb/usbstor/fdo.c new file mode 100644 index 00000000000..56ad612ad41 --- /dev/null +++ b/reactos/drivers/usb/usbstor/fdo.c @@ -0,0 +1,419 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbstor/fdo.c + * PURPOSE: USB block storage device driver. + * PROGRAMMERS: + * James Tabor + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbstor.h" + +VOID +USBSTOR_DumpDeviceDescriptor(PUSB_DEVICE_DESCRIPTOR DeviceDescriptor) +{ + DPRINT1("Dumping Device Descriptor %x\n", DeviceDescriptor); + DPRINT1("bLength %x\n", DeviceDescriptor->bLength); + DPRINT1("bDescriptorType %x\n", DeviceDescriptor->bDescriptorType); + DPRINT1("bcdUSB %x\n", DeviceDescriptor->bcdUSB); + DPRINT1("bDeviceClass %x\n", DeviceDescriptor->bDeviceClass); + DPRINT1("bDeviceSubClass %x\n", DeviceDescriptor->bDeviceSubClass); + DPRINT1("bDeviceProtocol %x\n", DeviceDescriptor->bDeviceProtocol); + DPRINT1("bMaxPacketSize0 %x\n", DeviceDescriptor->bMaxPacketSize0); + DPRINT1("idVendor %x\n", DeviceDescriptor->idVendor); + DPRINT1("idProduct %x\n", DeviceDescriptor->idProduct); + DPRINT1("bcdDevice %x\n", DeviceDescriptor->bcdDevice); + DPRINT1("iManufacturer %x\n", DeviceDescriptor->iManufacturer); + DPRINT1("iProduct %x\n", DeviceDescriptor->iProduct); + DPRINT1("iSerialNumber %x\n", DeviceDescriptor->iSerialNumber); + DPRINT1("bNumConfigurations %x\n", DeviceDescriptor->bNumConfigurations); +} + +NTSTATUS +USBSTOR_FdoHandleDeviceRelations( + IN PFDO_DEVICE_EXTENSION DeviceExtension, + IN OUT PIRP Irp) +{ + ULONG DeviceCount = 0; + ULONG Index; + PDEVICE_RELATIONS DeviceRelations; + PIO_STACK_LOCATION IoStack; + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // check if relation type is BusRelations + // + if (IoStack->Parameters.QueryDeviceRelations.Type != BusRelations) + { + // + // FDO always only handles bus relations + // + return USBSTOR_SyncForwardIrp(DeviceExtension->LowerDeviceObject, Irp); + } + + // + // go through array and count device objects + // + for(Index = 0; Index < max(DeviceExtension->MaxLUN, 1); Index++) + { + if (DeviceExtension->ChildPDO[Index]) + { + // + // child pdo + // + DeviceCount++; + } + } + + // + // allocate device relations + // + DeviceRelations = (PDEVICE_RELATIONS)AllocateItem(PagedPool, sizeof(DEVICE_RELATIONS) + (DeviceCount > 1 ? (DeviceCount-1) * sizeof(PDEVICE_OBJECT) : 0)); + if (!DeviceRelations) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // add device objects + // + for(Index = 0; Index < max(DeviceExtension->MaxLUN, 1); Index++) + { + if (DeviceExtension->ChildPDO[Index]) + { + // + // store child pdo + // + DeviceRelations->Objects[DeviceRelations->Count] = DeviceExtension->ChildPDO[Index]; + + // + // add reference + // + ObReferenceObject(DeviceExtension->ChildPDO[Index]); + + // + // increment count + // + DeviceRelations->Count++; + } + } + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + + // + // request completed successfully + // + return STATUS_SUCCESS; +} + +NTSTATUS +USBSTOR_FdoHandleRemoveDevice( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension, + IN OUT PIRP Irp) +{ + NTSTATUS Status; + + DPRINT("Handling FDO removal\n"); + + /* We don't need to request removal of our children here */ + + /* Send the IRP down the stack */ + IoSkipCurrentIrpStackLocation(Irp); + Status = IoCallDriver(DeviceExtension->LowerDeviceObject, Irp); + + /* Detach from the device stack */ + IoDetachDevice(DeviceExtension->LowerDeviceObject); + + /* Delete the device object */ + IoDeleteDevice(DeviceObject); + + return Status; +} + +NTSTATUS +USBSTOR_FdoHandleStartDevice( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension, + IN OUT PIRP Irp) +{ + PUSB_INTERFACE_DESCRIPTOR InterfaceDesc; + NTSTATUS Status; + UCHAR Index = 0; + + // + // forward irp to lower device + // + Status = USBSTOR_SyncForwardIrp(DeviceExtension->LowerDeviceObject, Irp); + if (!NT_SUCCESS(Status)) + { + // + // failed to start + // + DPRINT1("USBSTOR_FdoHandleStartDevice Lower device failed to start %x\n", Status); + return Status; + } + + // + // intialize irp queue + // + USBSTOR_QueueInitialize(DeviceExtension); + + // + // first get device & configuration & string descriptor + // + Status = USBSTOR_GetDescriptors(DeviceObject); + if (!NT_SUCCESS(Status)) + { + // + // failed to get device descriptor + // + DPRINT1("USBSTOR_FdoHandleStartDevice failed to get device descriptor with %x\n", Status); + return Status; + } + + // + // dump device descriptor + // + USBSTOR_DumpDeviceDescriptor(DeviceExtension->DeviceDescriptor); + + // + // Check that this device uses bulk transfers and is SCSI + // + InterfaceDesc = (PUSB_INTERFACE_DESCRIPTOR)((ULONG_PTR)DeviceExtension->ConfigurationDescriptor + sizeof(USB_CONFIGURATION_DESCRIPTOR)); + + // + // sanity check + // + ASSERT(InterfaceDesc->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE); + ASSERT(InterfaceDesc->bLength == sizeof(USB_INTERFACE_DESCRIPTOR)); + + DPRINT("bInterfaceSubClass %x\n", InterfaceDesc->bInterfaceSubClass); + if (InterfaceDesc->bInterfaceProtocol != 0x50) + { + DPRINT1("USB Device is not a bulk only device and is not currently supported\n"); + return STATUS_NOT_SUPPORTED; + } + + if (InterfaceDesc->bInterfaceSubClass != 0x06) + { + // + // FIXME: need to pad CDBs to 12 byte + // mode select commands must be translated from 1AH / 15h to 5AH / 55h + // + DPRINT1("[USBSTOR] Error: need to pad CDBs\n"); + return STATUS_NOT_IMPLEMENTED; + } + + // + // now select an interface + // + Status = USBSTOR_SelectConfigurationAndInterface(DeviceObject, DeviceExtension); + if (!NT_SUCCESS(Status)) + { + // + // failed to get device descriptor + // + DPRINT1("USBSTOR_FdoHandleStartDevice failed to select configuration / interface with %x\n", Status); + return Status; + } + + // + // check if we got a bulk in + bulk out endpoint + // + Status = USBSTOR_GetPipeHandles(DeviceExtension); + if (!NT_SUCCESS(Status)) + { + // + // failed to get pipe handles descriptor + // + DPRINT1("USBSTOR_FdoHandleStartDevice no pipe handles %x\n", Status); + return Status; + } + + // + // get num of lun which are supported + // + Status = USBSTOR_GetMaxLUN(DeviceExtension->LowerDeviceObject, DeviceExtension); + if (!NT_SUCCESS(Status)) + { + // + // failed to get max LUN + // + DPRINT1("USBSTOR_FdoHandleStartDevice failed to get max lun %x\n", Status); + return Status; + } + + // + // now create for each LUN a device object, 1 minimum + // + do + { + // + // create pdo + // + Status = USBSTOR_CreatePDO(DeviceObject, &DeviceExtension->ChildPDO[Index]); + + // + // check for failure + // + if (!NT_SUCCESS(Status)) + { + // + // failed to create child pdo + // + DPRINT1("USBSTOR_FdoHandleStartDevice USBSTOR_CreatePDO failed for Index %lu with Status %x\n", Index, Status); + return Status; + } + + // + // increment pdo index + // + Index++; + + }while(Index < DeviceExtension->MaxLUN); + +#if 0 + // + // finally get usb device interface + // + Status = USBSTOR_GetBusInterface(DeviceExtension->LowerDeviceObject, &DeviceExtension->BusInterface); + if (!NT_SUCCESS(Status)) + { + // + // failed to device interface + // + DPRINT1("USBSTOR_FdoHandleStartDevice failed to get device interface %x\n", Status); + return Status; + } +#endif + + // + // fdo is now initialized + // + DPRINT("USBSTOR_FdoHandleStartDevice FDO is initialized\n"); + return STATUS_SUCCESS; +} + +NTSTATUS +USBSTOR_FdoHandlePnp( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PFDO_DEVICE_EXTENSION DeviceExtension; + NTSTATUS Status; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(DeviceExtension->Common.IsFDO); + + switch(IoStack->MinorFunction) + { + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + Status = USBSTOR_FdoHandleDeviceRelations(DeviceExtension, Irp); + break; + } + case IRP_MN_STOP_DEVICE: + { + DPRINT1("USBSTOR_FdoHandlePnp: IRP_MN_STOP_DEVICE unimplemented\n"); + Status = STATUS_NOT_SUPPORTED; + break; + } + case IRP_MN_REMOVE_DEVICE: + { + DPRINT("IRP_MN_REMOVE_DEVICE\n"); + + return USBSTOR_FdoHandleRemoveDevice(DeviceObject, DeviceExtension, Irp); + } + case IRP_MN_QUERY_CAPABILITIES: + { + // + // FIXME: set custom capabilities + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(DeviceExtension->LowerDeviceObject, Irp); + } + case IRP_MN_QUERY_STOP_DEVICE: + case IRP_MN_QUERY_REMOVE_DEVICE: + { + // + // we can if nothing is pending + // + if (DeviceExtension->IrpPendingCount != 0 || + DeviceExtension->ActiveSrb != NULL) + { + /* We have pending requests */ + DPRINT1("Failing removal/stop request due to pending requests present\n"); + Status = STATUS_UNSUCCESSFUL; + } + else + { + /* We're all clear */ + Irp->IoStatus.Status = STATUS_SUCCESS; + + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(DeviceExtension->LowerDeviceObject, Irp); + } + break; + } + case IRP_MN_START_DEVICE: + { + Status = USBSTOR_FdoHandleStartDevice(DeviceObject, DeviceExtension, Irp); + break; + } + default: + { + // + // forward irp to next device object + // + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(DeviceExtension->LowerDeviceObject, Irp); + } + } + + // + // complete request + // + if (Status != STATUS_PENDING) + { + // + // store result + // + Irp->IoStatus.Status = Status; + + // + // complete request + // + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + // + // done processing + // + return Status; +} diff --git a/reactos/drivers/usb/usbstor/misc.c b/reactos/drivers/usb/usbstor/misc.c new file mode 100644 index 00000000000..4c49e7587fa --- /dev/null +++ b/reactos/drivers/usb/usbstor/misc.c @@ -0,0 +1,421 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbstor/misc.c + * PURPOSE: USB block storage device driver. + * PROGRAMMERS: + * James Tabor + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbstor.h" + +// +// driver verifier +// +IO_COMPLETION_ROUTINE SyncForwardIrpCompletionRoutine; + +NTSTATUS +NTAPI +USBSTOR_SyncForwardIrpCompletionRoutine( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context) +{ + if (Irp->PendingReturned) + { + KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE); + } + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +NTAPI +USBSTOR_SyncForwardIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + KEVENT Event; + NTSTATUS Status; + + // + // initialize event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // copy irp stack location + // + IoCopyCurrentIrpStackLocationToNext(Irp); + + // + // set completion routine + // + IoSetCompletionRoutine(Irp, USBSTOR_SyncForwardIrpCompletionRoutine, &Event, TRUE, TRUE, TRUE); + + + // + // call driver + // + Status = IoCallDriver(DeviceObject, Irp); + + // + // check if pending + // + if (Status == STATUS_PENDING) + { + // + // wait for the request to finish + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + // + // copy status code + // + Status = Irp->IoStatus.Status; + } + + // + // done + // + return Status; +} + +NTSTATUS +NTAPI +USBSTOR_GetBusInterface( + IN PDEVICE_OBJECT DeviceObject, + OUT PUSB_BUS_INTERFACE_USBDI_V2 BusInterface) +{ + KEVENT Event; + NTSTATUS Status; + PIRP Irp; + IO_STATUS_BLOCK IoStatus; + PIO_STACK_LOCATION Stack; + + // + // sanity checks + // + ASSERT(DeviceObject); + ASSERT(BusInterface); + + + // + // initialize event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + + // + // create irp + // + Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, + DeviceObject, + NULL, + 0, + NULL, + &Event, + &IoStatus); + + // + // was irp built + // + if (Irp == NULL) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize request + // + Stack=IoGetNextIrpStackLocation(Irp); + Stack->MajorFunction = IRP_MJ_PNP; + Stack->MinorFunction = IRP_MN_QUERY_INTERFACE; + Stack->Parameters.QueryInterface.Size = sizeof(BUS_INTERFACE_STANDARD); + Stack->Parameters.QueryInterface.InterfaceType = (LPGUID)&USB_BUS_INTERFACE_USBDI_GUID; + Stack->Parameters.QueryInterface.Version = 2; + Stack->Parameters.QueryInterface.Interface = (PINTERFACE)BusInterface; + Stack->Parameters.QueryInterface.InterfaceSpecificData = NULL; + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + + // + // call driver + // + Status= IoCallDriver(DeviceObject, Irp); + + // + // did operation complete + // + if (Status == STATUS_PENDING) + { + // + // wait for completion + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + // + // collect status + // + Status=IoStatus.Status; + } + + return Status; +} + +NTSTATUS +USBSTOR_SyncUrbRequest( + IN PDEVICE_OBJECT DeviceObject, + OUT PURB UrbRequest) +{ + PIRP Irp; + PIO_STACK_LOCATION IoStack; + KEVENT Event; + NTSTATUS Status; + + // + // allocate irp + // + Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + if (!Irp) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + + // + // get next stack location + // + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // initialize stack location + // + IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; + IoStack->Parameters.Others.Argument1 = (PVOID)UrbRequest; + IoStack->Parameters.DeviceIoControl.InputBufferLength = UrbRequest->UrbHeader.Length; + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // setup completion routine + // + IoSetCompletionRoutine(Irp, USBSTOR_SyncForwardIrpCompletionRoutine, &Event, TRUE, TRUE, TRUE); + + // + // call driver + // + Status = IoCallDriver(DeviceObject, Irp); + + // + // check if request is pending + // + if (Status == STATUS_PENDING) + { + // + // wait for completion + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + // + // update status + // + Status = Irp->IoStatus.Status; + } + + // + // free irp + // + IoFreeIrp(Irp); + + // + // done + // + return Status; +} + +PVOID +AllocateItem( + IN POOL_TYPE PoolType, + IN ULONG ItemSize) +{ + // + // allocate item + // + PVOID Item = ExAllocatePoolWithTag(PoolType, ItemSize, USB_STOR_TAG); + + if (Item) + { + // + // zero item + // + RtlZeroMemory(Item, ItemSize); + } + + // + // return element + // + return Item; +} + +VOID +FreeItem( + IN PVOID Item) +{ + // + // free item + // + ExFreePoolWithTag(Item, USB_STOR_TAG); +} + +NTSTATUS +USBSTOR_ClassRequest( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension, + IN UCHAR RequestType, + IN USHORT Index, + IN ULONG TransferFlags, + IN ULONG TransferBufferLength, + IN PVOID TransferBuffer) + +{ + PURB Urb; + PUCHAR Buffer; + NTSTATUS Status; + + // + // first allocate urb + // + Urb = (PURB)AllocateItem(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST)); + if (!Urb) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // allocate 1-byte buffer + // + Buffer = (PUCHAR)AllocateItem(NonPagedPool, sizeof(UCHAR)); + if (!Buffer) + { + // + // no memory + // + FreeItem(Buffer); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize vendor request + // + Urb->UrbControlVendorClassRequest.Hdr.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST); + Urb->UrbControlVendorClassRequest.Hdr.Function = URB_FUNCTION_CLASS_INTERFACE; + Urb->UrbControlVendorClassRequest.TransferFlags = TransferFlags; + Urb->UrbControlVendorClassRequest.TransferBufferLength = TransferBufferLength; + Urb->UrbControlVendorClassRequest.TransferBuffer = TransferBuffer; + Urb->UrbControlVendorClassRequest.Request = RequestType; + Urb->UrbControlVendorClassRequest.Index = Index; + + // + // submit request + // + Status = USBSTOR_SyncUrbRequest(DeviceObject, Urb); + + // + // free urb + // + FreeItem(Urb); + + // + // done + // + return Status; +} + + +NTSTATUS +USBSTOR_GetMaxLUN( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension) +{ + PUCHAR Buffer; + NTSTATUS Status; + + // + // allocate 1-byte buffer + // + Buffer = (PUCHAR)AllocateItem(NonPagedPool, sizeof(UCHAR)); + if (!Buffer) + { + // + // no memory + // + FreeItem(Buffer); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // execute request + // + Status = USBSTOR_ClassRequest(DeviceObject, DeviceExtension, USB_BULK_GET_MAX_LUN, DeviceExtension->InterfaceInformation->InterfaceNumber, USBD_TRANSFER_DIRECTION_IN, sizeof(UCHAR), Buffer); + + DPRINT("MaxLUN: %x\n", *Buffer); + + if (*Buffer > 0xF) + { + // + // invalid response documented in usb mass storage specification + // + Status = STATUS_DEVICE_DATA_ERROR; + } + else + { + // + // store maxlun + // + DeviceExtension->MaxLUN = *Buffer; + } + + // + // free buffer + // + FreeItem(Buffer); + + // + // done + // + return Status; + +} + +NTSTATUS +USBSTOR_ResetDevice( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension) +{ + NTSTATUS Status; + + // + // execute request + // + Status = USBSTOR_ClassRequest(DeviceObject, DeviceExtension, USB_BULK_RESET_DEVICE, DeviceExtension->InterfaceInformation->InterfaceNumber, USBD_TRANSFER_DIRECTION_OUT, 0, NULL); + DPRINT1("Status %x\n", Status); + + // + // done + // + return Status; + +} diff --git a/reactos/drivers/usb/usbstor/pdo.c b/reactos/drivers/usb/usbstor/pdo.c new file mode 100644 index 00000000000..2c42744f208 --- /dev/null +++ b/reactos/drivers/usb/usbstor/pdo.c @@ -0,0 +1,1009 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbstor/pdo.c + * PURPOSE: USB block storage device driver. + * PROGRAMMERS: + * James Tabor + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbstor.h" + +LPCSTR +USBSTOR_GetDeviceType( + IN PUFI_INQUIRY_RESPONSE InquiryData) +{ + // + // check if device type is zero + // + if (InquiryData->DeviceType == 0) + { + // + // direct access device + // + + // + // FIXME: check if floppy + // + return "Disk"; + } + + // + // FIXME: use constant - derrived from http://en.wikipedia.org/wiki/SCSI_Peripheral_Device_Type + // + switch (InquiryData->DeviceType) + { + case 1: + { + // + // sequential device, i.e magnetic tape + // + return "Sequential"; + } + case 4: + { + // + // write once device + // + return "Worm"; + } + case 5: + { + // + // CDROM device + // + return "CdRom"; + } + case 7: + { + // + // optical memory device + // + return "Optical"; + } + case 8: + { + // + // medium change device + // + return "Changer"; + } + default: + { + // + // other device + // + return "Other"; + } + } +} + +LPCSTR +USBSTOR_GetGenericType( + IN PUFI_INQUIRY_RESPONSE InquiryData) +{ + // + // check if device type is zero + // + if (InquiryData->DeviceType == 0) + { + // + // direct access device + // + + // + // FIXME: check if floppy + // + return "GenDisk"; + } + + // + // FIXME: use constant - derrived from http://en.wikipedia.org/wiki/SCSI_Peripheral_Device_Type + // + switch (InquiryData->DeviceType) + { + case 1: + { + // + // sequential device, i.e magnetic tape + // + return "GenSequential"; + } + case 4: + { + // + // write once device + // + return "GenWorm"; + } + case 5: + { + // + // CDROM device + // + return "GenCdRom"; + } + case 7: + { + // + // optical memory device + // + return "GenOptical"; + } + case 8: + { + // + // medium change device + // + return "GenChanger"; + } + default: + { + // + // other device + // + return "UsbstorOther"; + } + } +} + + +ULONG +CopyField( + IN PUCHAR Name, + IN PCHAR Buffer, + IN ULONG MaxLength) +{ + ULONG Index; + + for(Index = 0; Index < MaxLength; Index++) + { + if (Name[Index] <= ' ' || Name[Index] >= 0x7F /* last printable ascii character */ || Name[Index] == ',') + { + // + // convert to underscore + // + Buffer[Index] = '_'; + } + else + { + // + // just copy character + // + Buffer[Index] = Name[Index]; + } + } + + return MaxLength; +} + +NTSTATUS +USBSTOR_PdoHandleQueryDeviceText( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + //PPDO_DEVICE_EXTENSION DeviceExtension; + PIO_STACK_LOCATION IoStack; + LPWSTR Buffer; + static WCHAR DeviceText[] = L"USB Mass Storage Device"; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + if (IoStack->Parameters.QueryDeviceText.DeviceTextType == DeviceTextDescription) + { + DPRINT("USBSTOR_PdoHandleQueryDeviceText DeviceTextDescription\n"); + + // + // allocate item + // + Buffer = (LPWSTR)AllocateItem(PagedPool, sizeof(DeviceText)); + if (!Buffer) + { + // + // no memory + // + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy buffer + // + wcscpy(Buffer, DeviceText); + + // + // save result + // + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + return STATUS_SUCCESS; + } + else + { + DPRINT("USBSTOR_PdoHandleQueryDeviceText DeviceTextLocationInformation\n"); + + // + // allocate item + // + Buffer = (LPWSTR)AllocateItem(PagedPool, sizeof(DeviceText)); + if (!Buffer) + { + // + // no memory + // + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy buffer + // + wcscpy(Buffer, DeviceText); + + // + // save result + // + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + return STATUS_SUCCESS; + } + +} + + +NTSTATUS +USBSTOR_PdoHandleQueryDeviceId( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PPDO_DEVICE_EXTENSION DeviceExtension; + NTSTATUS Status; + CHAR Buffer[100]; + LPCSTR DeviceType; + ULONG Offset = 0; + PUFI_INQUIRY_RESPONSE InquiryData; + ANSI_STRING AnsiString; + UNICODE_STRING DeviceId; + + // + // get device extension + // + DeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(DeviceExtension->InquiryData); + + // + // get inquiry data + // + InquiryData = (PUFI_INQUIRY_RESPONSE)DeviceExtension->InquiryData; + + // + // get device type + // + DeviceType = USBSTOR_GetDeviceType(InquiryData); + + // + // zero buffer + // + RtlZeroMemory(Buffer, sizeof(Buffer)); + + // + // lets create device string + // + Offset = sprintf(&Buffer[Offset], "USBSTOR\\"); + Offset += sprintf(&Buffer[Offset], DeviceType); + Offset += sprintf(&Buffer[Offset], "&Ven_"); + Offset += CopyField(InquiryData->Vendor, &Buffer[Offset], 8); + Offset += sprintf(&Buffer[Offset], "&Prod_"); + Offset += CopyField(InquiryData->Product, &Buffer[Offset], 16); + Offset += sprintf(&Buffer[Offset], "&Rev_"); + Offset += CopyField(InquiryData->Revision, &Buffer[Offset], 4); + + // + // now initialize ansi string + // + RtlInitAnsiString(&AnsiString, (PCSZ)Buffer); + + // + // allocate DeviceId string + // + DeviceId.Length = 0; + DeviceId.MaximumLength = (strlen((PCHAR)Buffer) + 1) * sizeof(WCHAR); + DeviceId.Buffer = (LPWSTR)AllocateItem(PagedPool, DeviceId.MaximumLength); + if (!DeviceId.Buffer) + { + // + // no memory + // + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // convert to unicode + // + Status = RtlAnsiStringToUnicodeString(&DeviceId, &AnsiString, FALSE); + + if (NT_SUCCESS(Status)) + { + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceId.Buffer; + } + + DPRINT("DeviceId %wZ Status %x\n", &DeviceId, Status); + + // + // done + // + return Status; +} + +VOID +USBSTOR_ConvertToUnicodeString( + IN CHAR * Buffer, + IN ULONG ResultBufferLength, + IN ULONG ResultBufferOffset, + OUT LPWSTR ResultBuffer, + OUT PULONG NewResultBufferOffset) +{ + UNICODE_STRING DeviceString; + ANSI_STRING AnsiString; + NTSTATUS Status; + + ASSERT(ResultBufferLength); + ASSERT(ResultBufferLength > ResultBufferOffset); + + DPRINT("ResultBufferOffset %lu ResultBufferLength %lu Buffer %s Length %lu\n", ResultBufferOffset, ResultBufferLength, Buffer, strlen(Buffer)); + + // + // construct destination string + // + DeviceString.Buffer = &ResultBuffer[ResultBufferOffset]; + DeviceString.Length = 0; + DeviceString.MaximumLength = (ResultBufferLength - ResultBufferOffset) * sizeof(WCHAR); + + // + // initialize source string + // + RtlInitAnsiString(&AnsiString, Buffer); + + // + // convert to unicode + // + Status = RtlAnsiStringToUnicodeString(&DeviceString, &AnsiString, FALSE); + ASSERT(Status == STATUS_SUCCESS); + + // + // subtract consumed bytes + // + ResultBufferLength -= (DeviceString.Length + sizeof(WCHAR)) / sizeof(WCHAR); + ResultBufferOffset += (DeviceString.Length + sizeof(WCHAR)) / sizeof(WCHAR); + + // + // store new offset + // + *NewResultBufferOffset = ResultBufferOffset; +} + + + +NTSTATUS +USBSTOR_PdoHandleQueryHardwareId( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + LPCSTR GenericType, DeviceType; + LPWSTR Buffer; + CHAR Id1[50], Id2[50], Id3[50], Id4[50], Id5[50], Id6[50]; + ULONG Id1Length, Id2Length, Id3Length, Id4Length, Id5Length,Id6Length; + ULONG Offset, TotalLength, Length; + PUFI_INQUIRY_RESPONSE InquiryData; + + // + // get PDO device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(FDODeviceExtension->DeviceDescriptor); + + // + // get inquiry data + // + InquiryData = (PUFI_INQUIRY_RESPONSE)PDODeviceExtension->InquiryData; + + + // + // get device type and generic type + // + DeviceType = USBSTOR_GetDeviceType(InquiryData); + GenericType = USBSTOR_GetGenericType(InquiryData); + + ASSERT(GenericType); + + // + // generate id 1 + // USBSTOR\SCSIType_Vendor(8)_Product(16)_Revision(4) + // + RtlZeroMemory(Id1, sizeof(Id1)); + Offset = 0; + Offset = sprintf(&Id1[Offset], "USBSTOR\\"); + Offset += sprintf(&Id1[Offset], DeviceType); + Offset += CopyField(InquiryData->Vendor, &Id1[Offset], 8); + Offset += CopyField(InquiryData->Product, &Id1[Offset], 16); + Offset += CopyField(InquiryData->Revision, &Id1[Offset], 4); + Id1Length = strlen(Id1) + 1; + DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId1 %s\n", Id1); + + // + // generate id 2 + // USBSTOR\SCSIType_VENDOR(8)_Product(16) + // + RtlZeroMemory(Id2, sizeof(Id2)); + Offset = 0; + Offset = sprintf(&Id2[Offset], "USBSTOR\\"); + Offset += sprintf(&Id2[Offset], DeviceType); + Offset += CopyField(InquiryData->Vendor, &Id2[Offset], 8); + Offset += CopyField(InquiryData->Product, &Id2[Offset], 16); + Id2Length = strlen(Id2) + 1; + DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId2 %s\n", Id2); + + // + // generate id 3 + // USBSTOR\SCSIType_VENDOR(8) + // + RtlZeroMemory(Id3, sizeof(Id3)); + Offset = 0; + Offset = sprintf(&Id3[Offset], "USBSTOR\\"); + Offset += sprintf(&Id3[Offset], DeviceType); + Offset += CopyField(InquiryData->Vendor, &Id3[Offset], 8); + Id3Length = strlen(Id3) + 1; + DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId3 %s\n", Id3); + + // + // generate id 4 + // USBSTOR\SCSIType_VENDOR(8)_Product(16)_Revision(1) + // + RtlZeroMemory(Id4, sizeof(Id4)); + Offset = 0; + Offset = sprintf(&Id4[Offset], "USBSTOR\\"); + Offset += sprintf(&Id4[Offset], DeviceType); + Offset += CopyField(InquiryData->Vendor, &Id4[Offset], 8); + Offset += CopyField(InquiryData->Product, &Id4[Offset], 16); + Offset += CopyField(InquiryData->Revision, &Id4[Offset], 1); + Id4Length = strlen(Id4) + 1; + DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId4 %s\n", Id4); + + // + // generate id 5 + // USBSTOR\SCSIType + // + RtlZeroMemory(Id5, sizeof(Id5)); + Offset = 0; + Offset = sprintf(&Id5[Offset], "USBSTOR\\"); + Offset += sprintf(&Id5[Offset], GenericType); + Id5Length = strlen(Id5) + 1; + DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId5 %s\n", Id5); + + // + // generate id 6 + // SCSIType + // + RtlZeroMemory(Id6, sizeof(Id6)); + Offset = 0; + Offset = sprintf(&Id6[Offset], GenericType); + Id6Length = strlen(Id6) + 1; + DPRINT("USBSTOR_PdoHandleQueryHardwareId HardwareId6 %s\n", Id6); + + // + // compute total length + // + TotalLength = Id1Length + Id2Length + Id3Length + Id4Length + Id5Length + Id6Length + 1; + + // + // allocate buffer + // + Buffer = (LPWSTR)AllocateItem(PagedPool, TotalLength * sizeof(WCHAR)); + if (!Buffer) + { + // + // no memory + // + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // reset offset + // + Offset = 0; + Length = TotalLength; + + USBSTOR_ConvertToUnicodeString(Id1, Length, Offset, Buffer, &Offset); + USBSTOR_ConvertToUnicodeString(Id2, Length, Offset, Buffer, &Offset); + USBSTOR_ConvertToUnicodeString(Id3, Length, Offset, Buffer, &Offset); + USBSTOR_ConvertToUnicodeString(Id4, Length, Offset, Buffer, &Offset); + USBSTOR_ConvertToUnicodeString(Id5, Length, Offset, Buffer, &Offset); + USBSTOR_ConvertToUnicodeString(Id6, Length, Offset, Buffer, &Offset); + + // + // sanity check + // + ASSERT(Offset + 1 == Length); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + + // + // done + // + return STATUS_SUCCESS; +} + +NTSTATUS +USBSTOR_PdoHandleQueryCompatibleId( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + CHAR Buffer[100]; + ULONG Length, Offset; + LPWSTR InstanceId; + LPCSTR DeviceType; + + // + // get PDO device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(FDODeviceExtension->DeviceDescriptor); + + // + // get target device type + // + DeviceType = USBSTOR_GetDeviceType((PUFI_INQUIRY_RESPONSE)PDODeviceExtension->InquiryData); + + // + // zero memory + // + RtlZeroMemory(Buffer, sizeof(Buffer)); + + // + // format instance id + // + Length = sprintf(Buffer, "USBSTOR\\%s", DeviceType) + 1; + Length += sprintf(&Buffer[Length], "USBSTOR\\%s", "RAW") + 2; + + // + // allocate instance id + // + InstanceId = (LPWSTR)AllocateItem(PagedPool, Length * sizeof(WCHAR)); + if (!InstanceId) + { + // + // no memory + // + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + USBSTOR_ConvertToUnicodeString(Buffer, Length, 0, InstanceId, &Offset); + USBSTOR_ConvertToUnicodeString(&Buffer[Offset], Length, Offset, InstanceId, &Offset); + + DPRINT("USBSTOR_PdoHandleQueryCompatibleId %S\n", InstanceId); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)InstanceId; + + // + // completed successfully + // + return STATUS_SUCCESS; +} + +NTSTATUS +USBSTOR_PdoHandleQueryInstanceId( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + WCHAR Buffer[100]; + ULONG Length; + LPWSTR InstanceId; + + // + // get PDO device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension; + + // + // format instance id + // + if (FDODeviceExtension->SerialNumber) + { + // + // using serial number from device + // + swprintf(Buffer, L"%s&%d", FDODeviceExtension->SerialNumber->bString, PDODeviceExtension->LUN); + } + else + { + // + // FIXME: should use some random value + // + swprintf(Buffer, L"%s&%d", L"00000000", PDODeviceExtension->LUN); + } + + // + // calculate length + // + Length = wcslen(Buffer) + 1; + + // + // allocate instance id + // + InstanceId = (LPWSTR)AllocateItem(PagedPool, Length * sizeof(WCHAR)); + if (!InstanceId) + { + // + // no memory + // + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // copy instance id + // + wcscpy(InstanceId, Buffer); + + DPRINT("USBSTOR_PdoHandleQueryInstanceId %S\n", InstanceId); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)InstanceId; + + // + // completed successfully + // + return STATUS_SUCCESS; +} + +NTSTATUS +USBSTOR_PdoHandleDeviceRelations( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PDEVICE_RELATIONS DeviceRelations; + PIO_STACK_LOCATION IoStack; + + DPRINT("USBSTOR_PdoHandleDeviceRelations\n"); + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // check if relation type is BusRelations + // + if (IoStack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation) + { + // + // PDO handles only target device relation + // + return Irp->IoStatus.Status; + } + + // + // allocate device relations + // + DeviceRelations = (PDEVICE_RELATIONS)AllocateItem(PagedPool, sizeof(DEVICE_RELATIONS)); + if (!DeviceRelations) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize device relations + // + DeviceRelations->Count = 1; + DeviceRelations->Objects[0] = DeviceObject; + ObReferenceObject(DeviceObject); + + // + // store result + // + Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; + + // + // completed successfully + // + return STATUS_SUCCESS; +} + + +NTSTATUS +USBSTOR_PdoHandlePnp( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PPDO_DEVICE_EXTENSION DeviceExtension; + NTSTATUS Status; + PDEVICE_CAPABILITIES Caps; + ULONG bDelete; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get device extension + // + DeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(DeviceExtension->Common.IsFDO == FALSE); + + switch(IoStack->MinorFunction) + { + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + Status = USBSTOR_PdoHandleDeviceRelations(DeviceObject, Irp); + break; + } + case IRP_MN_QUERY_DEVICE_TEXT: + { + Status = USBSTOR_PdoHandleQueryDeviceText(DeviceObject, Irp); + break; + } + case IRP_MN_QUERY_ID: + { + if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID) + { + // + // handle query device id + // + Status = USBSTOR_PdoHandleQueryDeviceId(DeviceObject, Irp); + break; + } + else if (IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs) + { + // + // handle instance id + // + Status = USBSTOR_PdoHandleQueryHardwareId(DeviceObject, Irp); + break; + } + else if (IoStack->Parameters.QueryId.IdType == BusQueryInstanceID) + { + // + // handle instance id + // + Status = USBSTOR_PdoHandleQueryInstanceId(DeviceObject, Irp); + break; + } + else if (IoStack->Parameters.QueryId.IdType == BusQueryCompatibleIDs) + { + // + // handle instance id + // + Status = USBSTOR_PdoHandleQueryCompatibleId(DeviceObject, Irp); + break; + } + + DPRINT1("USBSTOR_PdoHandlePnp: IRP_MN_QUERY_ID IdType %x unimplemented\n", IoStack->Parameters.QueryId.IdType); + Status = STATUS_NOT_SUPPORTED; + Irp->IoStatus.Information = 0; + break; + } + case IRP_MN_REMOVE_DEVICE: + { + DPRINT("IRP_MN_REMOVE_DEVICE\n"); + + if(*DeviceExtension->PDODeviceObject != NULL) + { + // + // clear entry in FDO pdo list + // + *DeviceExtension->PDODeviceObject = NULL; + bDelete = TRUE; + } + else + { + // + // device object already marked for deletion + // + bDelete = FALSE; + } + + /* Complete the IRP */ + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + if (bDelete) + { + /* Delete the device object */ + IoDeleteDevice(DeviceObject); + } + return STATUS_SUCCESS; + } + case IRP_MN_QUERY_CAPABILITIES: + { + // + // just forward irp to lower device + // + Status = USBSTOR_SyncForwardIrp(DeviceExtension->LowerDeviceObject, Irp); + ASSERT(Status == STATUS_SUCCESS); + + if (NT_SUCCESS(Status)) + { + // + // check if no unique id + // + Caps = (PDEVICE_CAPABILITIES)IoStack->Parameters.DeviceCapabilities.Capabilities; + Caps->UniqueID = TRUE; //FIXME + Caps->Removable = TRUE; //FIXME + } + break; + } + case IRP_MN_QUERY_REMOVE_DEVICE: + case IRP_MN_QUERY_STOP_DEVICE: + { + // + // if we're not claimed it's ok + // + if (DeviceExtension->Claimed) + { + Status = STATUS_UNSUCCESSFUL; + DPRINT1("[USBSTOR] Request %x fails because device is still claimed\n", IoStack->MinorFunction); + } + else + Status = STATUS_SUCCESS; + break; + } + case IRP_MN_START_DEVICE: + { + // + // no-op for PDO + // + Status = STATUS_SUCCESS; + break; + } + default: + { + // + // do nothing + // + Status = Irp->IoStatus.Status; + } + } + + // + // complete request + // + if (Status != STATUS_PENDING) + { + // + // store result + // + Irp->IoStatus.Status = Status; + + // + // complete request + // + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + // + // done processing + // + return Status; +} + +NTSTATUS +USBSTOR_CreatePDO( + IN PDEVICE_OBJECT DeviceObject, + OUT PDEVICE_OBJECT *ChildDeviceObject) +{ + PDEVICE_OBJECT PDO; + NTSTATUS Status; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + + // + // create child device object + // + Status = IoCreateDevice(DeviceObject->DriverObject, sizeof(PDO_DEVICE_EXTENSION), NULL, FILE_DEVICE_MASS_STORAGE, FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN, FALSE, &PDO); + if (!NT_SUCCESS(Status)) + { + // + // failed to create device + // + return Status; + } + + // + // patch the stack size + // + PDO->StackSize = DeviceObject->StackSize; + + // + // get device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)PDO->DeviceExtension; + + // + // initialize device extension + // + RtlZeroMemory(PDODeviceExtension, sizeof(PDO_DEVICE_EXTENSION)); + PDODeviceExtension->Common.IsFDO = FALSE; + PDODeviceExtension->LowerDeviceObject = DeviceObject; + PDODeviceExtension->PDODeviceObject = ChildDeviceObject; + + // + // set device flags + // + PDO->Flags |= DO_DIRECT_IO | DO_MAP_IO_BUFFER; + + // + // device is initialized + // + PDO->Flags &= ~DO_DEVICE_INITIALIZING; + + // + // output device object + // + *ChildDeviceObject = PDO; + + USBSTOR_SendInquiryCmd(PDO); + + // + // done + // + return Status; +} diff --git a/reactos/drivers/usb/usbstor/queue.c b/reactos/drivers/usb/usbstor/queue.c new file mode 100644 index 00000000000..cda2d28d2e3 --- /dev/null +++ b/reactos/drivers/usb/usbstor/queue.c @@ -0,0 +1,668 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbstor/queue.c + * PURPOSE: USB block storage device driver. + * PROGRAMMERS: + * James Tabor + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbstor.h" + +VOID +USBSTOR_QueueInitialize( + PFDO_DEVICE_EXTENSION FDODeviceExtension) +{ + // + // sanity check + // + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // initialize queue lock + // + KeInitializeSpinLock(&FDODeviceExtension->IrpListLock); + + // + // initialize irp list head + // + InitializeListHead(&FDODeviceExtension->IrpListHead); + + // + // initialize event + // + KeInitializeEvent(&FDODeviceExtension->NoPendingRequests, NotificationEvent, TRUE); +} + +VOID +NTAPI +USBSTOR_CancelIo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // this IRP isn't in our list here + // + + // + // now release the cancel lock + // + IoReleaseCancelSpinLock(Irp->CancelIrql); + + // + // set cancel status + // + Irp->IoStatus.Status = STATUS_CANCELLED; + + // + // now cancel the irp + // + USBSTOR_QueueTerminateRequest(DeviceObject, Irp); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // start the next one + // + USBSTOR_QueueNextRequest(DeviceObject); +} + +VOID +NTAPI +USBSTOR_Cancel( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // acquire irp list lock + // + KeAcquireSpinLockAtDpcLevel(&FDODeviceExtension->IrpListLock); + + // + // remove the irp from the list + // + RemoveEntryList(&Irp->Tail.Overlay.ListEntry); + + // + // release irp list lock + // + KeReleaseSpinLockFromDpcLevel(&FDODeviceExtension->IrpListLock); + + // + // now release the cancel lock + // + IoReleaseCancelSpinLock(Irp->CancelIrql); + + // + // set cancel status + // + Irp->IoStatus.Status = STATUS_CANCELLED; + + // + // now cancel the irp + // + USBSTOR_QueueTerminateRequest(DeviceObject, Irp); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // start the next one + // + USBSTOR_QueueNextRequest(DeviceObject); +} + +BOOLEAN +USBSTOR_QueueAddIrp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PDRIVER_CANCEL OldDriverCancel; + KIRQL OldLevel; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + BOOLEAN IrpListFreeze; + BOOLEAN SrbProcessing; + PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1; + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // mark irp pending + // + IoMarkIrpPending(Irp); + + // + // acquire lock + // + KeAcquireSpinLock(&FDODeviceExtension->IrpListLock, &OldLevel); + + // + // check if there are irp pending + // + SrbProcessing = FDODeviceExtension->IrpPendingCount != 0; + + if (SrbProcessing) + { + // + // add irp to queue + // + InsertTailList(&FDODeviceExtension->IrpListHead, &Irp->Tail.Overlay.ListEntry); + } + + // + // increment pending count + // + FDODeviceExtension->IrpPendingCount++; + + + // + // clear the no requests pending event + // + KeClearEvent(&FDODeviceExtension->NoPendingRequests); + + // + // check if queue is freezed + // + IrpListFreeze = FDODeviceExtension->IrpListFreeze; + + // + // release list lock + // + KeReleaseSpinLock(&FDODeviceExtension->IrpListLock, OldLevel); + + // + // synchronize with cancellations by holding the cancel lock + // + IoAcquireCancelSpinLock(&Irp->CancelIrql); + + // + // now set the driver cancel routine + // + if (SrbProcessing) + { + ASSERT(FDODeviceExtension->ActiveSrb != NULL); + + OldDriverCancel = IoSetCancelRoutine(Irp, USBSTOR_Cancel); + } + else + { + ASSERT(FDODeviceExtension->ActiveSrb == NULL); + + FDODeviceExtension->ActiveSrb = Request; + OldDriverCancel = IoSetCancelRoutine(Irp, USBSTOR_CancelIo); + } + + // + // check if the irp has already been cancelled + // + if (Irp->Cancel && OldDriverCancel == NULL) + { + // + // cancel irp + // + Irp->CancelRoutine(DeviceObject, Irp); + + // + // irp was cancelled + // + return FALSE; + } + + // + // release the cancel lock + // + IoReleaseCancelSpinLock(Irp->CancelIrql); + + // + // if list is freezed, dont start this packet + // + DPRINT("IrpListFreeze: %lu IrpPendingCount %lu\n", IrpListFreeze, FDODeviceExtension->IrpPendingCount); + + return (IrpListFreeze || SrbProcessing); +} + +PIRP +USBSTOR_RemoveIrp( + IN PDEVICE_OBJECT DeviceObject) +{ + KIRQL OldLevel; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + PLIST_ENTRY Entry; + PIRP Irp = NULL; + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // acquire lock + // + KeAcquireSpinLock(&FDODeviceExtension->IrpListLock, &OldLevel); + + // + // check if list is empty + // + if (!IsListEmpty(&FDODeviceExtension->IrpListHead)) + { + // + // remove entry + // + Entry = RemoveHeadList(&FDODeviceExtension->IrpListHead); + + // + // get offset to start of irp + // + Irp = (PIRP)CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry); + } + + // + // release list lock + // + KeReleaseSpinLock(&FDODeviceExtension->IrpListLock, OldLevel); + + // + // return result + // + return Irp; +} + +VOID +USBSTOR_QueueWaitForPendingRequests( + IN PDEVICE_OBJECT DeviceObject) +{ + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // perform the wait + // + KeWaitForSingleObject(&FDODeviceExtension->NoPendingRequests, + Executive, + KernelMode, + FALSE, + NULL); +} + +VOID +USBSTOR_QueueTerminateRequest( + IN PDEVICE_OBJECT FDODeviceObject, + IN PIRP Irp) +{ + KIRQL OldLevel; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp); + PSCSI_REQUEST_BLOCK Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1; + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)FDODeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // acquire lock + // + KeAcquireSpinLock(&FDODeviceExtension->IrpListLock, &OldLevel); + + // + // decrement pending irp count + // + FDODeviceExtension->IrpPendingCount--; + + // + // check if this was our current active SRB + // + if (FDODeviceExtension->ActiveSrb == Request) + { + // + // indicate processing is completed + // + FDODeviceExtension->ActiveSrb = NULL; + } + + // + // Set the event if nothing else is pending + // + if (FDODeviceExtension->IrpPendingCount == 0 && + FDODeviceExtension->ActiveSrb == NULL) + { + KeSetEvent(&FDODeviceExtension->NoPendingRequests, IO_NO_INCREMENT, FALSE); + } + + // + // release lock + // + KeReleaseSpinLock(&FDODeviceExtension->IrpListLock, OldLevel); + +} + +VOID +USBSTOR_QueueNextRequest( + IN PDEVICE_OBJECT DeviceObject) +{ + PFDO_DEVICE_EXTENSION FDODeviceExtension; + PIRP Irp; + PIO_STACK_LOCATION IoStack; + PSCSI_REQUEST_BLOCK Request; + + // + // get pdo device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // check first if there's already a request pending or the queue is frozen + // + if (FDODeviceExtension->ActiveSrb != NULL || + FDODeviceExtension->IrpListFreeze) + { + // + // no work to do yet + // + return; + } + + // + // remove first irp from list + // + Irp = USBSTOR_RemoveIrp(DeviceObject); + + // + // is there an irp pending + // + if (!Irp) + { + // + // no work to do + // + IoStartNextPacket(DeviceObject, TRUE); + return; + } + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get srb + // + Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1; + + // + // sanity check + // + ASSERT(Request); + + // + // set the active SRB + // + FDODeviceExtension->ActiveSrb = Request; + + // + // start next packet + // + IoStartPacket(DeviceObject, Irp, &Request->QueueSortKey, USBSTOR_CancelIo); + + // + // start next request + // + IoStartNextPacket(DeviceObject, TRUE); +} + +VOID +USBSTOR_QueueRelease( + IN PDEVICE_OBJECT DeviceObject) +{ + PFDO_DEVICE_EXTENSION FDODeviceExtension; + PIRP Irp; + KIRQL OldLevel; + PIO_STACK_LOCATION IoStack; + PSCSI_REQUEST_BLOCK Request; + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // acquire lock + // + KeAcquireSpinLock(&FDODeviceExtension->IrpListLock, &OldLevel); + + // + // clear freezed status + // + FDODeviceExtension->IrpListFreeze = FALSE; + + // + // release irp list lock + // + KeReleaseSpinLock(&FDODeviceExtension->IrpListLock, OldLevel); + + // + // grab newest irp + // + Irp = USBSTOR_RemoveIrp(DeviceObject); + + // + // is there an irp + // + if (!Irp) + { + // + // no irp + // + return; + } + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get srb + // + Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1; + + // + // start new packet + // + IoStartPacket(DeviceObject, + Irp, + &Request->QueueSortKey, + USBSTOR_CancelIo); +} + + +VOID +NTAPI +USBSTOR_StartIo( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + KIRQL OldLevel; + NTSTATUS Status; + BOOLEAN ResetInProgress; + + DPRINT("USBSTOR_StartIo\n"); + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(FDODeviceExtension->Common.IsFDO); + + // + // acquire cancel spinlock + // + IoAcquireCancelSpinLock(&OldLevel); + + // + // set cancel routine to zero + // + IoSetCancelRoutine(Irp, NULL); + + // + // check if the irp has been cancelled + // + if (Irp->Cancel) + { + // + // irp has been cancelled, release cancel spinlock + // + IoReleaseCancelSpinLock(OldLevel); + + // + // irp is cancelled + // + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + + // + // terminate request + // + USBSTOR_QueueTerminateRequest(DeviceObject, Irp); + + // + // complete request + // + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // queue next request + // + USBSTOR_QueueNextRequest(DeviceObject); + + // + // done + // + return; + } + + // + // release cancel spinlock + // + IoReleaseCancelSpinLock(OldLevel); + + // + // acquire lock + // + KeAcquireSpinLock(&FDODeviceExtension->IrpListLock, &OldLevel); + + // + // check reset is in progress + // + ResetInProgress = FDODeviceExtension->ResetInProgress; + ASSERT(ResetInProgress == FALSE); + + // + // release lock + // + KeReleaseSpinLock(&FDODeviceExtension->IrpListLock, OldLevel); + + // + // get current irp stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get pdo device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)IoStack->DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + // + // is a reset in progress + // + if (ResetInProgress) + { + // + // hard reset is in progress + // + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + USBSTOR_QueueTerminateRequest(DeviceObject, Irp); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return; + } + + // + // execute scsi + // + Status = USBSTOR_HandleExecuteSCSI(IoStack->DeviceObject, Irp); + + // + // FIXME: handle error + // +} diff --git a/reactos/drivers/usb/usbstor/scsi.c b/reactos/drivers/usb/usbstor/scsi.c new file mode 100644 index 00000000000..b335309840a --- /dev/null +++ b/reactos/drivers/usb/usbstor/scsi.c @@ -0,0 +1,1263 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Storage Driver + * LICENSE: GPL - See COPYING in the top level directory + * FILE: drivers/usb/usbstor/pdo.c + * PURPOSE: USB block storage device driver. + * PROGRAMMERS: + * James Tabor + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "usbstor.h" + +NTSTATUS +USBSTOR_BuildCBW( + IN ULONG Tag, + IN ULONG DataTransferLength, + IN UCHAR LUN, + IN UCHAR CommandBlockLength, + IN PUCHAR CommandBlock, + IN OUT PCBW Control) +{ + // + // sanity check + // + ASSERT(CommandBlockLength <= 16); + + // + // now initialize CBW + // + Control->Signature = CBW_SIGNATURE; + Control->Tag = Tag; + Control->DataTransferLength = DataTransferLength; + Control->Flags = (CommandBlock[0] != SCSIOP_WRITE) ? 0x80 : 0x00; + Control->LUN = (LUN & MAX_LUN); + Control->CommandBlockLength = CommandBlockLength; + + // + // copy command block + // + RtlCopyMemory(Control->CommandBlock, CommandBlock, CommandBlockLength); + + // + // done + // + return STATUS_SUCCESS; +} + +PIRP_CONTEXT +USBSTOR_AllocateIrpContext() +{ + PIRP_CONTEXT Context; + + // + // allocate irp context + // + Context = (PIRP_CONTEXT)AllocateItem(NonPagedPool, sizeof(IRP_CONTEXT)); + if (!Context) + { + // + // no memory + // + return NULL; + } + + // + // allocate cbw block + // + Context->cbw = (PCBW)AllocateItem(NonPagedPool, 512); + if (!Context->cbw) + { + // + // no memory + // + FreeItem(Context); + return NULL; + } + + // + // done + // + return Context; + +} + +// +// driver verifier +// +IO_COMPLETION_ROUTINE USBSTOR_CSWCompletionRoutine; + +NTSTATUS +NTAPI +USBSTOR_CSWCompletionRoutine( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Ctx) +{ + PIRP_CONTEXT Context; + PIO_STACK_LOCATION IoStack; + PSCSI_REQUEST_BLOCK Request; + PCDB pCDB; + PREAD_CAPACITY_DATA_EX CapacityDataEx; + PREAD_CAPACITY_DATA CapacityData; + PUFI_CAPACITY_RESPONSE Response; + PERRORHANDLER_WORKITEM_DATA ErrorHandlerWorkItemData; + NTSTATUS Status; + PURB Urb; + + DPRINT("USBSTOR_CSWCompletionRoutine Irp %p Ctx %p\n", Irp, Ctx); + + // + // access context + // + Context = (PIRP_CONTEXT)Ctx; + + // + // is there a mdl + // + if (Context->TransferBufferMDL) + { + // + // is there an irp associated + // + if (Context->Irp) + { + // + // did we allocate the mdl + // + if (Context->TransferBufferMDL != Context->Irp->MdlAddress) + { + // + // free mdl + // + IoFreeMdl(Context->TransferBufferMDL); + } + } + else + { + // + // free mdl + // + IoFreeMdl(Context->TransferBufferMDL); + } + } + + if (Context->Irp) + { + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Context->Irp); + + // + // get request block + // + Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1; + ASSERT(Request); + + Status = Irp->IoStatus.Status; + + Urb = &Context->Urb; + + // + // get SCSI command data block + // + pCDB = (PCDB)Request->Cdb; + + // + // check status + // + if (!NT_SUCCESS(Status)) + { + DPRINT1("Status %x\n", Status); + DPRINT1("UrbStatus %x\n", Urb->UrbHeader.Status); + + // + // Check for errors that can be handled + // FIXME: Verify all usb errors that can be recovered via pipe reset/port reset/controller reset + // + if ((Urb->UrbHeader.Status & USB_RECOVERABLE_ERRORS) == Urb->UrbHeader.Status) + { + DPRINT1("Attempting Error Recovery\n"); + // + // free the allocated irp + // + IoFreeIrp(Irp); + + // + // Allocate Work Item Data + // + ErrorHandlerWorkItemData = ExAllocatePoolWithTag(NonPagedPool, sizeof(ERRORHANDLER_WORKITEM_DATA), USB_STOR_TAG); + if (!ErrorHandlerWorkItemData) + { + DPRINT1("Failed to allocate memory\n"); + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // + // Initialize and queue the work item to handle the error + // + ExInitializeWorkItem(&ErrorHandlerWorkItemData->WorkQueueItem, + ErrorHandlerWorkItemRoutine, + ErrorHandlerWorkItemData); + + ErrorHandlerWorkItemData->DeviceObject = Context->FDODeviceExtension->FunctionalDeviceObject; + ErrorHandlerWorkItemData->Context = Context; + DPRINT1("Queuing WorkItemROutine\n"); + ExQueueWorkItem(&ErrorHandlerWorkItemData->WorkQueueItem, DelayedWorkQueue); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + } + } + + Request->SrbStatus = SRB_STATUS_SUCCESS; + + // + // read capacity needs special work + // + if (pCDB->AsByte[0] == SCSIOP_READ_CAPACITY) + { + // + // get output buffer + // + Response = (PUFI_CAPACITY_RESPONSE)Context->TransferData; + + // + // store in pdo + // + Context->PDODeviceExtension->BlockLength = NTOHL(Response->BlockLength); + Context->PDODeviceExtension->LastLogicBlockAddress = NTOHL(Response->LastLogicalBlockAddress); + + if (Request->DataTransferLength == sizeof(READ_CAPACITY_DATA_EX)) + { + // + // get input buffer + // + CapacityDataEx = (PREAD_CAPACITY_DATA_EX)Request->DataBuffer; + + // + // set result + // + CapacityDataEx->BytesPerBlock = Response->BlockLength; + CapacityDataEx->LogicalBlockAddress.QuadPart = Response->LastLogicalBlockAddress; + Irp->IoStatus.Information = sizeof(READ_CAPACITY_DATA_EX); + } + else + { + // + // get input buffer + // + CapacityData = (PREAD_CAPACITY_DATA)Request->DataBuffer; + + // + // set result + // + CapacityData->BytesPerBlock = Response->BlockLength; + CapacityData->LogicalBlockAddress = Response->LastLogicalBlockAddress; + Irp->IoStatus.Information = sizeof(READ_CAPACITY_DATA); + } + + // + // free response + // + FreeItem(Context->TransferData); + } + } + + // + // sanity checks + // + ASSERT(Context->csw->Signature == CSW_SIGNATURE); + ASSERT(Context->csw->Tag == (ULONG)Context->csw); + ASSERT(Context->csw->Status == 0x00); + + // + // free cbw + // + FreeItem(Context->cbw); + + + if (Context->Irp) + { + // + // FIXME: check status + // + Context->Irp->IoStatus.Status = Irp->IoStatus.Status; + Context->Irp->IoStatus.Information = Context->TransferDataLength; + + // + // terminate current request + // + USBSTOR_QueueTerminateRequest(Context->PDODeviceExtension->LowerDeviceObject, Context->Irp); + + // + // complete request + // + IoCompleteRequest(Context->Irp, IO_NO_INCREMENT); + + // + // start next request + // + USBSTOR_QueueNextRequest(Context->PDODeviceExtension->LowerDeviceObject); + } + + if (Context->Event) + { + // + // signal event + // + KeSetEvent(Context->Event, 0, FALSE); + } + + // + // free our allocated irp + // + IoFreeIrp(Irp); + + // + // free context + // + FreeItem(Context); + + // + // done + // + return STATUS_MORE_PROCESSING_REQUIRED; +} + +// +// driver verifier +// +IO_COMPLETION_ROUTINE USBSTOR_DataCompletionRoutine; + +NTSTATUS +NTAPI +USBSTOR_DataCompletionRoutine( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Ctx) +{ + PIRP_CONTEXT Context; + PIO_STACK_LOCATION IoStack; + + DPRINT("USBSTOR_DataCompletionRoutine Irp %p Ctx %p\n", Irp, Ctx); + + // + // access context + // + Context = (PIRP_CONTEXT)Ctx; + + // + // get next stack location + // + + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // now initialize the urb for sending the csw + // + UsbBuildInterruptOrBulkTransferRequest(&Context->Urb, + sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), + Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle, + Context->csw, + NULL, + 512, //FIXME + USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK, + NULL); + + // + // initialize stack location + // + IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; + IoStack->Parameters.Others.Argument1 = (PVOID)&Context->Urb; + IoStack->Parameters.DeviceIoControl.InputBufferLength = Context->Urb.UrbHeader.Length; + Irp->IoStatus.Status = STATUS_SUCCESS; + + + // + // setup completion routine + // + IoSetCompletionRoutine(Irp, USBSTOR_CSWCompletionRoutine, Context, TRUE, TRUE, TRUE); + + // + // call driver + // + IoCallDriver(Context->FDODeviceExtension->LowerDeviceObject, Irp); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +// +// driver verifier +// +IO_COMPLETION_ROUTINE USBSTOR_CBWCompletionRoutine; + +NTSTATUS +NTAPI +USBSTOR_CBWCompletionRoutine( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Ctx) +{ + PIRP_CONTEXT Context; + PIO_STACK_LOCATION IoStack; + UCHAR Code; + USBD_PIPE_HANDLE PipeHandle; + + DPRINT("USBSTOR_CBWCompletionRoutine Irp %p Ctx %p\n", Irp, Ctx); + + // + // access context + // + Context = (PIRP_CONTEXT)Ctx; + + // + // get next stack location + // + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // is there data to be submitted + // + if (Context->TransferDataLength) + { + // + // get command code + // + Code = Context->cbw->CommandBlock[0]; + + if (Code == SCSIOP_WRITE) + { + // + // write request use bulk out pipe + // + PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle; + } + else + { + // + // default bulk in pipe + // + PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle; + } + + // + // now initialize the urb for sending data + // + UsbBuildInterruptOrBulkTransferRequest(&Context->Urb, + sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), + PipeHandle, + NULL, + Context->TransferBufferMDL, + Context->TransferDataLength, + ((Code == SCSIOP_WRITE) ? USBD_TRANSFER_DIRECTION_OUT : (USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK)), + NULL); + + // + // setup completion routine + // + IoSetCompletionRoutine(Irp, USBSTOR_DataCompletionRoutine, Context, TRUE, TRUE, TRUE); + } + else + { + // + // now initialize the urb for sending the csw + // + + UsbBuildInterruptOrBulkTransferRequest(&Context->Urb, + sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), + Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle, + Context->csw, + NULL, + 512, //FIXME + USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK, + NULL); + + // + // setup completion routine + // + IoSetCompletionRoutine(Irp, USBSTOR_CSWCompletionRoutine, Context, TRUE, TRUE, TRUE); + } + + // + // initialize stack location + // + IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; + IoStack->Parameters.Others.Argument1 = (PVOID)&Context->Urb; + IoStack->Parameters.DeviceIoControl.InputBufferLength = Context->Urb.UrbHeader.Length; + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // call driver + // + IoCallDriver(Context->FDODeviceExtension->LowerDeviceObject, Irp); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +USBSTOR_SendRequest( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP OriginalRequest, + IN OPTIONAL PKEVENT Event, + IN UCHAR CommandLength, + IN PUCHAR Command, + IN ULONG TransferDataLength, + IN PUCHAR TransferData) +{ + PIRP_CONTEXT Context; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + PIRP Irp; + PIO_STACK_LOCATION IoStack; + PUCHAR MdlVirtualAddress; + + // + // first allocate irp context + // + Context = USBSTOR_AllocateIrpContext(); + if (!Context) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get PDO device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension; + + // + // now build the cbw + // + USBSTOR_BuildCBW((ULONG)Context->cbw, + TransferDataLength, + PDODeviceExtension->LUN, + CommandLength, + Command, + Context->cbw); + + DPRINT("CBW %p\n", Context->cbw); + + // + // now initialize the urb + // + UsbBuildInterruptOrBulkTransferRequest(&Context->Urb, + sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), + FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle, + Context->cbw, + NULL, + sizeof(CBW), + USBD_TRANSFER_DIRECTION_OUT, + NULL); + + // + // initialize rest of context + // + Context->Irp = OriginalRequest; + Context->TransferData = TransferData; + Context->TransferDataLength = TransferDataLength; + Context->FDODeviceExtension = FDODeviceExtension; + Context->PDODeviceExtension = PDODeviceExtension; + Context->Event = Event; + + // + // is there transfer data + // + if (Context->TransferDataLength) + { + // + // check if the original request already does have an mdl associated + // + if (OriginalRequest) + { + if ((OriginalRequest->MdlAddress != NULL) && + (Context->TransferData == NULL || Command[0] == SCSIOP_READ || Command[0] == SCSIOP_WRITE)) + { + // + // Sanity check that the Mdl does describe the TransferData for read/write + // + if (CommandLength == UFI_READ_WRITE_CMD_LEN) + { + MdlVirtualAddress = MmGetMdlVirtualAddress(OriginalRequest->MdlAddress); + + // + // is there an offset + // + if (MdlVirtualAddress != Context->TransferData) + { + // + // lets build an mdl + // + Context->TransferBufferMDL = IoAllocateMdl(Context->TransferData, MmGetMdlByteCount(OriginalRequest->MdlAddress), FALSE, FALSE, NULL); + if (!Context->TransferBufferMDL) + { + // + // failed to allocate MDL + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // now build the partial mdl + // + IoBuildPartialMdl(OriginalRequest->MdlAddress, Context->TransferBufferMDL, Context->TransferData, Context->TransferDataLength); + } + } + + if (!Context->TransferBufferMDL) + { + // + // I/O paging request + // + Context->TransferBufferMDL = OriginalRequest->MdlAddress; + } + } + else + { + // + // allocate mdl for buffer, buffer must be allocated from NonPagedPool + // + Context->TransferBufferMDL = IoAllocateMdl(Context->TransferData, Context->TransferDataLength, FALSE, FALSE, NULL); + if (!Context->TransferBufferMDL) + { + // + // failed to allocate MDL + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build mdl for nonpaged pool + // + MmBuildMdlForNonPagedPool(Context->TransferBufferMDL); + } + } + else + { + // + // allocate mdl for buffer, buffer must be allocated from NonPagedPool + // + Context->TransferBufferMDL = IoAllocateMdl(Context->TransferData, Context->TransferDataLength, FALSE, FALSE, NULL); + if (!Context->TransferBufferMDL) + { + // + // failed to allocate MDL + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // build mdl for nonpaged pool + // + MmBuildMdlForNonPagedPool(Context->TransferBufferMDL); + } + } + + // + // now allocate the request + // + Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); + if (!Irp) + { + FreeItem(Context->cbw); + FreeItem(Context); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get next stack location + // + IoStack = IoGetNextIrpStackLocation(Irp); + + // + // initialize stack location + // + IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; + IoStack->Parameters.Others.Argument1 = (PVOID)&Context->Urb; + IoStack->Parameters.DeviceIoControl.InputBufferLength = Context->Urb.UrbHeader.Length; + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // setup completion routine + // + IoSetCompletionRoutine(Irp, USBSTOR_CBWCompletionRoutine, Context, TRUE, TRUE, TRUE); + + if (OriginalRequest) + { + // + // mark orignal irp as pending + // + IoMarkIrpPending(OriginalRequest); + } + + // + // call driver + // + IoCallDriver(FDODeviceExtension->LowerDeviceObject, Irp); + + // + // done + // + return STATUS_PENDING; +} + +NTSTATUS +USBSTOR_SendInquiryCmd( + IN PDEVICE_OBJECT DeviceObject) +{ + UFI_INQUIRY_CMD Cmd; + NTSTATUS Status; + KEVENT Event; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PUFI_INQUIRY_RESPONSE Response; + + + // + // allocate inquiry response + // + Response = AllocateItem(NonPagedPool, PAGE_SIZE); + if (!Response) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get PDO device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // initialize inquiry cmd + // + RtlZeroMemory(&Cmd, sizeof(UFI_INQUIRY_CMD)); + Cmd.Code = SCSIOP_INQUIRY; + Cmd.LUN = (PDODeviceExtension->LUN & MAX_LUN); + Cmd.AllocationLength = sizeof(UFI_INQUIRY_RESPONSE); + + // + // initialize event + // + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + // + // now send the request + // + Status = USBSTOR_SendRequest(DeviceObject, NULL, &Event, UFI_INQUIRY_CMD_LEN, (PUCHAR)&Cmd, sizeof(UFI_INQUIRY_RESPONSE), (PUCHAR)Response); + + // + // wait for the action to complete + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + KeResetEvent(&Event); + DPRINT("Resending request\n"); + + // + // now send the request + // + Status = USBSTOR_SendRequest(DeviceObject, NULL, &Event, UFI_INQUIRY_CMD_LEN, (PUCHAR)&Cmd, sizeof(UFI_INQUIRY_RESPONSE), (PUCHAR)Response); + + // + // wait for the action to complete + // + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + + + + DPRINT1("Response %p\n", Response); + DPRINT1("DeviceType %x\n", Response->DeviceType); + DPRINT1("RMB %x\n", Response->RMB); + DPRINT1("Version %x\n", Response->Version); + DPRINT1("Format %x\n", Response->Format); + DPRINT1("Length %x\n", Response->Length); + DPRINT1("Reserved %x\n", Response->Reserved); + DPRINT1("Vendor %c%c%c%c%c%c%c%c\n", Response->Vendor[0], Response->Vendor[1], Response->Vendor[2], Response->Vendor[3], Response->Vendor[4], Response->Vendor[5], Response->Vendor[6], Response->Vendor[7]); + DPRINT1("Product %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", Response->Product[0], Response->Product[1], Response->Product[2], Response->Product[3], + Response->Product[4], Response->Product[5], Response->Product[6], Response->Product[7], + Response->Product[8], Response->Product[9], Response->Product[10], Response->Product[11], + Response->Product[12], Response->Product[13], Response->Product[14], Response->Product[15]); + + DPRINT1("Revision %c%c%c%c\n", Response->Revision[0], Response->Revision[1], Response->Revision[2], Response->Revision[3]); + + // + // store inquiry data + // + PDODeviceExtension->InquiryData = (PVOID)Response; + + // + // done + // + return Status; +} + +NTSTATUS +USBSTOR_SendCapacityCmd( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + UFI_CAPACITY_CMD Cmd; + PUFI_CAPACITY_RESPONSE Response; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + + // + // get PDO device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + + // + // get FDO device extension + // + FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension; + + + // + // allocate capacity response + // + Response = (PUFI_CAPACITY_RESPONSE)AllocateItem(NonPagedPool, PAGE_SIZE); + if (!Response) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // initialize capacity cmd + // + RtlZeroMemory(&Cmd, sizeof(UFI_INQUIRY_CMD)); + Cmd.Code = SCSIOP_READ_CAPACITY; + Cmd.LUN = (PDODeviceExtension->LUN & MAX_LUN); + + // + // send request, response will be freed in completion routine + // + return USBSTOR_SendRequest(DeviceObject, Irp, NULL, UFI_READ_CAPACITY_CMD_LEN, (PUCHAR)&Cmd, sizeof(UFI_CAPACITY_RESPONSE), (PUCHAR)Response); +} + +NTSTATUS +USBSTOR_SendModeSenseCmd( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ +#if 0 + UFI_SENSE_CMD Cmd; + NTSTATUS Status; + PVOID Response; + PCBW OutControl; + PCDB pCDB; + PUFI_MODE_PARAMETER_HEADER Header; +#endif + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PIO_STACK_LOCATION IoStack; + PSCSI_REQUEST_BLOCK Request; + + // + // get PDO device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get request block + // + Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1; + + RtlZeroMemory(Request->DataBuffer, Request->DataTransferLength); + Request->SrbStatus = SRB_STATUS_SUCCESS; + Irp->IoStatus.Information = Request->DataTransferLength; + Irp->IoStatus.Status = STATUS_SUCCESS; + USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Irp); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // start next request + // + USBSTOR_QueueNextRequest(PDODeviceExtension->LowerDeviceObject); + + return STATUS_SUCCESS; + +#if 0 + // + // get SCSI command data block + // + pCDB = (PCDB)Request->Cdb; + + // + // get PDO device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // allocate sense response from non paged pool + // + Response = (PUFI_CAPACITY_RESPONSE)AllocateItem(NonPagedPool, Request->DataTransferLength); + if (!Response) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // sanity check + // + + + // Supported pages + // MODE_PAGE_ERROR_RECOVERY + // MODE_PAGE_FLEXIBILE + // MODE_PAGE_LUN_MAPPING + // MODE_PAGE_FAULT_REPORTING + // MODE_SENSE_RETURN_ALL + + // + // initialize mode sense cmd + // + RtlZeroMemory(&Cmd, sizeof(UFI_INQUIRY_CMD)); + Cmd.Code = SCSIOP_MODE_SENSE; + Cmd.LUN = (PDODeviceExtension->LUN & MAX_LUN); + Cmd.PageCode = pCDB->MODE_SENSE.PageCode; + Cmd.PC = pCDB->MODE_SENSE.Pc; + Cmd.AllocationLength = HTONS(pCDB->MODE_SENSE.AllocationLength); + + DPRINT1("PageCode %x\n", pCDB->MODE_SENSE.PageCode); + DPRINT1("PC %x\n", pCDB->MODE_SENSE.Pc); + + // + // now send mode sense cmd + // + Status = USBSTOR_SendCBW(DeviceObject, UFI_SENSE_CMD_LEN, (PUCHAR)&Cmd, Request->DataTransferLength, &OutControl); + if (!NT_SUCCESS(Status)) + { + // + // failed to send CBW + // + DPRINT1("USBSTOR_SendCapacityCmd> USBSTOR_SendCBW failed with %x\n", Status); + FreeItem(Response); + ASSERT(FALSE); + return Status; + } + + // + // now send data block response + // + Status = USBSTOR_SendData(DeviceObject, Request->DataTransferLength, Response); + if (!NT_SUCCESS(Status)) + { + // + // failed to send CBW + // + DPRINT1("USBSTOR_SendCapacityCmd> USBSTOR_SendData failed with %x\n", Status); + FreeItem(Response); + ASSERT(FALSE); + return Status; + } + + Header = (PUFI_MODE_PARAMETER_HEADER)Response; + + // + // TODO: build layout + // + // first struct is the header + // MODE_PARAMETER_HEADER / _MODE_PARAMETER_HEADER10 + // + // followed by + // MODE_PARAMETER_BLOCK + // + // + UNIMPLEMENTED + + // + // send csw + // + Status = USBSTOR_SendCSW(DeviceObject, OutControl, 512, &CSW); + + DPRINT1("------------------------\n"); + DPRINT1("CSW %p\n", &CSW); + DPRINT1("Signature %x\n", CSW.Signature); + DPRINT1("Tag %x\n", CSW.Tag); + DPRINT1("DataResidue %x\n", CSW.DataResidue); + DPRINT1("Status %x\n", CSW.Status); + + // + // FIXME: handle error + // + ASSERT(CSW.Status == 0); + ASSERT(CSW.DataResidue == 0); + + // + // calculate transfer length + // + *TransferBufferLength = Request->DataTransferLength - CSW.DataResidue; + + // + // copy buffer + // + RtlCopyMemory(Request->DataBuffer, Response, *TransferBufferLength); + + // + // free item + // + FreeItem(OutControl); + + // + // free response + // + FreeItem(Response); + + // + // done + // + return Status; +#endif +} + +NTSTATUS +USBSTOR_SendReadWriteCmd( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + UFI_READ_WRITE_CMD Cmd; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PCDB pCDB; + ULONG BlockCount; + PIO_STACK_LOCATION IoStack; + PSCSI_REQUEST_BLOCK Request; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get request block + // + Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1; + + // + // get SCSI command data block + // + pCDB = (PCDB)Request->Cdb; + + // + // get PDO device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // informal debug print + // + DPRINT("USBSTOR_SendReadWriteCmd DataTransferLength %lu, BlockLength %lu\n", Request->DataTransferLength, PDODeviceExtension->BlockLength); + + // + // sanity check + // + ASSERT(PDODeviceExtension->BlockLength); + + // + // block count + // + BlockCount = Request->DataTransferLength / PDODeviceExtension->BlockLength; + + // + // initialize read cmd + // + RtlZeroMemory(&Cmd, sizeof(UFI_READ_WRITE_CMD)); + Cmd.Code = pCDB->AsByte[0]; + Cmd.LUN = (PDODeviceExtension->LUN & MAX_LUN); + Cmd.ContiguousLogicBlocksByte0 = pCDB->CDB10.TransferBlocksMsb; + Cmd.ContiguousLogicBlocksByte1 = pCDB->CDB10.TransferBlocksLsb; + Cmd.LogicalBlockByte0 = pCDB->CDB10.LogicalBlockByte0; + Cmd.LogicalBlockByte1 = pCDB->CDB10.LogicalBlockByte1; + Cmd.LogicalBlockByte2 = pCDB->CDB10.LogicalBlockByte2; + Cmd.LogicalBlockByte3 = pCDB->CDB10.LogicalBlockByte3; + + DPRINT("USBSTOR_SendReadWriteCmd BlockAddress %x%x%x%x BlockCount %lu BlockLength %lu\n", Cmd.LogicalBlockByte0, Cmd.LogicalBlockByte1, Cmd.LogicalBlockByte2, Cmd.LogicalBlockByte3, BlockCount, PDODeviceExtension->BlockLength); + + // + // send request + // + return USBSTOR_SendRequest(DeviceObject, Irp, NULL, UFI_READ_WRITE_CMD_LEN, (PUCHAR)&Cmd, Request->DataTransferLength, (PUCHAR)Request->DataBuffer); +} + +NTSTATUS +USBSTOR_SendTestUnitCmd( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp) +{ + UFI_TEST_UNIT_CMD Cmd; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PIO_STACK_LOCATION IoStack; + PSCSI_REQUEST_BLOCK Request; + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get request block + // + Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1; + + // + // no transfer length + // + ASSERT(Request->DataTransferLength == 0); + + // + // get PDO device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // initialize test unit cmd + // + RtlZeroMemory(&Cmd, sizeof(UFI_TEST_UNIT_CMD)); + Cmd.Code = SCSIOP_TEST_UNIT_READY; + Cmd.LUN = (PDODeviceExtension->LUN & MAX_LUN); + + // + // send the request + // + return USBSTOR_SendRequest(DeviceObject, Irp, NULL, UFI_TEST_UNIT_CMD_LEN, (PUCHAR)&Cmd, 0, NULL); +} + + +NTSTATUS +USBSTOR_HandleExecuteSCSI( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PCDB pCDB; + NTSTATUS Status; + PIO_STACK_LOCATION IoStack; + PSCSI_REQUEST_BLOCK Request; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + + // + // get PDO device extension + // + PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // sanity check + // + ASSERT(PDODeviceExtension->Common.IsFDO == FALSE); + + // + // get current stack location + // + IoStack = IoGetCurrentIrpStackLocation(Irp); + + // + // get request block + // + Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1; + + // + // get SCSI command data block + // + pCDB = (PCDB)Request->Cdb; + + DPRINT("USBSTOR_HandleExecuteSCSI Operation Code %x\n", pCDB->AsByte[0]); + + if (pCDB->AsByte[0] == SCSIOP_READ_CAPACITY) + { + // + // sanity checks + // + ASSERT(Request->DataBuffer); + + DPRINT("SCSIOP_READ_CAPACITY Length %lu\n", Request->DataTransferLength); + Status = USBSTOR_SendCapacityCmd(DeviceObject, Irp); + } + else if (pCDB->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) + { + DPRINT("SCSIOP_MODE_SENSE DataTransferLength %lu\n", Request->DataTransferLength); + ASSERT(pCDB->MODE_SENSE.AllocationLength == Request->DataTransferLength); + ASSERT(Request->DataBuffer); + + // + // send mode sense command + // + Status = USBSTOR_SendModeSenseCmd(DeviceObject, Irp); + } + else if (pCDB->MODE_SENSE.OperationCode == SCSIOP_READ || pCDB->MODE_SENSE.OperationCode == SCSIOP_WRITE) + { + DPRINT("SCSIOP_READ / SCSIOP_WRITE DataTransferLength %lu\n", Request->DataTransferLength); + + // + // send read / write command + // + Status = USBSTOR_SendReadWriteCmd(DeviceObject, Irp); + } + else if (pCDB->AsByte[0] == SCSIOP_MEDIUM_REMOVAL) + { + DPRINT("SCSIOP_MEDIUM_REMOVAL\n"); + + // + // just complete the request + // + Request->SrbStatus = SRB_STATUS_SUCCESS; + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = Request->DataTransferLength; + USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Irp); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // start next request + // + USBSTOR_QueueNextRequest(PDODeviceExtension->LowerDeviceObject); + + return STATUS_SUCCESS; + } + else if (pCDB->MODE_SENSE.OperationCode == SCSIOP_TEST_UNIT_READY) + { + DPRINT("SCSIOP_TEST_UNIT_READY\n"); + + // + // send test unit command + // + Status = USBSTOR_SendTestUnitCmd(DeviceObject, Irp); + } + else + { + DPRINT1("UNIMPLEMENTED Operation Code %x\n", pCDB->AsByte[0]); + Request->SrbStatus = SRB_STATUS_ERROR; + Status = STATUS_NOT_SUPPORTED; + DbgBreakPoint(); + } + + return Status; +} diff --git a/reactos/drivers/usb/usbstor/usbstor.c b/reactos/drivers/usb/usbstor/usbstor.c index ef8cd7e307e..50fb0bf2670 100644 --- a/reactos/drivers/usb/usbstor/usbstor.c +++ b/reactos/drivers/usb/usbstor/usbstor.c @@ -5,6 +5,7 @@ * PURPOSE: USB block storage device driver. * PROGRAMMERS: * James Tabor + Johannes Anderwald */ /* INCLUDES ******************************************************************/ @@ -15,84 +16,199 @@ /* PUBLIC AND PRIVATE FUNCTIONS **********************************************/ -NTSTATUS NTAPI -IrpStub(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) +NTSTATUS +NTAPI +USBSTOR_AddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT PhysicalDeviceObject) { - NTSTATUS Status = STATUS_NOT_SUPPORTED; + NTSTATUS Status; + PDEVICE_OBJECT DeviceObject; + PFDO_DEVICE_EXTENSION DeviceExtension; + + // + // lets create the device + // + Status = IoCreateDevice(DriverObject, sizeof(FDO_DEVICE_EXTENSION), 0, FILE_DEVICE_BUS_EXTENDER, FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject); + + // + // check for success + // + if (!NT_SUCCESS(Status)) + { + DPRINT1("USBSTOR_AddDevice: Failed to create FDO Status %x\n", Status); + return Status; + } + + // + // get device extension + // + DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + ASSERT(DeviceExtension); + + // + // zero device extension + // + RtlZeroMemory(DeviceExtension, sizeof(FDO_DEVICE_EXTENSION)); + + // + // initialize device extension + // + DeviceExtension->Common.IsFDO = TRUE; + DeviceExtension->FunctionalDeviceObject = DeviceObject; + DeviceExtension->PhysicalDeviceObject = PhysicalDeviceObject; + DeviceExtension->LowerDeviceObject = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject); + + // + // did attaching fail + // + if (!DeviceExtension->LowerDeviceObject) + { + // + // device removed + // + IoDeleteDevice(DeviceObject); + + return STATUS_DEVICE_REMOVED; + } + + // + // set device flags + // + DeviceObject->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE; + + // + // device is initialized + // + DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + + // + // done + // + return STATUS_SUCCESS; +} + +VOID +NTAPI +USBSTOR_Unload( + PDRIVER_OBJECT DriverObject) +{ + // + // no-op + // +} + +NTSTATUS +NTAPI +USBSTOR_DispatchClose( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + // + // function always succeeds ;) + // + DPRINT("USBSTOR_DispatchClose\n"); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + + +NTSTATUS +NTAPI +USBSTOR_DispatchDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + NTSTATUS Status; + + // + // handle requests + // + Status = USBSTOR_HandleDeviceControl(DeviceObject, Irp); + + // + // complete request + // Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); + + // + // done + // return Status; } -NTSTATUS NTAPI -AddDevice(IN PDRIVER_OBJECT DriverObject, - IN PDEVICE_OBJECT pdo) + +NTSTATUS +NTAPI +USBSTOR_DispatchScsi( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) { - return STATUS_SUCCESS; + // + // handle requests + // + return USBSTOR_HandleInternalDeviceControl(DeviceObject, Irp); } -VOID NTAPI -DriverUnload(PDRIVER_OBJECT DriverObject) -{ -} - -VOID NTAPI -StartIo(PUSBSTOR_DEVICE_EXTENSION DeviceExtension, - PIRP Irp) -{ -} - -static NTSTATUS NTAPI -DispatchClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) +NTSTATUS +NTAPI +USBSTOR_DispatchReadWrite( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) { + // + // read write ioctl is not supported + // Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(Irp, IO_NO_INCREMENT); - return STATUS_SUCCESS; + return STATUS_INVALID_PARAMETER; } -static NTSTATUS NTAPI -DispatchCleanup(PDEVICE_OBJECT DeviceObject, PIRP Irp) +NTSTATUS +NTAPI +USBSTOR_DispatchPnp( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) { - return STATUS_SUCCESS; + PUSBSTOR_COMMON_DEVICE_EXTENSION DeviceExtension; + + // + // get common device extension + // + DeviceExtension = (PUSBSTOR_COMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + + // + // is it for the FDO + // + if (DeviceExtension->IsFDO) + { + // + // dispatch pnp request to fdo pnp handler + // + return USBSTOR_FdoHandlePnp(DeviceObject, Irp); + } + else + { + // + // dispatch request to pdo pnp handler + // + return USBSTOR_PdoHandlePnp(DeviceObject, Irp); + } } -static NTSTATUS NTAPI -DispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) +NTSTATUS +NTAPI +USBSTOR_DispatchPower( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) { - return STATUS_SUCCESS; -} + UNIMPLEMENTED - -static NTSTATUS NTAPI -DispatchScsi(PDEVICE_OBJECT DeviceObject, PIRP Irp) -{ - return STATUS_SUCCESS; -} - -static NTSTATUS NTAPI -DispatchReadWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp) -{ - return STATUS_SUCCESS; -} - -static NTSTATUS NTAPI -DispatchSystemControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) -{ - return STATUS_SUCCESS; -} - -static NTSTATUS NTAPI -DispatchPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) -{ - return STATUS_SUCCESS; -} - -static NTSTATUS NTAPI -DispatchPower(PDEVICE_OBJECT fido, PIRP Irp) -{ - DPRINT1("USBSTOR: IRP_MJ_POWER unimplemented\n"); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); @@ -101,37 +217,61 @@ DispatchPower(PDEVICE_OBJECT fido, PIRP Irp) -/* - * Standard DriverEntry method. - */ -NTSTATUS NTAPI -DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegPath) +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegPath) { - ULONG i; DPRINT("********* USB Storage *********\n"); - DriverObject->DriverUnload = DriverUnload; - DriverObject->DriverExtension->AddDevice = AddDevice; + // + // driver unload routine + // + DriverObject->DriverUnload = USBSTOR_Unload; - for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) - DriverObject->MajorFunction[i] = IrpStub; + // + // add device function + // + DriverObject->DriverExtension->AddDevice = USBSTOR_AddDevice; - DriverObject->DriverStartIo = (PVOID)StartIo; + // + // driver start i/o routine + // + DriverObject->DriverStartIo = USBSTOR_StartIo; - DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchClose; - DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; - DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DispatchCleanup; - DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDeviceControl; - DriverObject->MajorFunction[IRP_MJ_READ] = DispatchReadWrite; - DriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchReadWrite; + // + // create / close + // + DriverObject->MajorFunction[IRP_MJ_CREATE] = USBSTOR_DispatchClose; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = USBSTOR_DispatchClose; - /* Scsi Miniport support */ - DriverObject->MajorFunction[IRP_MJ_SCSI] = DispatchScsi; - DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = DispatchSystemControl; + // + // scsi pass through requests + // + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = USBSTOR_DispatchDeviceControl; - DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp; - DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower; + // + // irp dispatch read / write + // + DriverObject->MajorFunction[IRP_MJ_READ] = USBSTOR_DispatchReadWrite; + DriverObject->MajorFunction[IRP_MJ_WRITE] = USBSTOR_DispatchReadWrite; + + // + // scsi queue ioctl + // + DriverObject->MajorFunction[IRP_MJ_SCSI] = USBSTOR_DispatchScsi; + + // + // pnp processing + // + DriverObject->MajorFunction[IRP_MJ_PNP] = USBSTOR_DispatchPnp; + + // + // power processing + // + DriverObject->MajorFunction[IRP_MJ_POWER] = USBSTOR_DispatchPower; return STATUS_SUCCESS; } diff --git a/reactos/drivers/usb/usbstor/usbstor.h b/reactos/drivers/usb/usbstor/usbstor.h index 9793db758b9..dbc8ef51ae7 100644 --- a/reactos/drivers/usb/usbstor/usbstor.h +++ b/reactos/drivers/usb/usbstor/usbstor.h @@ -1,83 +1,460 @@ + +#pragma once + #include -#include -#include -#include +#define NDEBUG #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define USB_STOR_TAG 'sbsu' #define USB_MAXCHILDREN (16) -NTSTATUS NTAPI -IoAttachDeviceToDeviceStackSafe( - IN PDEVICE_OBJECT SourceDevice, - IN PDEVICE_OBJECT TargetDevice, - OUT PDEVICE_OBJECT *AttachedToDeviceObject); -typedef struct _USBSTOR_DEVICE_EXTENSION + +#define HTONS(n) (((((unsigned short)(n) & 0xFF)) << 8) | (((unsigned short)(n) & 0xFF00) >> 8)) +#define NTOHS(n) (((((unsigned short)(n) & 0xFF)) << 8) | (((unsigned short)(n) & 0xFF00) >> 8)) + +#define HTONL(n) (((((unsigned long)(n) & 0xFF)) << 24) | \ + ((((unsigned long)(n) & 0xFF00)) << 8) | \ + ((((unsigned long)(n) & 0xFF0000)) >> 8) | \ + ((((unsigned long)(n) & 0xFF000000)) >> 24)) + + +#define NTOHL(n) (((((unsigned long)(n) & 0xFF)) << 24) | \ + ((((unsigned long)(n) & 0xFF00)) << 8) | \ + ((((unsigned long)(n) & 0xFF0000)) >> 8) | \ + ((((unsigned long)(n) & 0xFF000000)) >> 24)) + +#define USB_RECOVERABLE_ERRORS (USBD_STATUS_STALL_PID | USBD_STATUS_DEV_NOT_RESPONDING \ + | USBD_STATUS_ENDPOINT_HALTED | USBD_STATUS_NO_BANDWIDTH) + +typedef struct __COMMON_DEVICE_EXTENSION__ { - BOOLEAN IsFDO; - struct usb_device* dev; - PDEVICE_OBJECT LowerDevice; + BOOLEAN IsFDO; - PDEVICE_OBJECT Children[USB_MAXCHILDREN]; +}USBSTOR_COMMON_DEVICE_EXTENSION, *PUSBSTOR_COMMON_DEVICE_EXTENSION; - /* Fields valid only when IsFDO == FALSE */ - UNICODE_STRING DeviceDescription; // REG_SZ - UNICODE_STRING DeviceId; // REG_SZ - UNICODE_STRING InstanceId; // REG_SZ - UNICODE_STRING HardwareIds; // REG_MULTI_SZ - UNICODE_STRING CompatibleIds; // REG_MULTI_SZ - UNICODE_STRING SymbolicLinkName; -} USBSTOR_DEVICE_EXTENSION, *PUSBSTOR_DEVICE_EXTENSION; +typedef struct +{ + USBSTOR_COMMON_DEVICE_EXTENSION Common; // common device extension + + PDEVICE_OBJECT FunctionalDeviceObject; // functional device object + PDEVICE_OBJECT PhysicalDeviceObject; // physical device object + PDEVICE_OBJECT LowerDeviceObject; // lower device object + USB_BUS_INTERFACE_USBDI_V2 BusInterface; // bus interface of device + PUSB_DEVICE_DESCRIPTOR DeviceDescriptor; // usb device descriptor + PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor; // usb configuration descriptor + PUSB_STRING_DESCRIPTOR SerialNumber; // usb serial number + PUSBD_INTERFACE_INFORMATION InterfaceInformation; // usb interface information + USBD_CONFIGURATION_HANDLE ConfigurationHandle; // usb configuration handle + UCHAR BulkInPipeIndex; // bulk in pipe index + UCHAR BulkOutPipeIndex; // bulk out pipe index + UCHAR MaxLUN; // max lun for device + PDEVICE_OBJECT ChildPDO[16]; // max 16 child pdo devices + KSPIN_LOCK IrpListLock; // irp list lock + LIST_ENTRY IrpListHead; // irp list head + BOOLEAN IrpListFreeze; // if true the irp list is freezed + BOOLEAN ResetInProgress; // if hard reset is in progress + ULONG IrpPendingCount; // count of irp pending + PSCSI_REQUEST_BLOCK ActiveSrb; // stores the current active SRB + KEVENT NoPendingRequests; // set if no pending or in progress requests +}FDO_DEVICE_EXTENSION, *PFDO_DEVICE_EXTENSION; + +typedef struct +{ + USBSTOR_COMMON_DEVICE_EXTENSION Common; + PDEVICE_OBJECT LowerDeviceObject; // points to FDO + UCHAR LUN; // lun id + PVOID InquiryData; // USB SCSI inquiry data + UCHAR Claimed; // indicating if it has been claimed by upper driver + ULONG BlockLength; // length of block + ULONG LastLogicBlockAddress; // last block address + PDEVICE_OBJECT *PDODeviceObject; // entry in pdo list +}PDO_DEVICE_EXTENSION, *PPDO_DEVICE_EXTENSION; + +// +// max lun command identifier +// +#define USB_BULK_GET_MAX_LUN 0xFE +#define USB_BULK_RESET_DEVICE 0xFF + +#include +typedef struct +{ + ULONG Signature; // CBW signature + ULONG Tag; // CBW Tag of operation + ULONG DataTransferLength; // data transfer length + UCHAR Flags; // CBW Flags endpoint direction + UCHAR LUN; // lun unit + UCHAR CommandBlockLength; // Command block length + UCHAR CommandBlock[16]; +}CBW, *PCBW; + +C_ASSERT(sizeof(CBW) == 31); -/* cleanup.c */ -NTSTATUS NTAPI -UsbStorCleanup( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +#define CBW_SIGNATURE 0x43425355 +#define CSW_SIGNATURE 0x53425355 -/* fdo.c */ -NTSTATUS NTAPI -UsbStorPnpFdo( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +#define MAX_LUN 0xF + +typedef struct +{ + ULONG Signature; // CSW signature + ULONG Tag; // CSW tag + ULONG DataResidue; // CSW data transfer diff + UCHAR Status; // CSW status +}CSW, *PCSW; + +//-------------------------------------------------------------------------------------------------------------------------------------------- +// +// UFI INQUIRY command +// +typedef struct +{ + UCHAR Code; // operation code 0x12 + UCHAR LUN; // lun address + UCHAR PageCode; // product data information, always 0x00 + UCHAR Reserved; // reserved 0x00 + UCHAR AllocationLength; // length of inquiry data to be returned, default 36 bytes + UCHAR Reserved1[7]; //reserved bytes 0x00 +}UFI_INQUIRY_CMD, *PUFI_INQUIRY_CMD; + +C_ASSERT(sizeof(UFI_INQUIRY_CMD) == 12); + +#define UFI_INQUIRY_CMD_LEN 0x6 + +// +// UFI INQUIRY command response +// +typedef struct +{ + UCHAR DeviceType; // device type + UCHAR RMB; // removable media bit + UCHAR Version; // contains version 0x00 + UCHAR Format; // response format + UCHAR Length; // additional length + UCHAR Reserved[3]; // reserved + UCHAR Vendor[8]; // vendor identification string + UCHAR Product[16]; // product identification string + UCHAR Revision[4]; // product revision code +}UFI_INQUIRY_RESPONSE, *PUFI_INQUIRY_RESPONSE; + +C_ASSERT(sizeof(UFI_INQUIRY_RESPONSE) == 36); + +//-------------------------------------------------------------------------------------------------------------------------------------------- +// +// UFI read cmd +// +typedef struct +{ + UCHAR Code; // operation code + UCHAR LUN; // lun + UCHAR LogicalBlockByte0; // lba byte 0 + UCHAR LogicalBlockByte1; // lba byte 1 + UCHAR LogicalBlockByte2; // lba byte 2 + UCHAR LogicalBlockByte3; // lba byte 3 + UCHAR Reserved; // reserved 0x00 + UCHAR ContiguousLogicBlocksByte0; // msb contigious logic blocks byte + UCHAR ContiguousLogicBlocksByte1; // msb contigious logic blocks + UCHAR Reserved1[3]; // reserved 0x00 +}UFI_READ_WRITE_CMD; + +C_ASSERT(sizeof(UFI_READ_WRITE_CMD) == 12); + +#define UFI_READ_WRITE_CMD_LEN (0xA) + +//-------------------------------------------------------------------------------------------------------------------------------------------- +// +// UFI read capacity cmd +// +typedef struct +{ + UCHAR Code; // operation code 0x25 + UCHAR LUN; // lun address + UCHAR LBA[4]; // logical block address, should be zero + UCHAR Reserved1[2]; // reserved 0x00 + UCHAR PMI; // PMI = 0x00 + UCHAR Reserved2[3]; // reserved 0x00 +}UFI_CAPACITY_CMD, *PUFI_CAPACITY_CMD; + +C_ASSERT(sizeof(UFI_CAPACITY_CMD) == 12); + +#define UFI_CAPACITY_CMD_LEN 0xA //FIXME support length 16 too if requested + +// +// UFI Read Capcacity command response +// +typedef struct +{ + ULONG LastLogicalBlockAddress; // last logical block address + ULONG BlockLength; // block length in bytes +}UFI_CAPACITY_RESPONSE, *PUFI_CAPACITY_RESPONSE; + +#define UFI_READ_CAPACITY_CMD_LEN 0xA +C_ASSERT(sizeof(UFI_CAPACITY_RESPONSE) == 8); + +//-------------------------------------------------------------------------------------------------------------------------------------------- +// +// UFI sense mode cmd +// +typedef struct +{ + UCHAR Code; // operation code + UCHAR LUN; // lun address + UCHAR PageCode:6; // page code selector + UCHAR PC:2; // type of parameters to be returned + UCHAR Reserved[4]; // reserved 0x00 + USHORT AllocationLength; // parameters length + UCHAR Reserved1[3]; +}UFI_SENSE_CMD, *PUFI_SENSE_CMD; + +C_ASSERT(sizeof(UFI_SENSE_CMD) == 12); + +#define UFI_SENSE_CMD_LEN (6) + +typedef struct +{ + USHORT ModeDataLength; // length of parameters for sense cmd + UCHAR MediumTypeCode; // 00 for mass storage, 0x94 for floppy + UCHAR WP:1; // write protect bit + UCHAR Reserved1:2; // reserved 00 + UCHAR DPOFUA:1; // should be zero + UCHAR Reserved2:4; // reserved + UCHAR Reserved[4]; // reserved +}UFI_MODE_PARAMETER_HEADER, *PUFI_MODE_PARAMETER_HEADER; + + +C_ASSERT(sizeof(UFI_MODE_PARAMETER_HEADER) == 8); + +typedef struct +{ + UCHAR PC; + UCHAR PageLength; + UCHAR Reserved1; + UCHAR ITM; + UCHAR Flags; + UCHAR Reserved[3]; +}UFI_TIMER_PROTECT_PAGE, *PUFI_TIMER_PROTECT_PAGE; +C_ASSERT(sizeof(UFI_TIMER_PROTECT_PAGE) == 8); + +//-------------------------------------------------------------------------------------------------------------------------------------------- +// +// UFI test unit command +// + +typedef struct +{ + UCHAR Code; // operation code 0x00 + UCHAR LUN; // lun + UCHAR Reserved[10]; // reserved 0x00 +}UFI_TEST_UNIT_CMD, *PUFI_TEST_UNIT_CMD; + +C_ASSERT(sizeof(UFI_TEST_UNIT_CMD) == 12); + +#define UFI_TEST_UNIT_CMD_LEN (6) + +typedef struct +{ + union + { + PCBW cbw; + PCSW csw; + }; + URB Urb; + PIRP Irp; + ULONG TransferDataLength; + PUCHAR TransferData; + PFDO_DEVICE_EXTENSION FDODeviceExtension; + PPDO_DEVICE_EXTENSION PDODeviceExtension; + PMDL TransferBufferMDL; + PKEVENT Event; +}IRP_CONTEXT, *PIRP_CONTEXT; + +typedef struct _ERRORHANDLER_WORKITEM_DATA +{ + PDEVICE_OBJECT DeviceObject; + PIRP_CONTEXT Context; + WORK_QUEUE_ITEM WorkQueueItem; +} ERRORHANDLER_WORKITEM_DATA, *PERRORHANDLER_WORKITEM_DATA; + + +//--------------------------------------------------------------------- +// +// fdo.c routines +// +NTSTATUS +USBSTOR_FdoHandlePnp( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp); + +//--------------------------------------------------------------------- +// +// pdo.c routines +// +NTSTATUS +USBSTOR_PdoHandlePnp( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp); NTSTATUS -UsbStorDeviceControlFdo( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +USBSTOR_CreatePDO( + IN PDEVICE_OBJECT DeviceObject, + OUT PDEVICE_OBJECT *ChildDeviceObject); -/* misc.c */ +//--------------------------------------------------------------------- +// +// misc.c routines +// NTSTATUS -ForwardIrpAndWait( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); - -NTSTATUS NTAPI -ForwardIrpAndForget( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +NTAPI +USBSTOR_SyncForwardIrp( + IN PDEVICE_OBJECT DeviceObject, + IN OUT PIRP Irp); NTSTATUS -UsbStorDuplicateUnicodeString( - OUT PUNICODE_STRING Destination, - IN PUNICODE_STRING Source, - IN POOL_TYPE PoolType); +NTAPI +USBSTOR_GetBusInterface( + IN PDEVICE_OBJECT DeviceObject, + OUT PUSB_BUS_INTERFACE_USBDI_V2 BusInterface); + +PVOID +AllocateItem( + IN POOL_TYPE PoolType, + IN ULONG ItemSize); + +VOID +FreeItem( + IN PVOID Item); NTSTATUS -UsbStorInitMultiSzString( - OUT PUNICODE_STRING Destination, - ... /* list of PCSZ */); - -/* pdo.c */ -NTSTATUS NTAPI -UsbStorPnpPdo( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +USBSTOR_SyncUrbRequest( + IN PDEVICE_OBJECT DeviceObject, + OUT PURB UrbRequest); NTSTATUS -UsbStorDeviceControlPdo( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp); +USBSTOR_GetMaxLUN( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension); + +NTSTATUS +NTAPI +USBSTOR_SyncForwardIrpCompletionRoutine( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context); + +NTSTATUS +USBSTOR_ResetDevice( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension); + +//--------------------------------------------------------------------- +// +// descriptor.c routines +// + +NTSTATUS +USBSTOR_GetDescriptors( + IN PDEVICE_OBJECT DeviceObject); + +NTSTATUS +USBSTOR_SelectConfigurationAndInterface( + IN PDEVICE_OBJECT DeviceObject, + IN PFDO_DEVICE_EXTENSION DeviceExtension); + +NTSTATUS +USBSTOR_GetPipeHandles( + IN PFDO_DEVICE_EXTENSION DeviceExtension); + +//--------------------------------------------------------------------- +// +// scsi.c routines +// +NTSTATUS +USBSTOR_HandleExecuteSCSI( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +USBSTOR_SendInquiryCmd( + IN PDEVICE_OBJECT DeviceObject); + +//--------------------------------------------------------------------- +// +// disk.c routines +// +NTSTATUS +USBSTOR_HandleInternalDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +NTSTATUS +USBSTOR_HandleDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +//--------------------------------------------------------------------- +// +// queue.c routines +// +VOID +NTAPI +USBSTOR_StartIo( + PDEVICE_OBJECT DeviceObject, + PIRP Irp); + +VOID +USBSTOR_QueueWaitForPendingRequests( + IN PDEVICE_OBJECT DeviceObject); + +VOID +USBSTOR_QueueRelease( + IN PDEVICE_OBJECT DeviceObject); + +BOOLEAN +USBSTOR_QueueAddIrp( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +VOID +NTAPI +USBSTOR_CancelIo( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +VOID +USBSTOR_QueueInitialize( + PFDO_DEVICE_EXTENSION FDODeviceExtension); + +VOID +NTAPI +ErrorHandlerWorkItemRoutine( + PVOID Context); + +VOID +USBSTOR_QueueNextRequest( + IN PDEVICE_OBJECT DeviceObject); + +VOID +USBSTOR_QueueTerminateRequest( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp); + +/* error.c */ +NTSTATUS +USBSTOR_GetEndpointStatus( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR bEndpointAddress, + OUT PUSHORT Value); diff --git a/reactos/drivers/usb/usbstor/usbstor.rbuild b/reactos/drivers/usb/usbstor/usbstor.rbuild index d7215fc8e92..ac4f9e62f68 100644 --- a/reactos/drivers/usb/usbstor/usbstor.rbuild +++ b/reactos/drivers/usb/usbstor/usbstor.rbuild @@ -1,10 +1,20 @@ + include ntoskrnl hal + usbd + descriptor.c + disk.c + fdo.c + misc.c + pdo.c + queue.c + error.c + scsi.c usbstor.c usbstor.rc diff --git a/reactos/hal/halx86/acpi/halpnpdd.c b/reactos/hal/halx86/acpi/halpnpdd.c index 8ca5dd3b029..771e19c6fd6 100644 --- a/reactos/hal/halx86/acpi/halpnpdd.c +++ b/reactos/hal/halx86/acpi/halpnpdd.c @@ -59,8 +59,8 @@ HalpReportDetectedDevices(IN PDRIVER_OBJECT DriverObject, PFDO_EXTENSION FdoExtension = Context; PPDO_EXTENSION PdoExtension; PDEVICE_OBJECT PdoDeviceObject; - NTSTATUS Status; PDESCRIPTION_HEADER Wdrt; + NTSTATUS Status; /* Create the PDO */ Status = IoCreateDevice(DriverObject, @@ -83,11 +83,11 @@ HalpReportDetectedDevices(IN PDRIVER_OBJECT DriverObject, PdoExtension->PhysicalDeviceObject = PdoDeviceObject; PdoExtension->ParentFdoExtension = FdoExtension; PdoExtension->PdoType = AcpiPdo; - + /* Add the PDO to the head of the list */ PdoExtension->Next = FdoExtension->ChildPdoList; FdoExtension->ChildPdoList = PdoExtension; - + /* Initialization is finished */ PdoDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; @@ -99,8 +99,8 @@ HalpReportDetectedDevices(IN PDRIVER_OBJECT DriverObject, DPRINT1("You have an ACPI Watchdog. That's great! You should be proud ;-)\n"); } - /* Invalidate device relations since we added a new device */ - IoInvalidateDeviceRelations(FdoExtension->PhysicalDeviceObject, BusRelations); + /* This will synchronously load the ACPI driver (needed because we're critical for boot) */ + IoSynchronousInvalidateDeviceRelations(FdoExtension->PhysicalDeviceObject, BusRelations); } NTSTATUS @@ -111,6 +111,7 @@ HalpAddDevice(IN PDRIVER_OBJECT DriverObject, NTSTATUS Status; PFDO_EXTENSION FdoExtension; PDEVICE_OBJECT DeviceObject, AttachedDevice; + DPRINT("HAL: PnP Driver ADD!\n"); /* Create the FDO */ @@ -151,9 +152,9 @@ HalpAddDevice(IN PDRIVER_OBJECT DriverObject, FdoExtension->AttachedDeviceObject = AttachedDevice; /* Register for reinitialization to report devices later */ - IoRegisterDriverReinitialization(DriverObject, - HalpReportDetectedDevices, - FdoExtension); + IoRegisterBootDriverReinitialization(DriverObject, + HalpReportDetectedDevices, + FdoExtension); /* Return status */ DPRINT("Device added %lx\n", Status); @@ -171,8 +172,7 @@ HalpQueryInterface(IN PDEVICE_OBJECT DeviceObject, OUT PULONG Length) { UNIMPLEMENTED; - while (TRUE); - return STATUS_NO_SUCH_DEVICE; + return STATUS_NOT_SUPPORTED; } NTSTATUS diff --git a/reactos/hal/halx86/legacy/bussupp.c b/reactos/hal/halx86/legacy/bussupp.c index b6f0b83c384..69dc4dba4e3 100644 --- a/reactos/hal/halx86/legacy/bussupp.c +++ b/reactos/hal/halx86/legacy/bussupp.c @@ -802,6 +802,7 @@ HalpDebugPciDumpBus(IN ULONG i, if (PciData->u.type0.InterruptPin != 0 && PciData->u.type0.InterruptLine != 0 && PciData->u.type0.InterruptLine != 0xFF) DbgPrint(", IRQ %02d", PciData->u.type0.InterruptLine); + else if (PciData->u.type0.InterruptPin != 0) DbgPrint(", IRQ assignment required"); DbgPrint("\n"); /* Scan addresses */ diff --git a/reactos/hal/halx86/legacy/halpnpdd.c b/reactos/hal/halx86/legacy/halpnpdd.c index c3d783d647e..d3a935f60b6 100644 --- a/reactos/hal/halx86/legacy/halpnpdd.c +++ b/reactos/hal/halx86/legacy/halpnpdd.c @@ -50,6 +50,50 @@ PDRIVER_OBJECT HalpDriverObject; /* PRIVATE FUNCTIONS **********************************************************/ +VOID +NTAPI +HalpReportDetectedDevices(IN PDRIVER_OBJECT DriverObject, + IN PVOID Context, + IN ULONG Count) +{ + PFDO_EXTENSION FdoExtension = Context; + PPDO_EXTENSION PdoExtension; + PDEVICE_OBJECT PdoDeviceObject; + NTSTATUS Status; + + /* Create the PDO */ + Status = IoCreateDevice(DriverObject, + sizeof(PDO_EXTENSION), + NULL, + FILE_DEVICE_BUS_EXTENDER, + FILE_AUTOGENERATED_DEVICE_NAME, + FALSE, + &PdoDeviceObject); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + DPRINT1("HAL: Could not create ACPI device object status=0x%08x\n", Status); + return; + } + + /* Setup the PDO device extension */ + PdoExtension = PdoDeviceObject->DeviceExtension; + PdoExtension->ExtensionType = PdoExtensionType; + PdoExtension->PhysicalDeviceObject = PdoDeviceObject; + PdoExtension->ParentFdoExtension = FdoExtension; + PdoExtension->PdoType = AcpiPdo; + + /* Add the PDO to the head of the list */ + PdoExtension->Next = FdoExtension->ChildPdoList; + FdoExtension->ChildPdoList = PdoExtension; + + /* Initialization is finished */ + PdoDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + /* Invalidate device relations since we added a new device */ + IoSynchronousInvalidateDeviceRelations(FdoExtension->PhysicalDeviceObject, BusRelations); +} + NTSTATUS NTAPI HalpAddDevice(IN PDRIVER_OBJECT DriverObject, @@ -57,9 +101,9 @@ HalpAddDevice(IN PDRIVER_OBJECT DriverObject, { NTSTATUS Status; PFDO_EXTENSION FdoExtension; - PPDO_EXTENSION PdoExtension; - PDEVICE_OBJECT DeviceObject, PdoDeviceObject, AttachedDevice; + PDEVICE_OBJECT DeviceObject, AttachedDevice; // PDESCRIPTION_HEADER Wdrt; + DPRINT("HAL: PnP Driver ADD!\n"); /* Create the FDO */ @@ -99,47 +143,10 @@ HalpAddDevice(IN PDRIVER_OBJECT DriverObject, /* Save the attachment */ FdoExtension->AttachedDeviceObject = AttachedDevice; - /* Create the PDO */ - Status = IoCreateDevice(DriverObject, - sizeof(PDO_EXTENSION), - NULL, - FILE_DEVICE_BUS_EXTENDER, - FILE_AUTOGENERATED_DEVICE_NAME, - FALSE, - &PdoDeviceObject); - if (!NT_SUCCESS(Status)) - { - /* Fail */ - DPRINT1("HAL: Could not create ACPI device object status=0x%08x\n", Status); - return Status; - } - - /* Setup the PDO device extension */ - PdoExtension = PdoDeviceObject->DeviceExtension; - PdoExtension->ExtensionType = PdoExtensionType; - PdoExtension->PhysicalDeviceObject = PdoDeviceObject; - PdoExtension->ParentFdoExtension = FdoExtension; - PdoExtension->PdoType = AcpiPdo; - - /* Add the PDO to the head of the list */ - PdoExtension->Next = FdoExtension->ChildPdoList; - FdoExtension->ChildPdoList = PdoExtension; - - /* Initialization is finished */ - PdoDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; - -#if 0 - /* Find the ACPI watchdog table */ - Wdrt = HalAcpiGetTable(0, 'TRDW'); - if (Wdrt) - { - /* FIXME: TODO */ - DPRINT1("You have an ACPI Watchdog. That's great! You should be proud ;-)\n"); - } -#endif - - /* Invalidate device relations since we added a new device */ - IoInvalidateDeviceRelations(TargetDevice, BusRelations); + /* Register for reinitialization to report devices later */ + IoRegisterBootDriverReinitialization(DriverObject, + HalpReportDetectedDevices, + FdoExtension); /* Return status */ DPRINT("Device added %lx\n", Status); @@ -157,8 +164,7 @@ HalpQueryInterface(IN PDEVICE_OBJECT DeviceObject, OUT PULONG Length) { UNIMPLEMENTED; - while (TRUE); - return STATUS_NO_SUCH_DEVICE; + return STATUS_NOT_SUPPORTED; } NTSTATUS diff --git a/reactos/include/ddk/hidclass.h b/reactos/include/ddk/hidclass.h index 69975003693..cdcc3eaa31a 100644 --- a/reactos/include/ddk/hidclass.h +++ b/reactos/include/ddk/hidclass.h @@ -73,16 +73,9 @@ DEFINE_GUID (GUID_HID_INTERFACE_HIDPARSE, \ #define IOCTL_HID_GET_DRIVER_CONFIG HID_BUFFER_CTL_CODE(100) #define IOCTL_HID_SET_DRIVER_CONFIG HID_BUFFER_CTL_CODE(101) +#define IOCTL_HID_GET_INDEXED_STRING HID_OUT_CTL_CODE(120) #define IOCTL_HID_GET_MS_GENRE_DESCRIPTOR HID_OUT_CTL_CODE(121) -/* FIXME: these values are wrong! */ -#define IOCTL_HID_GET_STRING 0 -#define IOCTL_HID_GET_DEVICE_ATTRIBUTES 1 -#define IOCTL_HID_GET_DEVICE_DESCRIPTOR 2 -#define IOCTL_HID_READ_REPORT 3 -#define IOCTL_HID_WRITE_REPORT 4 -#define IOCTL_HID_GET_REPORT_DESCRIPTOR 5 - typedef enum _HID_STRING_TYPE { HID_STRING_INDEXED = 0, diff --git a/reactos/include/ddk/hidpddi.h b/reactos/include/ddk/hidpddi.h new file mode 100644 index 00000000000..3f142308530 --- /dev/null +++ b/reactos/include/ddk/hidpddi.h @@ -0,0 +1,101 @@ +#ifndef _HIDPDDI_H +#define _HIDPDDI_H + +#include "hidusage.h" +#include "hidpi.h" + +typedef struct _HIDP_COLLECTION_DESC +{ + USAGE UsagePage; + USAGE Usage; + UCHAR CollectionNumber; + UCHAR Reserved [15]; + USHORT InputLength; + USHORT OutputLength; + USHORT FeatureLength; + USHORT PreparsedDataLength; + PHIDP_PREPARSED_DATA PreparsedData; +}HIDP_COLLECTION_DESC, *PHIDP_COLLECTION_DESC; + +typedef struct _HIDP_REPORT_IDS +{ + UCHAR ReportID; + UCHAR CollectionNumber; + USHORT InputLength; + USHORT OutputLength; + USHORT FeatureLength; +}HIDP_REPORT_IDS, *PHIDP_REPORT_IDS; + +typedef struct _HIDP_GETCOLDESC_DBG +{ + ULONG BreakOffset; + ULONG ErrorCode; + ULONG Args[6]; +}HIDP_GETCOLDESC_DBG, *PHIDP_GETCOLDESC_DBG; + +typedef struct _HIDP_DEVICE_DESC +{ + PHIDP_COLLECTION_DESC CollectionDesc; + ULONG CollectionDescLength; + PHIDP_REPORT_IDS ReportIDs; + ULONG ReportIDsLength; + HIDP_GETCOLDESC_DBG Dbg; +}HIDP_DEVICE_DESC, *PHIDP_DEVICE_DESC; + +NTSTATUS +NTAPI +HidP_GetCollectionDescription( + IN PHIDP_REPORT_DESCRIPTOR ReportDesc, + IN ULONG DescLength, + IN POOL_TYPE PoolType, + OUT PHIDP_DEVICE_DESC DeviceDescription +); + +VOID +NTAPI +HidP_FreeCollectionDescription ( + IN PHIDP_DEVICE_DESC DeviceDescription +); + +NTSTATUS +NTAPI +HidP_SysPowerEvent ( + IN PCHAR HidPacket, + IN USHORT HidPacketLength, + IN PHIDP_PREPARSED_DATA Ppd, + OUT PULONG OutputBuffer +); + +NTSTATUS +NTAPI +HidP_SysPowerCaps ( + IN PHIDP_PREPARSED_DATA Ppd, + OUT PULONG OutputBuffer +); + +#define HIDP_GETCOLDESC_SUCCESS 0x00 +#define HIDP_GETCOLDESC_RESOURCES 0x01 +#define HIDP_GETCOLDESC_BUFFER 0x02 +#define HIDP_GETCOLDESC_LINK_RESOURCES 0x03 +#define HIDP_GETCOLDESC_UNEXP_END_COL 0x04 +#define HIDP_GETCOLDESC_PREPARSE_RESOURCES 0x05 +#define HIDP_GETCOLDESC_ONE_BYTE 0x06 +#define HIDP_GETCOLDESC_TWO_BYTE 0x07 +#define HIDP_GETCOLDESC_FOUR_BYTE 0x08 +#define HIDP_GETCOLDESC_BYTE_ALLIGN 0x09 +#define HIDP_GETCOLDESC_MAIN_ITEM_NO_USAGE 0x0A +#define HIDP_GETCOLDESC_TOP_COLLECTION_USAGE 0x0B +#define HIDP_GETCOLDESC_PUSH_RESOURCES 0x10 +#define HIDP_GETCOLDESC_ITEM_UNKNOWN 0x12 +#define HIDP_GETCOLDESC_REPORT_ID 0x13 +#define HIDP_GETCOLDESC_BAD_REPORT_ID 0x14 +#define HIDP_GETCOLDESC_NO_REPORT_ID 0x15 +#define HIDP_GETCOLDESC_DEFAULT_ID_ERROR 0x16 +#define HIDP_GETCOLDESC_NO_DATA 0x1A +#define HIDP_GETCOLDESC_INVALID_MAIN_ITEM 0x1B +#define HIDP_GETCOLDESC_NO_CLOSE_DELIMITER 0x20 +#define HIDP_GETCOLDESC_NOT_VALID_DELIMITER 0x21 +#define HIDP_GETCOLDESC_MISMATCH_OC_DELIMITER 0x22 +#define HIDP_GETCOLDESC_UNSUPPORTED 0x40 + +#endif diff --git a/reactos/include/ddk/hidport.h b/reactos/include/ddk/hidport.h new file mode 100644 index 00000000000..e6819e9b52d --- /dev/null +++ b/reactos/include/ddk/hidport.h @@ -0,0 +1,100 @@ +#pragma once + +#include + +typedef struct _HID_MINIDRIVER_REGISTRATION +{ + ULONG Revision; + PDRIVER_OBJECT DriverObject; + PUNICODE_STRING RegistryPath; + ULONG DeviceExtensionSize; + BOOLEAN DevicesArePolled; + UCHAR Reserved[3]; +}HID_MINIDRIVER_REGISTRATION, *PHID_MINIDRIVER_REGISTRATION; + +typedef struct _HID_DEVICE_EXTENSION +{ + PDEVICE_OBJECT PhysicalDeviceObject; + PDEVICE_OBJECT NextDeviceObject; + PVOID MiniDeviceExtension; +}HID_DEVICE_EXTENSION, *PHID_DEVICE_EXTENSION; + +typedef struct _HID_DEVICE_ATTRIBUTES +{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + USHORT Reserved[11]; +}HID_DEVICE_ATTRIBUTES, * PHID_DEVICE_ATTRIBUTES; + +#include + +typedef struct _HID_DESCRIPTOR +{ + UCHAR bLength; + UCHAR bDescriptorType; + USHORT bcdHID; + UCHAR bCountry; + UCHAR bNumDescriptors; + + struct _HID_DESCRIPTOR_DESC_LIST + { + UCHAR bReportType; + USHORT wReportLength; + }DescriptorList [1]; +}HID_DESCRIPTOR, * PHID_DESCRIPTOR; + +#include + +#define HID_HID_DESCRIPTOR_TYPE 0x21 +#define HID_REPORT_DESCRIPTOR_TYPE 0x22 +#define HID_PHYSICAL_DESCRIPTOR_TYPE 0x23 + + + +typedef +VOID +(NTAPI *HID_SEND_IDLE_CALLBACK)( + IN PVOID Context +); + +typedef struct _HID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO +{ + HID_SEND_IDLE_CALLBACK IdleCallback; + PVOID IdleContext; +}HID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO, *PHID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO; + +NTSTATUS +NTAPI +HidRegisterMinidriver( + IN PHID_MINIDRIVER_REGISTRATION MinidriverRegistration +); + +#if(NTDDI_VERSION>=NTDDI_WINXPSP1) + +NTSTATUS +HidNotifyPresence( + IN PDEVICE_OBJECT DeviceObject, + IN BOOLEAN IsPresent +); + +#endif + +#define IOCTL_HID_GET_DEVICE_DESCRIPTOR HID_CTL_CODE(0) +#define IOCTL_HID_GET_REPORT_DESCRIPTOR HID_CTL_CODE(1) +#define IOCTL_HID_READ_REPORT HID_CTL_CODE(2) +#define IOCTL_HID_WRITE_REPORT HID_CTL_CODE(3) +#define IOCTL_HID_GET_STRING HID_CTL_CODE(4) +#define IOCTL_HID_ACTIVATE_DEVICE HID_CTL_CODE(7) +#define IOCTL_HID_DEACTIVATE_DEVICE HID_CTL_CODE(8) +#define IOCTL_HID_GET_DEVICE_ATTRIBUTES HID_CTL_CODE(9) +#define IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST HID_CTL_CODE(10) + +#define HID_HID_DESCRIPTOR_TYPE 0x21 +#define HID_REPORT_DESCRIPTOR_TYPE 0x22 +#define HID_PHYSICAL_DESCRIPTOR_TYPE 0x23 + +#define HID_STRING_ID_IMANUFACTURER 14 +#define HID_STRING_ID_IPRODUCT 15 +#define HID_STRING_ID_ISERIALNUMBER 16 diff --git a/reactos/include/ddk/ntddk.h b/reactos/include/ddk/ntddk.h index 74d2987aa78..b648308c084 100644 --- a/reactos/include/ddk/ntddk.h +++ b/reactos/include/ddk/ntddk.h @@ -4211,6 +4211,9 @@ IoTranslateBusAddress( #endif /* (NTDDI_VERSION >= NTDDI_WS03SP1) */ #if (NTDDI_VERSION >= NTDDI_VISTA) + +struct _DISK_GEOMETRY_EX; + NTKERNELAPI NTSTATUS NTAPI diff --git a/reactos/include/psdk/hidusage.h b/reactos/include/psdk/hidusage.h index 1827c19dcec..af3386ec8bc 100644 --- a/reactos/include/psdk/hidusage.h +++ b/reactos/include/psdk/hidusage.h @@ -199,6 +199,14 @@ typedef USHORT USAGE, *PUSAGE; #define HID_USAGE_SIMULATION_RUDDER ((USAGE) 0xBA) #define HID_USAGE_SIMULATION_THROTTLE ((USAGE) 0xBB) + +#define HID_USAGE_CONSUMERCTRL ((USAGE)0x01) +#define HID_USAGE_DIGITIZER_PEN ((USAGE)0x02) +#define HID_USAGE_DIGITIZER_IN_RANGE ((USAGE)0x32) +#define HID_USAGE_DIGITIZER_TIP_SWITCH ((USAGE)0x42) +#define HID_USAGE_DIGITIZER_BARREL_SWITCH ((USAGE)0x44) + + #ifdef __cplusplus } #endif diff --git a/reactos/include/psdk/usb.h b/reactos/include/psdk/usb.h index 0b738359759..100a92e9ad1 100644 --- a/reactos/include/psdk/usb.h +++ b/reactos/include/psdk/usb.h @@ -40,7 +40,7 @@ typedef PVOID PMDL; #endif #endif -#define USBDI_VERSION 0x00000600 +#define USBDI_VERSION 0x00000500 #include "usb200.h" diff --git a/reactos/lib/drivers/CMakeLists.txt b/reactos/lib/drivers/CMakeLists.txt index 1d1e3812b84..c23d0636528 100644 --- a/reactos/lib/drivers/CMakeLists.txt +++ b/reactos/lib/drivers/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(chew) add_subdirectory(csq) +add_subdirectory(hidparser) add_subdirectory(ip) add_subdirectory(lwip) add_subdirectory(sound) diff --git a/reactos/lib/drivers/directory.rbuild b/reactos/lib/drivers/directory.rbuild index 2c0c011058a..2dd51908b4f 100644 --- a/reactos/lib/drivers/directory.rbuild +++ b/reactos/lib/drivers/directory.rbuild @@ -4,6 +4,9 @@ + + + diff --git a/reactos/lib/drivers/hidparser/CMakeLists.txt b/reactos/lib/drivers/hidparser/CMakeLists.txt new file mode 100644 index 00000000000..b0b15dd22f4 --- /dev/null +++ b/reactos/lib/drivers/hidparser/CMakeLists.txt @@ -0,0 +1,14 @@ + +add_definitions( + -DUNICODE -D_UNICODE + -DNDEBUG) + +list(APPEND SOURCE + context.c + hidparser.c + parser.c + api.c) + +add_library(hidparser ${SOURCE}) +add_dependencies(hidparser bugcodes) + diff --git a/reactos/lib/drivers/hidparser/api.c b/reactos/lib/drivers/hidparser/api.c new file mode 100644 index 00000000000..16bbe3210fa --- /dev/null +++ b/reactos/lib/drivers/hidparser/api.c @@ -0,0 +1,748 @@ +/* + * PROJECT: ReactOS HID Parser Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/hidparser/api.c + * PURPOSE: HID Parser + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + + +#include "parser.h" + +static ULONG KeyboardScanCodes[256] = +{ + 0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0030, 0x002e, 0x0020, 0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026, + 0x0032, 0x0031, 0x0018, 0x0019, 0x0010, 0x0013, 0x001f, 0x0014, 0x0016, 0x002f, 0x0011, 0x002d, 0x0015, 0x002c, 0x0002, 0x0003, + 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x001c, 0x0001, 0x000e, 0x000f, 0x0039, 0x000c, 0x000d, 0x001a, + 0x001b, 0x002b, 0x002b, 0x0027, 0x0028, 0x0029, 0x0033, 0x0034, 0x0035, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, + 0x0041, 0x0042, 0x0043, 0x0044, 0x0057, 0x0058, 0x0063, 0x0046, 0x0077, 0xE052, 0xE047, 0xE049, 0xE053, 0xE04F, 0xE051, 0xE04D, + 0xE04B, 0xE050, 0xE048, 0x0045, 0xE035, 0x0037, 0x004a, 0x004e, 0xE01C, 0x004f, 0x0050, 0x0051, 0x004b, 0x004c, 0x004d, 0x0047, + 0x0048, 0x0049, 0x0052, 0x0053, 0x0056, 0xE05D, 0xE05E, 0x0075, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, + 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x0086, 0x008a, 0x0082, 0x0084, 0x0080, 0x0081, 0x0083, 0x0089, 0x0085, 0x0087, 0x0088, 0x0071, + 0x0073, 0x0072, 0x0000, 0x0000, 0x0000, 0x0079, 0x0000, 0x0059, 0x005d, 0x007c, 0x005c, 0x005e, 0x005f, 0x0000, 0x0000, 0x0000, + 0x007a, 0x007b, 0x005a, 0x005b, 0x0055, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x001d, 0x002a, 0x0038, 0x007d, 0x0061, 0x0036, 0x0064, 0x007e, 0x00a4, 0x00a6, 0x00a5, 0x00a3, 0x00a1, 0x0073, 0x0072, 0x0071, + 0x0096, 0x009e, 0x009f, 0x0080, 0x0088, 0x00b1, 0x00b2, 0x00b0, 0x008e, 0x0098, 0x00ad, 0x008c, 0x0000, 0x0000, 0x0000, 0x0000, +}; + + +HIDPARSER_STATUS +HidParser_GetCollectionUsagePage( + IN PVOID CollectionContext, + OUT PUSHORT Usage, + OUT PUSHORT UsagePage) +{ + PHID_COLLECTION Collection; + + // + // find collection + // + Collection = HidParser_GetCollectionFromContext(CollectionContext); + if (!Collection) + { + // + // collection not found + // + return HIDPARSER_STATUS_COLLECTION_NOT_FOUND; + } + + // + // store result + // + *UsagePage = (Collection->Usage >> 16); + *Usage = (Collection->Usage & 0xFFFF); + return HIDPARSER_STATUS_SUCCESS; +} + +ULONG +HidParser_GetReportLength( + IN PVOID CollectionContext, + IN UCHAR ReportType) +{ + PHID_REPORT Report; + ULONG ReportLength; + + // + // get first report + // + Report = HidParser_GetReportInCollection(CollectionContext, ReportType); + if (!Report) + { + // + // no report found + // + return 0; + } + + // + // get report length + // + ReportLength = Report->ReportSize; + + // + // done + // + if (ReportLength) + { + // + // byte aligned length + // + ASSERT(ReportLength % 8 == 0); + return ReportLength / 8; + } + return ReportLength; +} + +ULONG +HidParser_GetReportItemCountFromReportType( + IN PVOID CollectionContext, + IN UCHAR ReportType) +{ + PHID_REPORT Report; + + // + // get report + // + Report = HidParser_GetReportInCollection(CollectionContext, ReportType); + if (!Report) + { + // + // no such report + // + return 0; + } + + // + // return report item count + // + return Report->ItemCount; +} + + +ULONG +HidParser_GetReportItemTypeCountFromReportType( + IN PVOID CollectionContext, + IN UCHAR ReportType, + IN ULONG bData) +{ + ULONG Index; + PHID_REPORT Report; + ULONG ItemCount = 0; + + // + // get report + // + Report = HidParser_GetReportInCollection(CollectionContext, ReportType); + if (!Report) + { + // + // no such report + // + return 0; + } + + // + // enumerate all items + // + for(Index = 0; Index < Report->ItemCount; Index++) + { + // + // check item type + // + if (Report->Items[Index].HasData && bData == TRUE) + { + // + // found data item + // + ItemCount++; + } + else if (Report->Items[Index].HasData == FALSE && bData == FALSE) + { + // + // found value item + // + ItemCount++; + } + } + + // + // no report items + // + return ItemCount; +} + + +VOID +HidParser_InitParser( + IN PHIDPARSER_ALLOC_FUNCTION AllocFunction, + IN PHIDPARSER_FREE_FUNCTION FreeFunction, + IN PHIDPARSER_ZERO_FUNCTION ZeroFunction, + IN PHIDPARSER_COPY_FUNCTION CopyFunction, + IN PHIDPARSER_DEBUG_FUNCTION DebugFunction, + OUT PHID_PARSER Parser) +{ + Parser->Alloc = AllocFunction; + Parser->Free = FreeFunction; + Parser->Zero = ZeroFunction; + Parser->Copy = CopyFunction; + Parser->Debug = DebugFunction; +} + +ULONG +HidParser_GetMaxUsageListLengthWithReportAndPage( + IN PVOID CollectionContext, + IN UCHAR ReportType, + IN USAGE UsagePage OPTIONAL) +{ + ULONG Index; + PHID_REPORT Report; + ULONG ItemCount = 0; + USHORT CurrentUsagePage; + + // + // get report + // + Report = HidParser_GetReportInCollection(CollectionContext, ReportType); + if (!Report) + { + // + // no such report + // + return 0; + } + + for(Index = 0; Index < Report->ItemCount; Index++) + { + // + // check usage page + // + CurrentUsagePage = (Report->Items[Index].UsageMinimum >> 16); + if (CurrentUsagePage == UsagePage && Report->Items[Index].HasData) + { + // + // found item + // + ItemCount++; + } + } + + // + // done + // + return ItemCount; +} + +HIDPARSER_STATUS +HidParser_GetSpecificValueCapsWithReport( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN UCHAR ReportType, + IN USHORT UsagePage, + IN USHORT Usage, + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PULONG ValueCapsLength) +{ + ULONG Index; + PHID_REPORT Report; + ULONG ItemCount = 0; + USHORT CurrentUsagePage; + USHORT CurrentUsage; + + // + // get report + // + Report = HidParser_GetReportInCollection(CollectionContext, ReportType); + if (!Report) + { + // + // no such report + // + return HIDPARSER_STATUS_REPORT_NOT_FOUND; + } + + for(Index = 0; Index < Report->ItemCount; Index++) + { + // + // check usage page + // + CurrentUsagePage = (Report->Items[Index].UsageMinimum >> 16); + CurrentUsage = (Report->Items[Index].UsageMinimum & 0xFFFF); + + if ((Usage == CurrentUsage && UsagePage == CurrentUsagePage) || (Usage == 0 && UsagePage == CurrentUsagePage) || (Usage == CurrentUsage && UsagePage == 0) || (Usage == 0 && UsagePage == 0)) + { + // + // check if there is enough place for the caps + // + if (ItemCount < *ValueCapsLength) + { + // + // zero caps + // + Parser->Zero(&ValueCaps[ItemCount], sizeof(HIDP_VALUE_CAPS)); + + // + // init caps + // + ValueCaps[ItemCount].UsagePage = CurrentUsagePage; + ValueCaps[ItemCount].ReportID = Report->ReportID; + ValueCaps[ItemCount].LogicalMin = Report->Items[Index].Minimum; + ValueCaps[ItemCount].LogicalMax = Report->Items[Index].Maximum; + ValueCaps[ItemCount].IsAbsolute = !Report->Items[Index].Relative; + ValueCaps[ItemCount].BitSize = Report->Items[Index].BitCount; + + // + // FIXME: FILLMEIN + // + } + + + // + // found item + // + ItemCount++; + } + } + + // + // store result + // + *ValueCapsLength = ItemCount; + + if (ItemCount) + { + // + // success + // + return HIDPARSER_STATUS_SUCCESS; + } + + // + // item not found + // + return HIDPARSER_STATUS_USAGE_NOT_FOUND; +} + +HIDPARSER_STATUS +HidParser_GetUsagesWithReport( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN UCHAR ReportType, + IN USAGE UsagePage, + OUT USAGE *UsageList, + IN OUT PULONG UsageLength, + IN PCHAR ReportDescriptor, + IN ULONG ReportDescriptorLength) +{ + ULONG Index; + PHID_REPORT Report; + ULONG ItemCount = 0; + USHORT CurrentUsagePage; + PHID_REPORT_ITEM ReportItem; + UCHAR Activated; + ULONG Data; + PUSAGE_AND_PAGE UsageAndPage = NULL; + + // + // get report + // + Report = HidParser_GetReportInCollection(CollectionContext, ReportType); + if (!Report) + { + // + // no such report + // + return HIDPARSER_STATUS_REPORT_NOT_FOUND; + } + + if (Report->ReportSize / 8 != (ReportDescriptorLength - 1)) + { + // + // invalid report descriptor length + // + return HIDPARSER_STATUS_INVALID_REPORT_LENGTH; + } + + // + // cast to usage and page + // + if (UsagePage == HID_USAGE_PAGE_UNDEFINED) + { + // + // the caller requested any set usages + // + UsageAndPage = (PUSAGE_AND_PAGE)UsageList; + } + + for(Index = 0; Index < Report->ItemCount; Index++) + { + // + // get report item + // + ReportItem = &Report->Items[Index]; + + // + // does it have data + // + if (!ReportItem->HasData) + continue; + + // + // check usage page + // + CurrentUsagePage = (ReportItem->UsageMinimum >> 16); + + if (UsagePage != HID_USAGE_PAGE_UNDEFINED) + { + // + // does usage match + // + if (UsagePage != CurrentUsagePage) + continue; + } + + // + // check if the specified usage is activated + // + ASSERT(ReportItem->ByteOffset < ReportDescriptorLength); + ASSERT(ReportItem->BitCount <= 8); + + // + // one extra shift for skipping the prepended report id + // + Data = ReportDescriptor[ReportItem->ByteOffset + 1]; + + // + // shift data + // + Data >>= ReportItem->Shift; + + // + // clear unwanted bits + // + Data &= ReportItem->Mask; + + // + // is it activated + // + Activated = (Data != 0); + + if (!Activated) + continue; + + // + // is there enough space for the usage + // + if (ItemCount >= *UsageLength) + { + ItemCount++; + continue; + } + + if (UsagePage != HID_USAGE_PAGE_UNDEFINED) + { + // + // store item + // + UsageList[ItemCount] = (ReportItem->UsageMinimum & 0xFFFF); + } + else + { + // + // store usage and page + // + if (ReportItem->BitCount == 1) + { + // + // use usage minimum + // + UsageAndPage[ItemCount].Usage =(ReportItem->UsageMinimum & 0xFFFF); + } + else + { + // + // use value from control + // + UsageAndPage[ItemCount].Usage = (USHORT)Data; + } + UsageAndPage[ItemCount].UsagePage = CurrentUsagePage; + } + ItemCount++; + } + + if (ItemCount > *UsageLength) + { + // + // list too small + // + return HIDPARSER_STATUS_BUFFER_TOO_SMALL; + } + + if (UsagePage == HID_USAGE_PAGE_UNDEFINED) + { + // + // success, clear rest of array + // + Parser->Zero(&UsageAndPage[ItemCount], (*UsageLength - ItemCount) * sizeof(USAGE_AND_PAGE)); + } + else + { + // + // success, clear rest of array + // + Parser->Zero(&UsageList[ItemCount], (*UsageLength - ItemCount) * sizeof(USAGE)); + } + + + // + // store result size + // + *UsageLength = ItemCount; + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} + +ULONG +HidParser_UsesReportId( + IN PVOID CollectionContext, + IN UCHAR ReportType) +{ + PHID_REPORT Report; + + // + // get report + // + Report = HidParser_GetReportInCollection(CollectionContext, ReportType); + if (!Report) + { + // + // no such report + // + return 0; + } + + // + // returns true when report id != 0 + // + return (Report->ReportID != 0); + +} + + +HIDPARSER_STATUS +HidParser_GetScaledUsageValueWithReport( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN UCHAR ReportType, + IN USAGE UsagePage, + IN USAGE Usage, + OUT PLONG UsageValue, + IN PCHAR ReportDescriptor, + IN ULONG ReportDescriptorLength) +{ + ULONG Index; + PHID_REPORT Report; + USHORT CurrentUsagePage; + PHID_REPORT_ITEM ReportItem; + ULONG Data; + + // + // get report + // + Report = HidParser_GetReportInCollection(CollectionContext, ReportType); + if (!Report) + { + // + // no such report + // + return HIDPARSER_STATUS_REPORT_NOT_FOUND; + } + + if (Report->ReportSize / 8 != (ReportDescriptorLength - 1)) + { + // + // invalid report descriptor length + // + return HIDPARSER_STATUS_INVALID_REPORT_LENGTH; + } + + for(Index = 0; Index < Report->ItemCount; Index++) + { + // + // get report item + // + ReportItem = &Report->Items[Index]; + + // + // check usage page + // + CurrentUsagePage = (ReportItem->UsageMinimum >> 16); + + // + // does usage page match + // + if (UsagePage != CurrentUsagePage) + continue; + + // + // does the usage match + // + if (Usage != (ReportItem->UsageMinimum & 0xFFFF)) + continue; + + // + // check if the specified usage is activated + // + ASSERT(ReportItem->ByteOffset < ReportDescriptorLength); + + // + // one extra shift for skipping the prepended report id + // + Data = 0; + Parser->Copy(&Data, &ReportDescriptor[ReportItem->ByteOffset +1], min(sizeof(ULONG), ReportDescriptorLength - (ReportItem->ByteOffset + 1))); + Data = ReportDescriptor[ReportItem->ByteOffset + 1]; + + // + // shift data + // + Data >>= ReportItem->Shift; + + // + // clear unwanted bits + // + Data &= ReportItem->Mask; + + if (ReportItem->Minimum > ReportItem->Maximum) + { + // + // logical boundaries are signed values + // + if ((Data & ~(ReportItem->Mask >> 1)) != 0) + { + Data |= ~ReportItem->Mask; + } + } + + // + // store result + // + *UsageValue = Data; + return HIDPARSER_STATUS_SUCCESS; + } + + // + // usage not found + // + return HIDPARSER_STATUS_USAGE_NOT_FOUND; +} + +ULONG +HidParser_GetScanCode( + IN USAGE Usage) +{ + if (Usage < sizeof(KeyboardScanCodes) / sizeof(KeyboardScanCodes[0])) + { + // + // valid usage + // + return KeyboardScanCodes[Usage]; + } + + // + // invalid usage + // + return 0; +} + +VOID +HidParser_DispatchKey( + IN PCHAR ScanCodes, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext) +{ + ULONG Index; + ULONG Length = 0; + + // + // count code length + // + for(Index = 0; Index < sizeof(ULONG); Index++) + { + if (ScanCodes[Index] == 0) + { + // + // last scan code + // + break; + } + + // + // is this a key break + // + if (KeyAction == HidP_Keyboard_Break) + { + // + // add break - see USB HID to PS/2 Scan Code Translation Table + // + ScanCodes[Index] |= 0x80; + } + + // + // more scan counts + // + Length++; + } + + if (Length > 0) + { + // + // dispatch scan codes + // + InsertCodesProcedure(InsertCodesContext, ScanCodes, Length); + } +} + + +HIDPARSER_STATUS +HidParser_TranslateUsage( + IN PHID_PARSER Parser, + IN USAGE Usage, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext) +{ + ULONG ScanCode; + + // + // get scan code + // + ScanCode = HidParser_GetScanCode(Usage); + if (!ScanCode) + { + // + // invalid lookup or no scan code available + // + DPRINT1("No Scan code for Usage %x\n", Usage); + return HIDPARSER_STATUS_I8042_TRANS_UNKNOWN; + } + + // + // FIXME: translate modifier states + // + HidParser_DispatchKey((PCHAR)&ScanCode, KeyAction, InsertCodesProcedure, InsertCodesContext); + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} diff --git a/reactos/lib/drivers/hidparser/context.c b/reactos/lib/drivers/hidparser/context.c new file mode 100644 index 00000000000..3bf3662883c --- /dev/null +++ b/reactos/lib/drivers/hidparser/context.c @@ -0,0 +1,344 @@ +/* + * PROJECT: ReactOS HID Parser Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/hidparser/context.c + * PURPOSE: HID Parser + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + + +#include "parser.h" + +typedef struct +{ + ULONG Size; + union + { + UCHAR RawData[1]; + }; +}HID_COLLECTION_CONTEXT, *PHID_COLLECTION_CONTEXT; + +ULONG +HidParser_CalculateCollectionSize( + IN PHID_COLLECTION Collection) +{ + ULONG Size = 0, Index; + + Size = sizeof(HID_COLLECTION); + + // + // add size required for the number of report items + // + for(Index = 0; Index < Collection->ReportCount; Index++) + { + // + // get report size + // + ASSERT(Collection->Reports[Index]->ItemCount); + Size += sizeof(HID_REPORT) + Collection->Reports[Index]->ItemCount * sizeof(HID_REPORT_ITEM); + } + + // + // calculate size for sub collections + // + for(Index = 0; Index < Collection->NodeCount; Index++) + { + Size += HidParser_CalculateCollectionSize(Collection->Nodes[Index]); + } + + // + // append size for the offset + // + Size += (Collection->ReportCount + Collection->NodeCount) * sizeof(ULONG); + + // + // done + // + return Size; +} + +ULONG +HidParser_CalculateContextSize( + IN PHID_COLLECTION Collection) +{ + ULONG Size; + + // + // minimum size is the size of the collection + // + Size = HidParser_CalculateCollectionSize(Collection); + + // + // append collection context size + // + Size += sizeof(HID_COLLECTION_CONTEXT); + return Size; +} + +ULONG +HidParser_StoreCollection( + IN PHID_PARSER Parser, + IN PHID_COLLECTION Collection, + IN PHID_COLLECTION_CONTEXT CollectionContext, + IN ULONG CurrentOffset) +{ + ULONG Index; + ULONG ReportSize; + ULONG InitialOffset; + ULONG CollectionSize; + PHID_COLLECTION TargetCollection; + + // + // backup initial offset + // + InitialOffset = CurrentOffset; + + // + // get target collection + // + TargetCollection = (PHID_COLLECTION)(&CollectionContext->RawData[CurrentOffset]); + + // + // first copy the collection details + // + Parser->Copy(TargetCollection, Collection, sizeof(HID_COLLECTION)); + + // + // calulcate collection size + // + CollectionSize = sizeof(HID_COLLECTION) + sizeof(ULONG) * (Collection->ReportCount + Collection->NodeCount); + + // + // increase offset + // + CurrentOffset += CollectionSize; + + // + // sanity check + // + ASSERT(CurrentOffset < CollectionContext->Size); + + // + // first store the report items + // + for(Index = 0; Index < Collection->ReportCount; Index++) + { + // + // calculate report size + // + ReportSize = sizeof(HID_REPORT) + Collection->Reports[Index]->ItemCount * sizeof(HID_REPORT_ITEM); + + // + // sanity check + // + ASSERT(CurrentOffset + ReportSize < CollectionContext->Size); + + // + // copy report item + // + Parser->Copy(&CollectionContext->RawData[CurrentOffset], Collection->Reports[Index], ReportSize); + + // + // store offset to report item + // + TargetCollection->Offsets[Index] = CurrentOffset; + + // + // move to next offset + // + CurrentOffset += ReportSize; + } + + ASSERT(CurrentOffset <= CollectionContext->Size); + + // + // now store the sub collections + // + for(Index = 0; Index < Collection->NodeCount; Index++) + { + // + // store offset + // + TargetCollection->Offsets[Collection->NodeCount + Index] = CurrentOffset; + + // + // store sub collections + // + CurrentOffset += HidParser_StoreCollection(Parser, Collection->Nodes[Index], CollectionContext, CurrentOffset); + + // + // sanity check + // + ASSERT(CurrentOffset < CollectionContext->Size); + } + + // + // return size of collection + // + return CurrentOffset - InitialOffset; +} + +HIDPARSER_STATUS +HidParser_BuildCollectionContext( + IN PHID_PARSER Parser, + IN PHID_COLLECTION RootCollection, + IN PVOID Context, + IN ULONG ContextSize) +{ + PHID_COLLECTION_CONTEXT CollectionContext; + ULONG CollectionSize; + + // + // init context + // + CollectionContext = (PHID_COLLECTION_CONTEXT)Context; + CollectionContext->Size = ContextSize; + + // + // store collections + // + CollectionSize = HidParser_StoreCollection(Parser, RootCollection, CollectionContext, 0); + + // + // sanity check + // + ASSERT(CollectionSize + sizeof(HID_COLLECTION_CONTEXT) == ContextSize); + + DPRINT1("CollectionContext %p\n", CollectionContext); + DPRINT1("CollectionContext RawData %p\n", CollectionContext->RawData); + DPRINT1("CollectionContext Size %lu\n", CollectionContext->Size); + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} + +PHID_REPORT +HidParser_SearchReportInCollection( + IN PHID_COLLECTION_CONTEXT CollectionContext, + IN PHID_COLLECTION Collection, + IN UCHAR ReportType) +{ + ULONG Index; + PHID_REPORT Report; + PHID_COLLECTION SubCollection; + + // + // search first in local array + // + for(Index = 0; Index < Collection->ReportCount; Index++) + { + // + // get report + // + Report = (PHID_REPORT)(CollectionContext->RawData + Collection->Offsets[Index]); + if (Report->Type == ReportType) + { + // + // found report + // + return Report; + } + } + + // + // now search in sub collections + // + for(Index = 0; Index < Collection->NodeCount; Index++) + { + // + // get collection + // + SubCollection = (PHID_COLLECTION)(CollectionContext->RawData + Collection->Offsets[Collection->NodeCount + Index]); + + // + // recursively search collection + // + Report = HidParser_SearchReportInCollection(CollectionContext, SubCollection, ReportType); + if (Report) + { + // + // found report + // + return Report; + } + } + + // + // not found + // + return NULL; +} + +PHID_REPORT +HidParser_GetReportInCollection( + IN PVOID Context, + IN UCHAR ReportType) +{ + PHID_COLLECTION_CONTEXT CollectionContext = (PHID_COLLECTION_CONTEXT)Context; + + // + // done + // + return HidParser_SearchReportInCollection(CollectionContext, (PHID_COLLECTION)&CollectionContext->RawData, ReportType); +} + +PHID_COLLECTION +HidParser_GetCollectionFromContext( + IN PVOID Context) +{ + PHID_COLLECTION_CONTEXT CollectionContext = (PHID_COLLECTION_CONTEXT)Context; + + // + // return root collection + // + return (PHID_COLLECTION)CollectionContext->RawData; +} + +ULONG +HidParser_GetCollectionCount( + IN PHID_COLLECTION_CONTEXT CollectionContext, + IN PHID_COLLECTION Collection) +{ + ULONG Index; + ULONG Count = Collection->NodeCount; + PHID_COLLECTION SubCollection; + + for(Index = 0; Index < Collection->NodeCount; Index++) + { + // + // get offset to sub collection + // + SubCollection = (PHID_COLLECTION)(CollectionContext->RawData + Collection->Offsets[Collection->NodeCount + Index]); + + // + // count collection for sub nodes + // + Count += HidParser_GetCollectionCount(CollectionContext, SubCollection); + } + + // + // done + // + return Count; +} + +ULONG +HidParser_GetTotalCollectionCount( + IN PVOID Context) +{ + PHID_COLLECTION_CONTEXT CollectionContext; + + // + // get parser context + // + CollectionContext = (PHID_COLLECTION_CONTEXT)Context; + + // + // count collections + // + return HidParser_GetCollectionCount(CollectionContext, (PHID_COLLECTION)CollectionContext->RawData); +} diff --git a/reactos/lib/drivers/hidparser/hidparser.c b/reactos/lib/drivers/hidparser/hidparser.c new file mode 100644 index 00000000000..1b15fd74058 --- /dev/null +++ b/reactos/lib/drivers/hidparser/hidparser.c @@ -0,0 +1,1180 @@ +/* + * PROJECT: ReactOS HID Parser Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/hidparser/hidparser.c + * PURPOSE: HID Parser + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#include "parser.h" + +NTSTATUS +TranslateHidParserStatus( + IN HIDPARSER_STATUS Status) +{ + switch(Status) + { + case HIDPARSER_STATUS_INSUFFICIENT_RESOURCES: + return HIDP_STATUS_INTERNAL_ERROR; + case HIDPARSER_STATUS_NOT_IMPLEMENTED: + return HIDP_STATUS_NOT_IMPLEMENTED; + case HIDPARSER_STATUS_REPORT_NOT_FOUND: + return HIDP_STATUS_REPORT_DOES_NOT_EXIST; + case HIDPARSER_STATUS_INVALID_REPORT_LENGTH: + return HIDP_STATUS_INVALID_REPORT_LENGTH; + case HIDPARSER_STATUS_INVALID_REPORT_TYPE: + return HIDP_STATUS_INVALID_REPORT_TYPE; + case HIDPARSER_STATUS_BUFFER_TOO_SMALL: + return HIDP_STATUS_BUFFER_TOO_SMALL; + case HIDPARSER_STATUS_USAGE_NOT_FOUND: + return HIDP_STATUS_USAGE_NOT_FOUND; + case HIDPARSER_STATUS_I8042_TRANS_UNKNOWN: + return HIDP_STATUS_I8042_TRANS_UNKNOWN; + case HIDPARSER_STATUS_COLLECTION_NOT_FOUND: + return HIDP_STATUS_NOT_IMPLEMENTED; //FIXME + } + DPRINT1("TranslateHidParserStatus Status %ld not implemented\n", Status); + return HIDP_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +HidParser_GetCollectionDescription( + IN PHID_PARSER Parser, + IN PHIDP_REPORT_DESCRIPTOR ReportDesc, + IN ULONG DescLength, + IN POOL_TYPE PoolType, + OUT PHIDP_DEVICE_DESC DeviceDescription) +{ + HIDPARSER_STATUS ParserStatus; + ULONG CollectionCount; + ULONG Index; + PVOID ParserContext; + + // + // first parse the report descriptor + // + ParserStatus = HidParser_ParseReportDescriptor(Parser, ReportDesc, DescLength, &ParserContext); + if (ParserStatus != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to parse report descriptor + // + Parser->Debug("[HIDPARSER] Failed to parse report descriptor with %x\n", ParserStatus); + return TranslateHidParserStatus(ParserStatus); + } + + // + // get collection count + // + CollectionCount = HidParser_NumberOfTopCollections(ParserContext); + if (CollectionCount == 0) + { + // + // no top level collections found + // + ASSERT(FALSE); + return STATUS_NO_DATA_DETECTED; + } + + // + // zero description + // + Parser->Zero(DeviceDescription, sizeof(HIDP_DEVICE_DESC)); + + // + // allocate collection + // + DeviceDescription->CollectionDesc = (PHIDP_COLLECTION_DESC)Parser->Alloc(sizeof(HIDP_COLLECTION_DESC) * CollectionCount); + if (!DeviceDescription->CollectionDesc) + { + // + // no memory + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // allocate report description + // + DeviceDescription->ReportIDs = (PHIDP_REPORT_IDS)Parser->Alloc(sizeof(HIDP_REPORT_IDS) * CollectionCount); + if (!DeviceDescription->ReportIDs) + { + // + // no memory + // + Parser->Free(DeviceDescription->CollectionDesc); + return STATUS_INSUFFICIENT_RESOURCES; + } + + for(Index = 0; Index < CollectionCount; Index++) + { + // + // set preparsed data length + // + DeviceDescription->CollectionDesc[Index].PreparsedDataLength = HidParser_GetContextSize(Parser, ParserContext, Index); + ParserStatus = HidParser_BuildContext(Parser, ParserContext, Index, DeviceDescription->CollectionDesc[Index].PreparsedDataLength, (PVOID*)&DeviceDescription->CollectionDesc[Index].PreparsedData); + if (ParserStatus != HIDPARSER_STATUS_SUCCESS) + { + // + // no memory + // + return TranslateHidParserStatus(ParserStatus); + } + + // + // init report description + // + DeviceDescription->ReportIDs[Index].CollectionNumber = Index + 1; + DeviceDescription->ReportIDs[Index].ReportID = Index; //FIXME + DeviceDescription->ReportIDs[Index].InputLength = HidParser_GetReportLength((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_INPUT); + DeviceDescription->ReportIDs[Index].OutputLength = HidParser_GetReportLength((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_OUTPUT); + DeviceDescription->ReportIDs[Index].FeatureLength = HidParser_GetReportLength((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_FEATURE); + + + DeviceDescription->ReportIDs[Index].InputLength += (HidParser_UsesReportId((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_INPUT) == TRUE ? 1 : 0); + DeviceDescription->ReportIDs[Index].OutputLength += (HidParser_UsesReportId((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_OUTPUT) == TRUE ? 1 : 0); + DeviceDescription->ReportIDs[Index].FeatureLength += (HidParser_UsesReportId((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_FEATURE) == TRUE ? 1 : 0); + + + // + // init collection description + // + DeviceDescription->CollectionDesc[Index].CollectionNumber = Index + 1; + + // + // get collection usage page + // + ParserStatus = HidParser_GetCollectionUsagePage((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, &DeviceDescription->CollectionDesc[Index].Usage, &DeviceDescription->CollectionDesc[Index].UsagePage); + + // + // windows seems to prepend the report id, regardless if it is required + // + DeviceDescription->CollectionDesc[Index].CollectionNumber = Index + 1; + DeviceDescription->CollectionDesc[Index].InputLength = DeviceDescription->ReportIDs[Index].InputLength; + DeviceDescription->CollectionDesc[Index].OutputLength = DeviceDescription->ReportIDs[Index].OutputLength; + DeviceDescription->CollectionDesc[Index].FeatureLength = DeviceDescription->ReportIDs[Index].FeatureLength; + + DeviceDescription->CollectionDesc[Index].InputLength += (HidParser_UsesReportId((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_INPUT) == FALSE ? 1 : 0); + DeviceDescription->CollectionDesc[Index].OutputLength += (HidParser_UsesReportId((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_OUTPUT) == FALSE ? 1 : 0); + DeviceDescription->CollectionDesc[Index].FeatureLength += (HidParser_UsesReportId((PVOID)DeviceDescription->CollectionDesc[Index].PreparsedData, HID_REPORT_TYPE_FEATURE) == FALSE ? 1 : 0); + + + } + + // + // store collection & report count + // + DeviceDescription->CollectionDescLength = CollectionCount; + DeviceDescription->ReportIDsLength = CollectionCount; + + // + // done + // + return STATUS_SUCCESS; +} + +VOID +NTAPI +HidParser_FreeCollectionDescription( + IN PHID_PARSER Parser, + IN PHIDP_DEVICE_DESC DeviceDescription) +{ + ULONG Index; + + // + // first free all context + // + for(Index = 0; Index < DeviceDescription->CollectionDescLength; Index++) + { + // + // free collection context + // + Parser->Free(DeviceDescription->CollectionDesc[Index].PreparsedData); + } + + // + // now free collection description + // + Parser->Free(DeviceDescription->CollectionDesc); + + // + // free report description + // + ExFreePool(DeviceDescription->ReportIDs); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetCaps( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + OUT PHIDP_CAPS Capabilities) +{ + // + // zero capabilities + // + Parser->Zero(Capabilities, sizeof(HIDP_CAPS)); + + // + // init capabilities + // + HidParser_GetCollectionUsagePage(CollectionContext, &Capabilities->Usage, &Capabilities->UsagePage); + Capabilities->InputReportByteLength = HidParser_GetReportLength(CollectionContext, HID_REPORT_TYPE_INPUT); + Capabilities->OutputReportByteLength = HidParser_GetReportLength(CollectionContext, HID_REPORT_TYPE_OUTPUT); + Capabilities->FeatureReportByteLength = HidParser_GetReportLength(CollectionContext, HID_REPORT_TYPE_FEATURE); + + // + // always pre-prend report id + // + Capabilities->InputReportByteLength = (Capabilities->InputReportByteLength > 0 ? Capabilities->InputReportByteLength + 1 : 0); + Capabilities->OutputReportByteLength = (Capabilities->OutputReportByteLength > 0 ? Capabilities->OutputReportByteLength + 1 : 0); + Capabilities->FeatureReportByteLength = (Capabilities->FeatureReportByteLength > 0 ? Capabilities->FeatureReportByteLength + 1 : 0); + + // + // get number of link collection nodes + // + Capabilities->NumberLinkCollectionNodes = HidParser_GetTotalCollectionCount(CollectionContext); + + // + // get data indices + // + Capabilities->NumberInputDataIndices = HidParser_GetReportItemTypeCountFromReportType(CollectionContext, HID_REPORT_TYPE_INPUT, TRUE); + Capabilities->NumberOutputDataIndices = HidParser_GetReportItemTypeCountFromReportType(CollectionContext, HID_REPORT_TYPE_OUTPUT, TRUE); + Capabilities->NumberFeatureDataIndices = HidParser_GetReportItemTypeCountFromReportType(CollectionContext, HID_REPORT_TYPE_FEATURE, TRUE); + + // + // get value caps + // + Capabilities->NumberInputValueCaps = HidParser_GetReportItemTypeCountFromReportType(CollectionContext, HID_REPORT_TYPE_INPUT, FALSE); + Capabilities->NumberOutputValueCaps = HidParser_GetReportItemTypeCountFromReportType(CollectionContext, HID_REPORT_TYPE_OUTPUT, FALSE); + Capabilities->NumberFeatureValueCaps = HidParser_GetReportItemTypeCountFromReportType(CollectionContext, HID_REPORT_TYPE_FEATURE, FALSE); + + + // + // get button caps + // + Capabilities->NumberInputButtonCaps = HidParser_GetReportItemCountFromReportType(CollectionContext, HID_REPORT_TYPE_INPUT); + Capabilities->NumberOutputButtonCaps = HidParser_GetReportItemCountFromReportType(CollectionContext, HID_REPORT_TYPE_OUTPUT); + Capabilities->NumberFeatureButtonCaps = HidParser_GetReportItemCountFromReportType(CollectionContext, HID_REPORT_TYPE_FEATURE); + + // + // done + // + return HIDP_STATUS_SUCCESS; +} + +HIDAPI +ULONG +NTAPI +HidParser_MaxUsageListLength( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage OPTIONAL) +{ + // + // FIXME test what should be returned when usage page is not defined + // + if (UsagePage == HID_USAGE_PAGE_UNDEFINED) + { + // + // implement me + // + UNIMPLEMENTED + + // + // invalid report + // + return 0; + } + + if (ReportType == HidP_Input) + { + // + // input report + // + return HidParser_GetMaxUsageListLengthWithReportAndPage(CollectionContext, HID_REPORT_TYPE_INPUT, UsagePage); + } + else if (ReportType == HidP_Output) + { + // + // input report + // + return HidParser_GetMaxUsageListLengthWithReportAndPage(CollectionContext, HID_REPORT_TYPE_OUTPUT, UsagePage); + } + else if (ReportType == HidP_Feature) + { + // + // input report + // + return HidParser_GetMaxUsageListLengthWithReportAndPage(CollectionContext, HID_REPORT_TYPE_FEATURE, UsagePage); + } + else + { + // + // invalid report type + // + return 0; + } +} + +#undef HidParser_GetButtonCaps + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetButtonCaps( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_BUTTON_CAPS ButtonCaps, + IN PUSHORT ButtonCapsLength) +{ + return HidParser_GetSpecificButtonCaps(Parser, CollectionContext, ReportType, HID_USAGE_PAGE_UNDEFINED, HIDP_LINK_COLLECTION_UNSPECIFIED, HID_USAGE_PAGE_UNDEFINED, ButtonCaps, (PULONG)ButtonCapsLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetSpecificValueCaps( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PULONG ValueCapsLength) +{ + HIDPARSER_STATUS ParserStatus; + + // + // FIXME: implement searching in specific collection + // + ASSERT(LinkCollection == HIDP_LINK_COLLECTION_UNSPECIFIED); + + if (ReportType == HidP_Input) + { + // + // input report + // + ParserStatus = HidParser_GetSpecificValueCapsWithReport(Parser, CollectionContext, HID_REPORT_TYPE_INPUT, UsagePage, Usage, ValueCaps, ValueCapsLength); + } + else if (ReportType == HidP_Output) + { + // + // input report + // + ParserStatus = HidParser_GetSpecificValueCapsWithReport(Parser, CollectionContext, HID_REPORT_TYPE_OUTPUT, UsagePage, Usage, ValueCaps, ValueCapsLength); + } + else if (ReportType == HidP_Feature) + { + // + // input report + // + ParserStatus = HidParser_GetSpecificValueCapsWithReport(Parser, CollectionContext, HID_REPORT_TYPE_FEATURE, UsagePage, Usage, ValueCaps, ValueCapsLength); + } + else + { + // + // invalid report type + // + return HIDP_STATUS_INVALID_REPORT_TYPE; + } + + + if (ParserStatus == HIDPARSER_STATUS_SUCCESS) + { + // + // success + // + return HIDP_STATUS_SUCCESS; + } + + // + // translate error + // + return TranslateHidParserStatus(ParserStatus); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_UsageListDifference( + IN PUSAGE PreviousUsageList, + IN PUSAGE CurrentUsageList, + OUT PUSAGE BreakUsageList, + OUT PUSAGE MakeUsageList, + IN ULONG UsageListLength) +{ + ULONG Index, SubIndex, bFound, BreakUsageIndex = 0, MakeUsageIndex = 0; + USAGE CurrentUsage, Usage; + + if (UsageListLength) + { + Index = 0; + do + { + /* get current usage */ + CurrentUsage = PreviousUsageList[Index]; + + /* is the end of list reached? */ + if (!CurrentUsage) + break; + + /* start searching in current usage list */ + SubIndex = 0; + bFound = FALSE; + do + { + /* get usage of current list */ + Usage = CurrentUsageList[SubIndex]; + + /* end of list reached? */ + if (!Usage) + break; + + /* check if it matches the current one */ + if (CurrentUsage == Usage) + { + /* it does */ + bFound = TRUE; + break; + } + + /* move to next usage */ + SubIndex++; + }while(SubIndex < UsageListLength); + + /* was the usage found ?*/ + if (!bFound) + { + /* store it in the break usage list */ + BreakUsageList[BreakUsageIndex] = CurrentUsage; + BreakUsageIndex++; + } + + /* move to next usage */ + Index++; + + }while(Index < UsageListLength); + + /* now process the new items */ + Index = 0; + do + { + /* get current usage */ + CurrentUsage = CurrentUsageList[Index]; + + /* is the end of list reached? */ + if (!CurrentUsage) + break; + + /* start searching in current usage list */ + SubIndex = 0; + bFound = FALSE; + do + { + /* get usage of previous list */ + Usage = PreviousUsageList[SubIndex]; + + /* end of list reached? */ + if (!Usage) + break; + + /* check if it matches the current one */ + if (CurrentUsage == Usage) + { + /* it does */ + bFound = TRUE; + break; + } + + /* move to next usage */ + SubIndex++; + }while(SubIndex < UsageListLength); + + /* was the usage found ?*/ + if (!bFound) + { + /* store it in the make usage list */ + MakeUsageList[MakeUsageIndex] = CurrentUsage; + MakeUsageIndex++; + } + + /* move to next usage */ + Index++; + + }while(Index < UsageListLength); + } + + /* does the break list contain empty entries */ + if (BreakUsageIndex < UsageListLength) + { + /* zeroize entries */ + RtlZeroMemory(&BreakUsageList[BreakUsageIndex], sizeof(USAGE) * (UsageListLength - BreakUsageIndex)); + } + + /* does the make usage list contain empty entries */ + if (MakeUsageIndex < UsageListLength) + { + /* zeroize entries */ + RtlZeroMemory(&MakeUsageList[MakeUsageIndex], sizeof(USAGE) * (UsageListLength - MakeUsageIndex)); + } + + /* done */ + return HIDP_STATUS_SUCCESS; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsages( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + OUT USAGE *UsageList, + IN OUT PULONG UsageLength, + IN PCHAR Report, + IN ULONG ReportLength) +{ + HIDPARSER_STATUS ParserStatus; + + // + // FIXME: implement searching in specific collection + // + ASSERT(LinkCollection == HIDP_LINK_COLLECTION_UNSPECIFIED); + + if (ReportType == HidP_Input) + { + // + // input report + // + ParserStatus = HidParser_GetUsagesWithReport(Parser, CollectionContext, HID_REPORT_TYPE_INPUT, UsagePage, UsageList, UsageLength, Report, ReportLength); + } + else if (ReportType == HidP_Output) + { + // + // input report + // + ParserStatus = HidParser_GetUsagesWithReport(Parser, CollectionContext, HID_REPORT_TYPE_OUTPUT, UsagePage, UsageList, UsageLength, Report, ReportLength); + } + else if (ReportType == HidP_Feature) + { + // + // input report + // + ParserStatus = HidParser_GetUsagesWithReport(Parser, CollectionContext, HID_REPORT_TYPE_FEATURE, UsagePage, UsageList, UsageLength, Report, ReportLength); + } + else + { + // + // invalid report type + // + return HIDP_STATUS_INVALID_REPORT_TYPE; + } + + if (ParserStatus == HIDPARSER_STATUS_SUCCESS) + { + // + // success + // + return HIDP_STATUS_SUCCESS; + } + + // + // translate error + // + return TranslateHidParserStatus(ParserStatus); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetScaledUsageValue( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + OUT PLONG UsageValue, + IN PCHAR Report, + IN ULONG ReportLength) +{ + HIDPARSER_STATUS ParserStatus; + + // + // FIXME: implement searching in specific collection + // + ASSERT(LinkCollection == HIDP_LINK_COLLECTION_UNSPECIFIED); + + if (ReportType == HidP_Input) + { + // + // input report + // + ParserStatus = HidParser_GetScaledUsageValueWithReport(Parser, CollectionContext, HID_REPORT_TYPE_INPUT, UsagePage, Usage, UsageValue, Report, ReportLength); + } + else if (ReportType == HidP_Output) + { + // + // input report + // + ParserStatus = HidParser_GetScaledUsageValueWithReport(Parser, CollectionContext, HID_REPORT_TYPE_OUTPUT, UsagePage, Usage, UsageValue, Report, ReportLength); + } + else if (ReportType == HidP_Feature) + { + // + // input report + // + ParserStatus = HidParser_GetScaledUsageValueWithReport(Parser, CollectionContext, HID_REPORT_TYPE_FEATURE, UsagePage, Usage, UsageValue, Report, ReportLength); + } + else + { + // + // invalid report type + // + return HIDP_STATUS_INVALID_REPORT_TYPE; + } + + if (ParserStatus == HIDPARSER_STATUS_SUCCESS) + { + // + // success + // + return HIDP_STATUS_SUCCESS; + } + + // + // translate error + // + return TranslateHidParserStatus(ParserStatus); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_TranslateUsageAndPagesToI8042ScanCodes( + IN PHID_PARSER Parser, + IN PUSAGE_AND_PAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext) +{ + ULONG Index; + HIDPARSER_STATUS Status = HIDPARSER_STATUS_SUCCESS; + + for(Index = 0; Index < UsageListLength; Index++) + { + // + // check current usage + // + if (ChangedUsageList[Index].UsagePage == HID_USAGE_PAGE_KEYBOARD) + { + // + // process usage + // + Status = HidParser_TranslateUsage(Parser, ChangedUsageList[Index].Usage, KeyAction, ModifierState, InsertCodesProcedure, InsertCodesContext); + } + else if (ChangedUsageList[Index].UsagePage == HID_USAGE_PAGE_CONSUMER) + { + // + // FIXME: implement me + // + UNIMPLEMENTED + Status = HIDPARSER_STATUS_NOT_IMPLEMENTED; + } + else + { + // + // invalid page / end of usage list page + // + return HIDP_STATUS_I8042_TRANS_UNKNOWN; + } + + // + // check status + // + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed + // + return TranslateHidParserStatus(Status); + } + } + + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed + // + return TranslateHidParserStatus(Status); + } + + // + // done + // + return HIDP_STATUS_SUCCESS; +} + + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsagesEx( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USHORT LinkCollection, + OUT PUSAGE_AND_PAGE ButtonList, + IN OUT ULONG *UsageLength, + IN PCHAR Report, + IN ULONG ReportLength) +{ + return HidParser_GetUsages(Parser, CollectionContext, ReportType, HID_USAGE_PAGE_UNDEFINED, LinkCollection, (PUSAGE)ButtonList, UsageLength, Report, ReportLength); +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_UsageAndPageListDifference( + IN PUSAGE_AND_PAGE PreviousUsageList, + IN PUSAGE_AND_PAGE CurrentUsageList, + OUT PUSAGE_AND_PAGE BreakUsageList, + OUT PUSAGE_AND_PAGE MakeUsageList, + IN ULONG UsageListLength) +{ + ULONG Index, SubIndex, BreakUsageListIndex = 0, MakeUsageListIndex = 0, bFound; + PUSAGE_AND_PAGE CurrentUsage, Usage; + + if (UsageListLength) + { + /* process removed usages */ + Index = 0; + do + { + /* get usage from current index */ + CurrentUsage = &PreviousUsageList[Index]; + + /* end of list reached? */ + if (CurrentUsage->Usage == 0 && CurrentUsage->UsagePage == 0) + break; + + /* search in current list */ + SubIndex = 0; + bFound = FALSE; + do + { + /* get usage */ + Usage = &CurrentUsageList[SubIndex]; + + /* end of list reached? */ + if (Usage->Usage == 0 && Usage->UsagePage == 0) + break; + + /* does it match */ + if (Usage->Usage == CurrentUsage->Usage && Usage->UsagePage == CurrentUsage->UsagePage) + { + /* found match */ + bFound = TRUE; + } + + /* move to next index */ + SubIndex++; + + }while(SubIndex < UsageListLength); + + if (!bFound) + { + /* store it in break usage list */ + BreakUsageList[BreakUsageListIndex].Usage = CurrentUsage->Usage; + BreakUsageList[BreakUsageListIndex].UsagePage = CurrentUsage->UsagePage; + BreakUsageListIndex++; + } + + /* move to next index */ + Index++; + + }while(Index < UsageListLength); + + /* process new usages */ + Index = 0; + do + { + /* get usage from current index */ + CurrentUsage = &CurrentUsageList[Index]; + + /* end of list reached? */ + if (CurrentUsage->Usage == 0 && CurrentUsage->UsagePage == 0) + break; + + /* search in current list */ + SubIndex = 0; + bFound = FALSE; + do + { + /* get usage */ + Usage = &PreviousUsageList[SubIndex]; + + /* end of list reached? */ + if (Usage->Usage == 0 && Usage->UsagePage == 0) + break; + + /* does it match */ + if (Usage->Usage == CurrentUsage->Usage && Usage->UsagePage == CurrentUsage->UsagePage) + { + /* found match */ + bFound = TRUE; + } + + /* move to next index */ + SubIndex++; + + }while(SubIndex < UsageListLength); + + if (!bFound) + { + /* store it in break usage list */ + MakeUsageList[MakeUsageListIndex].Usage = CurrentUsage->Usage; + MakeUsageList[MakeUsageListIndex].UsagePage = CurrentUsage->UsagePage; + MakeUsageListIndex++; + } + + /* move to next index */ + Index++; + }while(Index < UsageListLength); + } + + /* are there remaining free list entries */ + if (BreakUsageListIndex < UsageListLength) + { + /* zero them */ + RtlZeroMemory(&BreakUsageList[BreakUsageListIndex], (UsageListLength - BreakUsageListIndex) * sizeof(USAGE_AND_PAGE)); + } + + /* are there remaining free list entries */ + if (MakeUsageListIndex < UsageListLength) + { + /* zero them */ + RtlZeroMemory(&MakeUsageList[MakeUsageListIndex], (UsageListLength - MakeUsageListIndex) * sizeof(USAGE_AND_PAGE)); + } + + /* done */ + return HIDP_STATUS_SUCCESS; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetSpecificButtonCaps( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PHIDP_BUTTON_CAPS ButtonCaps, + IN OUT PULONG ButtonCapsLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetData( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + OUT PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetExtendedAttributes( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USHORT DataIndex, + OUT PHIDP_EXTENDED_ATTRIBUTES Attributes, + IN OUT PULONG LengthAttributes) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetLinkCollectionNodes( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + OUT PHIDP_LINK_COLLECTION_NODE LinkCollectionNodes, + IN OUT PULONG LinkCollectionNodesLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsageValue( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PULONG UsageValue, + IN PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +HidParser_SysPowerEvent( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN PCHAR HidPacket, + IN USHORT HidPacketLength, + OUT PULONG OutputBuffer) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS +NTAPI +HidParser_SysPowerCaps ( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + OUT PULONG OutputBuffer) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsageValueArray( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + OUT PCHAR UsageValue, + IN USHORT UsageValueByteLength, + IN PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_UnsetUsages( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_TranslateUsagesToI8042ScanCodes( + IN PUSAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsages( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsageValueArray( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + IN PCHAR UsageValue, + IN USHORT UsageValueByteLength, + OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsageValue( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + IN ULONG UsageValue, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetScaledUsageValue( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + IN LONG UsageValue, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetData( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +HIDAPI +ULONG +NTAPI +HidParser_MaxDataListLength( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return 0; +} + +HIDAPI +NTSTATUS +NTAPI +HidParser_InitializeReportForID( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN UCHAR ReportID, + IN OUT PCHAR Report, + IN ULONG ReportLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} + +#undef HidParser_GetValueCaps + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetValueCaps( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + HIDP_REPORT_TYPE ReportType, + PHIDP_VALUE_CAPS ValueCaps, + PULONG ValueCapsLength) +{ + UNIMPLEMENTED + ASSERT(FALSE); + return STATUS_NOT_IMPLEMENTED; +} diff --git a/reactos/lib/drivers/hidparser/hidparser.h b/reactos/lib/drivers/hidparser/hidparser.h new file mode 100644 index 00000000000..beb46ecc891 --- /dev/null +++ b/reactos/lib/drivers/hidparser/hidparser.h @@ -0,0 +1,449 @@ +/* + * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/hidparser/hidparser.c + * PURPOSE: HID Parser + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + +#pragma once + +#define _HIDPI_ +#define _HIDPI_NO_FUNCTION_MACROS_ +#include +#include +#include +#ifndef NDEBUG +#define NDEBUG +#endif +#include + +// +// function prototypes +// +typedef PVOID (NTAPI *PHIDPARSER_ALLOC_FUNCTION)(ULONG Size); +typedef VOID (NTAPI *PHIDPARSER_FREE_FUNCTION)(PVOID Item); +typedef VOID (NTAPI *PHIDPARSER_ZERO_FUNCTION)(PVOID Item, ULONG Size); +typedef VOID (NTAPI *PHIDPARSER_COPY_FUNCTION)(PVOID Target, PVOID Source, ULONG Size); +typedef VOID (NTAPI *PHIDPARSER_DEBUG_FUNCTION)(LPCSTR Src, ...); + +// +// status code +// +typedef long HIDPARSER_STATUS; + +// +// result codes +// +typedef enum +{ + HIDPARSER_STATUS_SUCCESS = 0, + HIDPARSER_STATUS_INSUFFICIENT_RESOURCES = -1, + HIDPARSER_STATUS_NOT_IMPLEMENTED = -2, + HIDPARSER_STATUS_REPORT_NOT_FOUND = -3, + HIDPARSER_STATUS_COLLECTION_NOT_FOUND = -4, + HIDPARSER_STATUS_INVALID_REPORT_LENGTH = -5, + HIDPARSER_STATUS_INVALID_REPORT_TYPE = -6, + HIDPARSER_STATUS_BUFFER_TOO_SMALL = -7, + HIDPARSER_STATUS_USAGE_NOT_FOUND = -8, + HIDPARSER_STATUS_I8042_TRANS_UNKNOWN = -9 +}HIDPARSER_STATUS_CODES; + +typedef struct +{ + // + // size of struct + // + unsigned long Size; + + // + // allocation function + // + PHIDPARSER_ALLOC_FUNCTION Alloc; + + // + // free function + // + PFREE_FUNCTION Free; + + // + // zero function + // + PHIDPARSER_ZERO_FUNCTION Zero; + + // + // copy function + // + PHIDPARSER_COPY_FUNCTION Copy; + + // + // debug function + // + PHIDPARSER_DEBUG_FUNCTION Debug; +}HID_PARSER, *PHID_PARSER; + +VOID +HidParser_InitParser( + IN PHIDPARSER_ALLOC_FUNCTION AllocFunction, + IN PHIDPARSER_FREE_FUNCTION FreeFunction, + IN PHIDPARSER_ZERO_FUNCTION ZeroFunction, + IN PHIDPARSER_COPY_FUNCTION CopyFunction, + IN PHIDPARSER_DEBUG_FUNCTION DebugFunction, + OUT PHID_PARSER Parser); + +NTSTATUS +NTAPI +HidParser_GetCollectionDescription( + IN PHID_PARSER Parser, + IN PHIDP_REPORT_DESCRIPTOR ReportDesc, + IN ULONG DescLength, + IN POOL_TYPE PoolType, + OUT PHIDP_DEVICE_DESC DeviceDescription); + +VOID +NTAPI +HidParser_FreeCollectionDescription( + IN PHID_PARSER Parser, + IN PHIDP_DEVICE_DESC DeviceDescription); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetCaps( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + OUT PHIDP_CAPS Capabilities); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetSpecificValueCaps( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PULONG ValueCapsLength); + + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetButtonCaps( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + HIDP_REPORT_TYPE ReportType, + PHIDP_BUTTON_CAPS ButtonCaps, + PUSHORT ButtonCapsLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetSpecificButtonCaps( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PHIDP_BUTTON_CAPS ButtonCaps, + IN OUT PULONG ButtonCapsLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetScaledUsageValue( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + OUT PLONG UsageValue, + IN PCHAR Report, + IN ULONG ReportLength); + + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetData( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + OUT PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetExtendedAttributes( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USHORT DataIndex, + OUT PHIDP_EXTENDED_ATTRIBUTES Attributes, + IN OUT PULONG LengthAttributes); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetLinkCollectionNodes( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + OUT PHIDP_LINK_COLLECTION_NODE LinkCollectionNodes, + IN OUT PULONG LinkCollectionNodesLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsageValue( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + OUT PULONG UsageValue, + IN PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_UsageListDifference( + IN PUSAGE PreviousUsageList, + IN PUSAGE CurrentUsageList, + OUT PUSAGE BreakUsageList, + OUT PUSAGE MakeUsageList, + IN ULONG UsageListLength); + + +HIDAPI +ULONG +NTAPI +HidParser_MaxUsageListLength( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage OPTIONAL); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsages( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + OUT USAGE *UsageList, + IN OUT ULONG *UsageLength, + IN PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsagesEx( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USHORT LinkCollection, + OUT PUSAGE_AND_PAGE ButtonList, + IN OUT ULONG *UsageLength, + IN PCHAR Report, + IN ULONG ReportLength); + + +NTSTATUS +NTAPI +HidParser_SysPowerEvent ( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN PCHAR HidPacket, + IN USHORT HidPacketLength, + OUT PULONG OutputBuffer); + +NTSTATUS +NTAPI +HidParser_SysPowerCaps ( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + OUT PULONG OutputBuffer); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetUsageValueArray( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + OUT PCHAR UsageValue, + IN USHORT UsageValueByteLength, + IN PCHAR Report, + IN ULONG ReportLength); + + +HIDAPI +NTSTATUS +NTAPI +HidParser_UsageAndPageListDifference( + IN PUSAGE_AND_PAGE PreviousUsageList, + IN PUSAGE_AND_PAGE CurrentUsageList, + OUT PUSAGE_AND_PAGE BreakUsageList, + OUT PUSAGE_AND_PAGE MakeUsageList, + IN ULONG UsageListLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_UnsetUsages( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_TranslateUsagesToI8042ScanCodes( + IN PUSAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext); + +HIDAPI +NTSTATUS +NTAPI +HidParser_TranslateUsageAndPagesToI8042ScanCodes( + IN PHID_PARSER Parser, + IN PUSAGE_AND_PAGE ChangedUsageList, + IN ULONG UsageListLength, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext); + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsages( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN PUSAGE UsageList, + IN OUT PULONG UsageLength, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsageValueArray( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + IN PCHAR UsageValue, + IN USHORT UsageValueByteLength, + OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetUsageValue( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection, + IN USAGE Usage, + IN ULONG UsageValue, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetScaledUsageValue( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN USAGE UsagePage, + IN USHORT LinkCollection OPTIONAL, + IN USAGE Usage, + IN LONG UsageValue, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +NTSTATUS +NTAPI +HidParser_SetData( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN PHIDP_DATA DataList, + IN OUT PULONG DataLength, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDAPI +ULONG +NTAPI +HidParser_MaxDataListLength( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType); + +HIDAPI +NTSTATUS +NTAPI +HidParser_InitializeReportForID( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN HIDP_REPORT_TYPE ReportType, + IN UCHAR ReportID, + IN OUT PCHAR Report, + IN ULONG ReportLength); + +HIDPARSER_STATUS +HidParser_TranslateUsage( + IN PHID_PARSER Parser, + IN USAGE Usage, + IN HIDP_KEYBOARD_DIRECTION KeyAction, + IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState, + IN PHIDP_INSERT_SCANCODES InsertCodesProcedure, + IN PVOID InsertCodesContext); + +HIDAPI +NTSTATUS +NTAPI +HidParser_GetValueCaps( + PHID_PARSER Parser, + IN PVOID CollectionContext, + HIDP_REPORT_TYPE ReportType, + PHIDP_VALUE_CAPS ValueCaps, + PULONG ValueCapsLength); diff --git a/reactos/lib/drivers/hidparser/hidparser.rbuild b/reactos/lib/drivers/hidparser/hidparser.rbuild new file mode 100644 index 00000000000..61416b5f723 --- /dev/null +++ b/reactos/lib/drivers/hidparser/hidparser.rbuild @@ -0,0 +1,11 @@ + + + + + + + hidparser.c + parser.c + context.c + api.c + \ No newline at end of file diff --git a/reactos/lib/drivers/hidparser/parser.c b/reactos/lib/drivers/hidparser/parser.c new file mode 100644 index 00000000000..0705788aa99 --- /dev/null +++ b/reactos/lib/drivers/hidparser/parser.c @@ -0,0 +1,1400 @@ +/* + * PROJECT: ReactOS HID Parser Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/hidparser/parser.c + * PURPOSE: HID Parser + * PROGRAMMERS: + * Michael Martin (michael.martin@reactos.org) + * Johannes Anderwald (johannes.anderwald@reactos.org) + */ + + +#include "parser.h" + +static UCHAR ItemSize[4] = { 0, 1, 2, 4 }; + +VOID +HidParser_DeleteReport( + IN PHID_PARSER Parser, + IN PHID_REPORT Report) +{ + // + // not implemented + // +} + +VOID +HidParser_FreeCollection( + IN PHID_PARSER Parser, + IN PHID_COLLECTION Collection) +{ + // + // not implemented + // +} + +HIDPARSER_STATUS +HidParser_AllocateCollection( + IN PHID_PARSER Parser, + IN PHID_COLLECTION ParentCollection, + IN UCHAR Type, + IN PLOCAL_ITEM_STATE LocalItemState, + OUT PHID_COLLECTION * OutCollection) +{ + PHID_COLLECTION Collection; + USAGE_VALUE UsageValue; + + // + // first allocate the collection + // + Collection = (PHID_COLLECTION)Parser->Alloc(sizeof(HID_COLLECTION)); + if (!Collection) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // init collection + // + Collection->Root = ParentCollection; + Collection->Type = Type; + Collection->StringID = LocalItemState->StringIndex; + Collection->PhysicalID = LocalItemState->DesignatorIndex; + + // + // set Usage + // + ASSERT(LocalItemState); + ASSERT(LocalItemState->UsageStack); + + if (LocalItemState->UsageStackUsed > 0) + { + // + // usage value from first local stack item + // + UsageValue.u.Extended = LocalItemState->UsageStack[0].u.Extended; + } + else if (LocalItemState->UsageMinimumSet) + { + // + // use value from minimum + // + UsageValue.u.Extended = LocalItemState->UsageMinimum.u.Extended; + } + else if (LocalItemState->UsageMaximumSet) + { + // + // use value from maximum + // + UsageValue.u.Extended = LocalItemState->UsageMaximum.u.Extended; + } + else if (Type == COLLECTION_LOGICAL) + { + // + // root collection + // + UsageValue.u.Extended = 0; + } + else + { + // + // no usage set + // + Parser->Debug("HIDPARSE] No usage set\n"); + UsageValue.u.Extended = 0; + } + + // + // store usage + // + Collection->Usage = UsageValue.u.Extended; + + // + // store result + // + *OutCollection = Collection; + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +HidParser_AddCollection( + IN PHID_PARSER Parser, + IN PHID_COLLECTION CurrentCollection, + IN PHID_COLLECTION NewCollection) +{ + PHID_COLLECTION * NewAllocCollection; + ULONG CollectionCount; + + // + // increment collection array + // + CollectionCount = CurrentCollection->NodeCount + 1; + + // + // allocate new collection + // + NewAllocCollection = (PHID_COLLECTION*)Parser->Alloc(sizeof(PHID_COLLECTION) * CollectionCount); + if (!NewAllocCollection) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + if (CurrentCollection->NodeCount) + { + // + // copy old array + // + Parser->Copy(NewAllocCollection, CurrentCollection->Nodes, CurrentCollection->NodeCount * sizeof(PHID_COLLECTION)); + + // + // delete old array + // + Parser->Free(CurrentCollection->Nodes); + } + + // + // insert new item + // + NewAllocCollection[CurrentCollection->NodeCount] = (struct __HID_COLLECTION__*)NewCollection; + + + // + // store new array + // + CurrentCollection->Nodes = NewAllocCollection; + CurrentCollection->NodeCount++; + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +HidParser_FindReportInCollection( + IN PHID_COLLECTION Collection, + IN UCHAR ReportType, + IN UCHAR ReportID, + OUT PHID_REPORT *OutReport) +{ + ULONG Index; + HIDPARSER_STATUS Status; + + // + // search in local list + // + for(Index = 0; Index < Collection->ReportCount; Index++) + { + if (Collection->Reports[Index]->Type == ReportType && Collection->Reports[Index]->ReportID == ReportID) + { + // + // found report + // + *OutReport = Collection->Reports[Index]; + return HIDPARSER_STATUS_SUCCESS; + } + } + + // + // search in sub collections + // + for(Index = 0; Index < Collection->NodeCount; Index++) + { + Status = HidParser_FindReportInCollection(Collection->Nodes[Index], ReportType, ReportID, OutReport); + if (Status == HIDPARSER_STATUS_SUCCESS) + return Status; + } + + // + // no such report found + // + *OutReport = NULL; + return HIDPARSER_STATUS_REPORT_NOT_FOUND; +} + + +HIDPARSER_STATUS +HidParser_FindReport( + IN PHID_PARSER Parser, + IN PHID_PARSER_CONTEXT ParserContext, + IN UCHAR ReportType, + IN UCHAR ReportID, + OUT PHID_REPORT *OutReport) +{ + // + // search in current top level collection + // + return HidParser_FindReportInCollection(ParserContext->RootCollection->Nodes[ParserContext->RootCollection->NodeCount-1], ReportType, ReportID, OutReport); +} + +HIDPARSER_STATUS +HidParser_AllocateReport( + IN PHID_PARSER Parser, + IN UCHAR ReportType, + IN UCHAR ReportID, + OUT PHID_REPORT *OutReport) +{ + PHID_REPORT Report; + + // + // allocate report + // + Report = (PHID_REPORT)Parser->Alloc(sizeof(HID_REPORT)); + if (!Report) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // init report + // + Report->ReportID = ReportID; + Report->Type = ReportType; + + // + // done + // + *OutReport = Report; + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +HidParser_AddReportToCollection( + IN PHID_PARSER Parser, + IN PHID_PARSER_CONTEXT ParserContext, + IN PHID_COLLECTION CurrentCollection, + IN PHID_REPORT NewReport) +{ + PHID_REPORT * NewReportArray; + + // + // allocate new report array + // + NewReportArray = (PHID_REPORT*)Parser->Alloc(sizeof(PHID_REPORT) * (CurrentCollection->ReportCount + 1)); + if (!NewReportArray) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + if (CurrentCollection->ReportCount) + { + // + // copy old array contents + // + Parser->Copy(NewReportArray, CurrentCollection->Reports, sizeof(PHID_REPORT) * CurrentCollection->ReportCount); + + // + // free old array + // + Parser->Free(CurrentCollection->Reports); + } + + // + // store result + // + NewReportArray[CurrentCollection->ReportCount] = NewReport; + CurrentCollection->Reports = NewReportArray; + CurrentCollection->ReportCount++; + + // + // completed successfully + // + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +HidParser_GetReport( + IN PHID_PARSER Parser, + IN PHID_PARSER_CONTEXT ParserContext, + IN PHID_COLLECTION Collection, + IN UCHAR ReportType, + IN UCHAR ReportID, + IN UCHAR CreateIfNotExists, + OUT PHID_REPORT *OutReport) +{ + HIDPARSER_STATUS Status; + + // + // try finding existing report + // + Status = HidParser_FindReport(Parser, ParserContext, ReportType, ReportID, OutReport); + if (Status == HIDPARSER_STATUS_SUCCESS || CreateIfNotExists == FALSE) + { + // + // founed report + // + return Status; + } + + // + // allocate new report + // + Status = HidParser_AllocateReport(Parser, ReportType, ReportID, OutReport); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to allocate report + // + return Status; + } + + // + // add report + // + Status = HidParser_AddReportToCollection(Parser, ParserContext, Collection, *OutReport); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to allocate report + // + Parser->Free(*OutReport); + } + + // + // done + // + return Status; +} + +HIDPARSER_STATUS +HidParser_ReserveReportItems( + IN PHID_PARSER Parser, + IN PHID_REPORT Report, + IN ULONG ReportCount, + OUT PHID_REPORT *OutReport) +{ + PHID_REPORT NewReport; + ULONG OldSize, Size; + + if (Report->ItemCount + ReportCount <= Report->ItemAllocated) + { + // + // space is already allocated + // + *OutReport = Report; + return HIDPARSER_STATUS_SUCCESS; + } + + // + //calculate new size + // + OldSize = sizeof(HID_REPORT) + (Report->ItemCount) * sizeof(HID_REPORT_ITEM); + Size = ReportCount * sizeof(HID_REPORT_ITEM); + + // + // allocate memory + // + NewReport = (PHID_REPORT)Parser->Alloc(Size + OldSize); + if (!NewReport) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // copy old report + // + Parser->Copy(NewReport, Report, OldSize); + + // + // increase array size + // + NewReport->ItemAllocated += ReportCount; + + // + // store result + // + *OutReport = NewReport; + + // + // completed sucessfully + // + return HIDPARSER_STATUS_SUCCESS; +} + +VOID +HidParser_SignRange( + IN ULONG Minimum, + IN ULONG Maximum, + OUT PULONG NewMinimum, + OUT PULONG NewMaximum) +{ + ULONG Mask = 0x80000000; + ULONG Index; + + for (Index = 0; Index < 4; Index++) + { + if (Minimum & Mask) + { + Minimum |= Mask; + if (Maximum & Mask) + Maximum |= Mask; + return; + } + + Mask >>= 8; + Mask |= 0xff000000; + } + + *NewMinimum = Minimum; + *NewMaximum = Maximum; +} + +HIDPARSER_STATUS +HidParser_InitReportItem( + IN PHID_REPORT Report, + IN PHID_REPORT_ITEM ReportItem, + IN PGLOBAL_ITEM_STATE GlobalItemState, + IN PLOCAL_ITEM_STATE LocalItemState, + IN PMAIN_ITEM_DATA ItemData, + IN ULONG ReportItemIndex) +{ + ULONG LogicalMinimum; + ULONG LogicalMaximum; + ULONG PhysicalMinimum; + ULONG PhysicalMaximum; + ULONG UsageMinimum; + ULONG UsageMaximum; + USAGE_VALUE UsageValue; + + // + // get logical bounds + // + LogicalMinimum = GlobalItemState->LogicalMinimum; + LogicalMaximum = GlobalItemState->LogicialMaximum; + if (LogicalMinimum > LogicalMaximum) + { + // + // make them signed + // + HidParser_SignRange(LogicalMinimum, LogicalMaximum, &LogicalMinimum, &LogicalMaximum); + } + //ASSERT(LogicalMinimum <= LogicalMaximum); + + // + // get physical bounds + // + PhysicalMinimum = GlobalItemState->PhysicalMinimum; + PhysicalMaximum = GlobalItemState->PhysicalMaximum; + if (PhysicalMinimum > PhysicalMaximum) + { + // + // make them signed + // + HidParser_SignRange(PhysicalMinimum, PhysicalMaximum, &PhysicalMinimum, &PhysicalMaximum); + } + //ASSERT(PhysicalMinimum <= PhysicalMaximum); + + // + // get usage bounds + // + UsageMinimum = 0; + UsageMaximum = 0; + if (ItemData->ArrayVariable == FALSE) + { + // + // get usage bounds + // + UsageMinimum = LocalItemState->UsageMinimum.u.Extended; + UsageMaximum = LocalItemState->UsageMaximum.u.Extended; + } + else + { + // + // get usage value from stack + // + if (ReportItemIndex < LocalItemState->UsageStackUsed) + { + // + // use stack item + // + UsageValue = LocalItemState->UsageStack[ReportItemIndex]; + } + else + { + // + // get usage minimum from local state + // + UsageValue = LocalItemState->UsageMinimum; + + // + // append item index + // + UsageValue.u.Extended += ReportItemIndex; + + if (LocalItemState->UsageMaximumSet) + { + if (UsageValue.u.Extended > LocalItemState->UsageMaximum.u.Extended) + { + // + // maximum reached + // + UsageValue.u.Extended = LocalItemState->UsageMaximum.u.Extended; + } + } + } + + // + // usage usage bounds + // + UsageMinimum = UsageMaximum = UsageValue.u.Extended; + } + + // + // now store all values + // + ReportItem->ByteOffset = (Report->ReportSize / 8); + ReportItem->Shift = (Report->ReportSize % 8); + ReportItem->Mask = ~(0xFFFFFFFF << GlobalItemState->ReportSize); + ReportItem->BitCount = GlobalItemState->ReportSize; + ReportItem->HasData = (ItemData->DataConstant == FALSE); + ReportItem->Array = (ItemData->ArrayVariable == 0); + ReportItem->Relative = (ItemData->Relative == TRUE); + ReportItem->Minimum = LogicalMinimum; + ReportItem->Maximum = LogicalMaximum; + ReportItem->UsageMinimum = UsageMinimum; + ReportItem->UsageMaximum = UsageMaximum; + + // + // increment report size + // + Report->ReportSize += GlobalItemState->ReportSize; + + // + // completed successfully + // + return HIDPARSER_STATUS_SUCCESS; +} + +BOOLEAN +HidParser_UpdateCurrentCollectionReport( + IN PHID_COLLECTION Collection, + IN PHID_REPORT Report, + IN PHID_REPORT NewReport) +{ + ULONG Index; + BOOLEAN Found = FALSE, TempFound; + + // + // search in local list + // + for(Index = 0; Index < Collection->ReportCount; Index++) + { + if (Collection->Reports[Index] == Report) + { + // + // update report + // + Collection->Reports[Index] = NewReport; + Found = TRUE; + } + } + + // + // search in sub collections + // + for(Index = 0; Index < Collection->NodeCount; Index++) + { + // + // was it found + // + TempFound = HidParser_UpdateCurrentCollectionReport(Collection->Nodes[Index], Report, NewReport); + if (TempFound) + { + // + // the same report should not be found in different collections + // + ASSERT(Found == FALSE); + Found = TRUE; + } + } + + // + // done + // + return Found; +} + +BOOLEAN +HidParser_UpdateCollectionReport( + IN PHID_PARSER_CONTEXT ParserContext, + IN PHID_REPORT Report, + IN PHID_REPORT NewReport) +{ + // + // update in current collection + // + return HidParser_UpdateCurrentCollectionReport(ParserContext->RootCollection->Nodes[ParserContext->RootCollection->NodeCount-1], Report, NewReport); +} + + +HIDPARSER_STATUS +HidParser_AddMainItem( + IN PHID_PARSER Parser, + IN PHID_PARSER_CONTEXT ParserContext, + IN PHID_REPORT Report, + IN PGLOBAL_ITEM_STATE GlobalItemState, + IN PLOCAL_ITEM_STATE LocalItemState, + IN PMAIN_ITEM_DATA ItemData, + IN PHID_COLLECTION Collection) +{ + HIDPARSER_STATUS Status; + ULONG Index; + PHID_REPORT NewReport; + BOOLEAN Found; + + // + // first grow report item array + // + Status = HidParser_ReserveReportItems(Parser, Report, GlobalItemState->ReportCount, &NewReport); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to allocate memory + // + return Status; + } + + if (NewReport != Report) + { + // + // update current top level collection + // + Found = HidParser_UpdateCollectionReport(ParserContext, Report, NewReport); + ASSERT(Found); + } + + // + // sanity check + // + ASSERT(NewReport->ItemCount + GlobalItemState->ReportCount <= NewReport->ItemAllocated); + + for(Index = 0; Index < GlobalItemState->ReportCount; Index++) + { + Status = HidParser_InitReportItem(NewReport, &NewReport->Items[NewReport->ItemCount], GlobalItemState, LocalItemState, ItemData, Index); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // failed to init report item + // + return Status; + } + + // + // increment report item count + // + NewReport->ItemCount++; + } + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} + +HIDPARSER_STATUS +AllocateParserContext( + IN PHID_PARSER Parser, + OUT PHID_PARSER_CONTEXT *OutParserContext) +{ + PHID_PARSER_CONTEXT ParserContext; + + ParserContext = Parser->Alloc(sizeof(HID_PARSER_CONTEXT)); + if (!ParserContext) + { + // + // failed + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // store result + // + *OutParserContext = ParserContext; + return HIDPARSER_STATUS_SUCCESS; +} + + +HIDPARSER_STATUS +HidParser_ParseReportDescriptor( + IN PHID_PARSER Parser, + IN PUCHAR ReportDescriptor, + IN ULONG ReportLength, + OUT PVOID *OutParser) +{ + PGLOBAL_ITEM_STATE LinkedGlobalItemState, NextLinkedGlobalItemState; + ULONG Index; + PUSAGE_VALUE NewUsageStack, UsageValue; + HIDPARSER_STATUS Status; + PHID_COLLECTION CurrentCollection, NewCollection; + PUCHAR CurrentOffset, ReportEnd; + PITEM_PREFIX CurrentItem; + ULONG CurrentItemSize; + PLONG_ITEM CurrentLongItem; + PSHORT_ITEM CurrentShortItem; + ULONG Data; + UCHAR ReportType; + PHID_REPORT Report; + PMAIN_ITEM_DATA MainItemData; + PHID_PARSER_CONTEXT ParserContext; + + // + // allocate parser + // + Status = AllocateParserContext(Parser, &ParserContext); + if (Status != HIDPARSER_STATUS_SUCCESS) + return Status; + + + // + // allocate usage stack + // + ParserContext->LocalItemState.UsageStackAllocated = 10; + ParserContext->LocalItemState.UsageStack = (PUSAGE_VALUE)Parser->Alloc(ParserContext->LocalItemState.UsageStackAllocated * sizeof(USAGE_VALUE)); + if (!ParserContext->LocalItemState.UsageStack) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // now allocate root collection + // + Status = HidParser_AllocateCollection(Parser, NULL, COLLECTION_LOGICAL, &ParserContext->LocalItemState, &ParserContext->RootCollection); + if (Status != HIDPARSER_STATUS_SUCCESS) + { + // + // no memory + // + Parser->Free(ParserContext->LocalItemState.UsageStack); + ParserContext->LocalItemState.UsageStack = NULL; + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // start parsing + // + CurrentCollection = ParserContext->RootCollection; + CurrentOffset = ReportDescriptor; + ReportEnd = ReportDescriptor + ReportLength; + + do + { + // + // get current item + // + CurrentItem = (PITEM_PREFIX)CurrentOffset; + + // + // get item size + // + CurrentItemSize = ItemSize[CurrentItem->Size]; + Data = 0; + + if (CurrentItem->Type == ITEM_TYPE_LONG) + { + // + // increment item size with size of data item + // + CurrentLongItem = (PLONG_ITEM)CurrentItem; + CurrentItemSize += CurrentLongItem->DataSize; + } + else + { + // + // get short item + // + CurrentShortItem = (PSHORT_ITEM)CurrentItem; + + // + // get associated data + // + //ASSERT(CurrentItemSize == 1 || CurrentItemSize == 2 || CurrentItemSize == 4); + if (CurrentItemSize == 1) + Data = CurrentShortItem->Data.UData8[0]; + else if (CurrentItemSize == 2) + Data = CurrentShortItem->Data.UData16[0]; + else if (CurrentItemSize == 4) + Data = CurrentShortItem->Data.UData32; + else + { + // + // invalid item size + // + //Parser->Debug("CurrentItem invalid item size %lu\n", CurrentItemSize); + } + + } + Parser->Debug("Tag %x Type %x Size %x Offset %lu Length %lu\n", CurrentItem->Tag, CurrentItem->Type, CurrentItem->Size, ((ULONG_PTR)CurrentItem - (ULONG_PTR)ReportDescriptor), ReportLength); + // + // handle items + // + ASSERT(CurrentItem->Type >= ITEM_TYPE_MAIN && CurrentItem->Type <= ITEM_TYPE_LONG); + switch(CurrentItem->Type) + { + case ITEM_TYPE_MAIN: + { + // preprocess the local state if relevant (usages for + // collections and report items) + if (CurrentItem->Tag != ITEM_TAG_MAIN_END_COLLECTION) + { + // make all usages extended for easier later processing + for (Index = 0; Index < ParserContext->LocalItemState.UsageStackUsed; Index++) + { + // + // is it already extended + // + if (ParserContext->LocalItemState.UsageStack[Index].IsExtended) + continue; + + // + // extend usage item + // + ParserContext->LocalItemState.UsageStack[Index].u.s.UsagePage = ParserContext->GlobalItemState.UsagePage; + ParserContext->LocalItemState.UsageStack[Index].IsExtended = TRUE; + } + + if (!ParserContext->LocalItemState.UsageMinimum.IsExtended) { + // the specs say if one of them is extended they must + // both be extended, so if the minimum isn't, the + // maximum mustn't either. + ParserContext->LocalItemState.UsageMinimum.u.s.UsagePage + = ParserContext->LocalItemState.UsageMaximum.u.s.UsagePage + = ParserContext->GlobalItemState.UsagePage; + ParserContext->LocalItemState.UsageMinimum.IsExtended + = ParserContext->LocalItemState.UsageMaximum.IsExtended = TRUE; + } + + //LocalItemState.usage_stack = usageStack; + //ParserContext->LocalItemState.UsageStackUsed = UsageStackUsed; + } + + if (CurrentItem->Tag == ITEM_TAG_MAIN_COLLECTION) { + + // + // allocate new collection + // + Status = HidParser_AllocateCollection(Parser, CurrentCollection, (UCHAR)Data, &ParserContext->LocalItemState, &NewCollection); + ASSERT(Status == HIDPARSER_STATUS_SUCCESS); + + // + // add new collection to current collection + // + Status = HidParser_AddCollection(Parser, CurrentCollection, NewCollection); + ASSERT(Status == HIDPARSER_STATUS_SUCCESS); + + // + // make new collection current + // + CurrentCollection = NewCollection; + } + else if (CurrentItem->Tag == ITEM_TAG_MAIN_END_COLLECTION) + { + // + // assert on ending the root collection + // + ASSERT(CurrentCollection != ParserContext->RootCollection); + + // + // use parent of current collection + // + CurrentCollection = CurrentCollection->Root; + ASSERT(CurrentCollection); + } + else + { + ReportType = HID_REPORT_TYPE_ANY; + + switch (CurrentItem->Tag) { + case ITEM_TAG_MAIN_INPUT: + ReportType = HID_REPORT_TYPE_INPUT; + break; + + case ITEM_TAG_MAIN_OUTPUT: + ReportType = HID_REPORT_TYPE_OUTPUT; + break; + + case ITEM_TAG_MAIN_FEATURE: + ReportType = HID_REPORT_TYPE_FEATURE; + break; + + default: + Parser->Debug("[HIDPARSE] Unknown ReportType Tag %x Type %x Size %x CurrentItemSize %x\n", CurrentItem->Tag, CurrentItem->Type, CurrentItem->Size, CurrentItemSize); + ASSERT(FALSE); + break; + } + + if (ReportType == HID_REPORT_TYPE_ANY) + break; + + // + // get report + // + Status = HidParser_GetReport(Parser, ParserContext, CurrentCollection, ReportType, ParserContext->GlobalItemState.ReportId, TRUE, &Report); + ASSERT(Status == HIDPARSER_STATUS_SUCCESS); + + // fill in a sensible default if the index isn't set + if (!ParserContext->LocalItemState.DesignatorIndexSet) { + ParserContext->LocalItemState.DesignatorIndex + = ParserContext->LocalItemState.DesignatorMinimum; + } + + if (!ParserContext->LocalItemState.StringIndexSet) + ParserContext->LocalItemState.StringIndex = ParserContext->LocalItemState.StringMinimum; + + // + // get main item data + // + MainItemData = (PMAIN_ITEM_DATA)&Data; + + // + // add states & data to the report + // + Status = HidParser_AddMainItem(Parser, ParserContext, Report, &ParserContext->GlobalItemState, &ParserContext->LocalItemState, MainItemData, CurrentCollection); + ASSERT(Status == HIDPARSER_STATUS_SUCCESS); + } + + // + // backup stack + // + Index = ParserContext->LocalItemState.UsageStackAllocated; + NewUsageStack = ParserContext->LocalItemState.UsageStack; + + // + // reset the local item state and clear the usage stack + // + Parser->Zero(&ParserContext->LocalItemState, sizeof(LOCAL_ITEM_STATE)); + + // + // restore stack + // + ParserContext->LocalItemState.UsageStack = NewUsageStack; + ParserContext->LocalItemState.UsageStackAllocated = Index; + break; + } + case ITEM_TYPE_GLOBAL: + { + switch (CurrentItem->Tag) { + case ITEM_TAG_GLOBAL_USAGE_PAGE: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_USAGE_PAGE %x\n", Data); + ParserContext->GlobalItemState.UsagePage = Data; + break; + case ITEM_TAG_GLOBAL_LOGICAL_MINIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_LOGICAL_MINIMUM %x\n", Data); + ParserContext->GlobalItemState.LogicalMinimum = Data; + break; + + case ITEM_TAG_GLOBAL_LOGICAL_MAXIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_LOCAL_MAXIMUM %x\n", Data); + ParserContext->GlobalItemState.LogicialMaximum = Data; + break; + + case ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM %x\n", Data); + ParserContext->GlobalItemState.PhysicalMinimum = Data; + break; + + case ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM %x\n", Data); + ParserContext->GlobalItemState.PhysicalMaximum = Data; + break; + + case ITEM_TAG_GLOBAL_UNIT_EXPONENT: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_UNIT_EXPONENT %x\n", Data); + ParserContext->GlobalItemState.UnitExponent = Data; + break; + + case ITEM_TAG_GLOBAL_UNIT: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_UNIT %x\n", Data); + ParserContext->GlobalItemState.Unit = Data; + break; + + case ITEM_TAG_GLOBAL_REPORT_SIZE: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_SIZE %x\n", Data); + ParserContext->GlobalItemState.ReportSize = Data; + break; + + case ITEM_TAG_GLOBAL_REPORT_ID: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_ID %x\n", Data); + ParserContext->GlobalItemState.ReportId = Data; + ParserContext->UseReportIDs = TRUE; + break; + + case ITEM_TAG_GLOBAL_REPORT_COUNT: + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_REPORT_COUNT %x\n", Data); + ParserContext->GlobalItemState.ReportCount = Data; + break; + + case ITEM_TAG_GLOBAL_PUSH: + { + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_PUSH\n"); + // + // allocate global item state + // + LinkedGlobalItemState = (PGLOBAL_ITEM_STATE)Parser->Alloc(sizeof(GLOBAL_ITEM_STATE)); + ASSERT(LinkedGlobalItemState); + + // + // copy global item state + // + Parser->Copy(LinkedGlobalItemState, &ParserContext->GlobalItemState, sizeof(GLOBAL_ITEM_STATE)); + + // + // store pushed item in link member + // + ParserContext->GlobalItemState.Next = (struct __GLOBAL_ITEM_STATE__*)LinkedGlobalItemState; + break; + } + case ITEM_TAG_GLOBAL_POP: + { + Parser->Debug("[HIDPARSE] ITEM_TAG_GLOBAL_POP\n"); + if (ParserContext->GlobalItemState.Next == NULL) + { + // + // pop without push + // + ASSERT(FALSE); + break; + } + + // + // get link + // + LinkedGlobalItemState = (PGLOBAL_ITEM_STATE)ParserContext->GlobalItemState.Next; + + // + // replace current item with linked one + // + Parser->Copy(&ParserContext->GlobalItemState, LinkedGlobalItemState, sizeof(GLOBAL_ITEM_STATE)); + + // + // free item + // + Parser->Free(LinkedGlobalItemState); + break; + } + + default: + // + // unknown / unsupported tag + // + ASSERT(FALSE); + break; + } + + break; + } + case ITEM_TYPE_LOCAL: + { + switch (CurrentItem->Tag) + { + case ITEM_TAG_LOCAL_USAGE: + { + if (ParserContext->LocalItemState.UsageStackUsed >= ParserContext->LocalItemState.UsageStackAllocated) + { + // + // increment stack size + // + ParserContext->LocalItemState.UsageStackAllocated += 10; + + // + // build new usage stack + // + NewUsageStack = (PUSAGE_VALUE)Parser->Alloc(sizeof(USAGE_VALUE) * ParserContext->LocalItemState.UsageStackAllocated); + ASSERT(NewUsageStack); + + // + // copy old usage stack + // + Parser->Copy(NewUsageStack, ParserContext->LocalItemState.UsageStack, sizeof(USAGE_VALUE) * (ParserContext->LocalItemState.UsageStackAllocated - 10)); + + // + // free old usage stack + // + Parser->Free(ParserContext->LocalItemState.UsageStack); + + // + // replace with new usage stack + // + ParserContext->LocalItemState.UsageStack = NewUsageStack; + } + + // + // get fresh usage value + // + UsageValue = &ParserContext->LocalItemState.UsageStack[ParserContext->LocalItemState.UsageStackUsed]; + + // + // init usage stack + // + UsageValue->IsExtended = CurrentItemSize == sizeof(ULONG); + UsageValue->u.Extended = Data; + + // + // increment usage stack usage count + // + ParserContext->LocalItemState.UsageStackUsed++; + break; + } + + case ITEM_TAG_LOCAL_USAGE_MINIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_USAGE_MINIMUM Data %x\n", Data); + ParserContext->LocalItemState.UsageMinimum.u.Extended = Data; + ParserContext->LocalItemState.UsageMinimum.IsExtended + = CurrentItemSize == sizeof(ULONG); + ParserContext->LocalItemState.UsageMinimumSet = TRUE; + break; + + case ITEM_TAG_LOCAL_USAGE_MAXIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_USAGE_MAXIMUM Data %x ItemSize %x %x\n", Data, CurrentItemSize, CurrentItem->Size); + ParserContext->LocalItemState.UsageMaximum.u.Extended = Data; + ParserContext->LocalItemState.UsageMaximum.IsExtended + = CurrentItemSize == sizeof(ULONG); + ParserContext->LocalItemState.UsageMaximumSet = TRUE; + break; + + case ITEM_TAG_LOCAL_DESIGNATOR_INDEX: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_DESIGNATOR_INDEX Data %x\n", Data); + ParserContext->LocalItemState.DesignatorIndex = Data; + ParserContext->LocalItemState.DesignatorIndexSet = TRUE; + break; + + case ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM Data %x\n", Data); + ParserContext->LocalItemState.DesignatorMinimum = Data; + break; + + case ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM Data %x\n", Data); + ParserContext->LocalItemState.DesignatorMaximum = Data; + break; + + case ITEM_TAG_LOCAL_STRING_INDEX: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_STRING_INDEX Data %x\n", Data); + ParserContext->LocalItemState.StringIndex = Data; + ParserContext->LocalItemState.StringIndexSet = TRUE; + break; + + case ITEM_TAG_LOCAL_STRING_MINIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_STRING_MINIMUM Data %x\n", Data); + ParserContext->LocalItemState.StringMinimum = Data; + break; + + case ITEM_TAG_LOCAL_STRING_MAXIMUM: + Parser->Debug("[HIDPARSE] ITEM_TAG_LOCAL_STRING_MAXIMUM Data %x\n", Data); + ParserContext->LocalItemState.StringMaximum = Data; + break; + + default: + Parser->Debug("Unknown Local Item Tag %x\n", CurrentItem->Tag); + ASSERT(FALSE); + break; + } + break; + } + + case ITEM_TYPE_LONG: + { + CurrentLongItem = (PLONG_ITEM)CurrentItem; + Parser->Debug("Unsupported ITEM_TYPE_LONG Tag %x\n", CurrentLongItem->LongItemTag); + break; + } + } + + // + // move to next item + // + CurrentOffset += CurrentItemSize + sizeof(ITEM_PREFIX); + + + }while(CurrentOffset < ReportEnd); + + + // + // cleanup global stack + // + LinkedGlobalItemState = (PGLOBAL_ITEM_STATE)ParserContext->GlobalItemState.Next; + while(LinkedGlobalItemState != NULL) + { + Parser->Debug("[HIDPARSE] Freeing GlobalState %p\n", LinkedGlobalItemState); + // + // free global item state + // + NextLinkedGlobalItemState = (PGLOBAL_ITEM_STATE)LinkedGlobalItemState->Next; + + // + // free state + // + Parser->Free(LinkedGlobalItemState); + + // + // move to next global state + // + LinkedGlobalItemState = NextLinkedGlobalItemState; + } + + // + // free usage stack + // + Parser->Free(ParserContext->LocalItemState.UsageStack); + ParserContext->LocalItemState.UsageStack = NULL; + + // + // store result + // + *OutParser = ParserContext; + + // + // done + // + return HIDPARSER_STATUS_SUCCESS; +} + +PHID_COLLECTION +HidParser_GetCollection( + IN PHID_PARSER Parser, + PHID_PARSER_CONTEXT ParserContext, + IN ULONG CollectionNumber) +{ + // + // sanity checks + // + ASSERT(ParserContext); + ASSERT(ParserContext->RootCollection); + ASSERT(ParserContext->RootCollection->NodeCount); + + // + // is collection index out of bounds + // + if (CollectionNumber < ParserContext->RootCollection->NodeCount) + { + // + // valid collection + // + return ParserContext->RootCollection->Nodes[CollectionNumber]; + } + + // + // no such collection + // + Parser->Debug("HIDPARSE] No such collection %lu\n", CollectionNumber); + return NULL; +} + + +ULONG +HidParser_NumberOfTopCollections( + IN PVOID ParserCtx) +{ + PHID_PARSER_CONTEXT ParserContext; + + // + // get parser context + // + ParserContext = (PHID_PARSER_CONTEXT)ParserCtx; + + // + // sanity checks + // + ASSERT(ParserContext); + ASSERT(ParserContext->RootCollection); + ASSERT(ParserContext->RootCollection->NodeCount); + + // + // number of top collections + // + return ParserContext->RootCollection->NodeCount; +} + +HIDPARSER_STATUS +HidParser_BuildContext( + IN PHID_PARSER Parser, + IN PVOID ParserContext, + IN ULONG CollectionIndex, + IN ULONG ContextSize, + OUT PVOID *CollectionContext) +{ + PHID_COLLECTION Collection; + PVOID Context; + HIDPARSER_STATUS Status; + + // + // lets get the collection + // + Collection = HidParser_GetCollection(Parser, (PHID_PARSER_CONTEXT)ParserContext, CollectionIndex); + ASSERT(Collection); + + // + // lets allocate the context + // + Context = Parser->Alloc(ContextSize); + if (Context == NULL) + { + // + // no memory + // + return HIDPARSER_STATUS_INSUFFICIENT_RESOURCES; + } + + // + // lets build the context + // + Status = HidParser_BuildCollectionContext(Parser, Collection, Context, ContextSize); + if (Status == HIDPARSER_STATUS_SUCCESS) + { + // + // store context + // + *CollectionContext = Context; + } + + // + // done + // + return Status; +} + + +ULONG +HidParser_GetContextSize( + IN PHID_PARSER Parser, + IN PVOID ParserContext, + IN ULONG CollectionIndex) +{ + PHID_COLLECTION Collection; + ULONG Size; + + // + // lets get the collection + // + Collection = HidParser_GetCollection(Parser, (PHID_PARSER_CONTEXT)ParserContext, CollectionIndex); + + // + // calculate size + // + Size = HidParser_CalculateContextSize(Collection); + return Size; +} + diff --git a/reactos/lib/drivers/hidparser/parser.h b/reactos/lib/drivers/hidparser/parser.h new file mode 100644 index 00000000000..ef0d7f3e421 --- /dev/null +++ b/reactos/lib/drivers/hidparser/parser.h @@ -0,0 +1,382 @@ + +#pragma once +#include +#include +#include +#include "hidparser.h" +#include + + /* + * Copyright 2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + +#define HID_REPORT_TYPE_ANY 0x07 + + +#define ITEM_TYPE_MAIN 0x0 +#define ITEM_TYPE_GLOBAL 0x1 +#define ITEM_TYPE_LOCAL 0x2 +#define ITEM_TYPE_LONG 0x3 + +#define ITEM_TAG_MAIN_INPUT 0x8 +#define ITEM_TAG_MAIN_OUTPUT 0x9 +#define ITEM_TAG_MAIN_FEATURE 0xb +#define ITEM_TAG_MAIN_COLLECTION 0xa +#define ITEM_TAG_MAIN_END_COLLECTION 0xc + +#define ITEM_TAG_GLOBAL_USAGE_PAGE 0x0 +#define ITEM_TAG_GLOBAL_LOGICAL_MINIMUM 0x1 +#define ITEM_TAG_GLOBAL_LOGICAL_MAXIMUM 0x2 +#define ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM 0x3 +#define ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM 0x4 +#define ITEM_TAG_GLOBAL_UNIT_EXPONENT 0x5 +#define ITEM_TAG_GLOBAL_UNIT 0x6 +#define ITEM_TAG_GLOBAL_REPORT_SIZE 0x7 +#define ITEM_TAG_GLOBAL_REPORT_ID 0x8 +#define ITEM_TAG_GLOBAL_REPORT_COUNT 0x9 +#define ITEM_TAG_GLOBAL_PUSH 0xa +#define ITEM_TAG_GLOBAL_POP 0xb + +#define ITEM_TAG_LOCAL_USAGE 0x0 +#define ITEM_TAG_LOCAL_USAGE_MINIMUM 0x1 +#define ITEM_TAG_LOCAL_USAGE_MAXIMUM 0x2 +#define ITEM_TAG_LOCAL_DESIGNATOR_INDEX 0x3 +#define ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM 0x4 +#define ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM 0x5 +#define ITEM_TAG_LOCAL_STRING_INDEX 0x7 +#define ITEM_TAG_LOCAL_STRING_MINIMUM 0x8 +#define ITEM_TAG_LOCAL_STRING_MAXIMUM 0x9 +#define ITEM_TAG_LOCAL_DELIMITER 0xa + +#define ITEM_TAG_LONG 0xf + +#define COLLECTION_PHYSICAL 0x00 +#define COLLECTION_APPLICATION 0x01 +#define COLLECTION_LOGICAL 0x02 +#define COLLECTION_REPORT 0x03 +#define COLLECTION_NAMED_ARRAY 0x04 +#define COLLECTION_USAGE_SWITCH 0x05 +#define COLLECTION_USAGE_MODIFIER 0x06 +#define COLLECTION_ALL 0xff + +#define UNIT_SYSTEM 0x0 +#define UNIT_LENGTH 0x1 +#define UNIT_MASS 0x2 +#define UNIT_TIME 0x3 +#define UNIT_TEMPERATURE 0x4 +#define UNIT_CURRENT 0x5 +#define UNIT_LUMINOUS_INTENSITY 0x6 + +#define USAGE_PAGE_SHIFT 16 +#define USAGE_PAGE_MASK 0xffff +#define USAGE_ID_SHIFT 0 +#define USAGE_ID_MASK 0xffff + +typedef struct +{ + UCHAR Size:2; + UCHAR Type:2; + UCHAR Tag:4; +}ITEM_PREFIX, *PITEM_PREFIX; + +typedef struct +{ + ITEM_PREFIX Prefix; + + union + { + UCHAR UData8[4]; + CHAR SData8[4]; + USHORT UData16[2]; + SHORT SData16[2]; + ULONG UData32; + LONG SData32; + }Data; + +}SHORT_ITEM, *PSHORT_ITEM; + +typedef struct +{ + ITEM_PREFIX Prefix; + UCHAR DataSize; + UCHAR LongItemTag; + UCHAR Data[0]; + +}LONG_ITEM,*PLONG_ITEM; + + +#define LBITFIELD9(b1,b2,b3,b4,b5,b6,b7,b8,b9) USHORT b9,b8,b7,b6,b5,b4,b3,b2,b1 +typedef struct +{ + USHORT DataConstant:1; + USHORT ArrayVariable:1; + USHORT Relative:1; + USHORT Wrap:1; + USHORT NonLinear:1; + USHORT NoPreferred:1; + USHORT NullState:1; + USHORT IsVolatile:1; + USHORT BitsBytes:1; + UCHAR reserved[2]; + +}MAIN_ITEM_DATA, *PMAIN_ITEM_DATA; + +typedef struct __GLOBAL_ITEM_STATE_ +{ + USHORT UsagePage; + ULONG LogicalMinimum; + ULONG LogicialMaximum; + ULONG PhysicalMinimum; + ULONG PhysicalMaximum; + UCHAR UnitExponent; + UCHAR Unit; + ULONG ReportSize; + ULONG ReportCount; + UCHAR ReportId; + struct __GLOBAL_ITEM_STATE__ * Next; +}GLOBAL_ITEM_STATE, *PGLOBAL_ITEM_STATE; + + +typedef struct usage_value +{ + union + { + struct { + USHORT UsageId; + USHORT UsagePage; + }s; + ULONG Extended; + }u; + + UCHAR IsExtended; +}USAGE_VALUE, *PUSAGE_VALUE; + + +typedef struct +{ + PUSAGE_VALUE UsageStack; + ULONG UsageStackUsed; + ULONG UsageStackAllocated; + + USAGE_VALUE UsageMinimum; + USAGE_VALUE UsageMaximum; + + UCHAR UsageMinimumSet; + UCHAR UsageMaximumSet; + + ULONG DesignatorIndex; + UCHAR DesignatorIndexSet; + + ULONG DesignatorMinimum; + ULONG DesignatorMaximum; + + UCHAR StringIndex; + UCHAR StringIndexSet; + UCHAR StringMinimum; + UCHAR StringMaximum; + +}LOCAL_ITEM_STATE, *PLOCAL_ITEM_STATE; + +typedef struct +{ + ULONG ByteOffset; + UCHAR Shift; + ULONG Mask; + UCHAR BitCount; + UCHAR HasData; + UCHAR Array; + UCHAR Relative; + ULONG Minimum; + ULONG Maximum; + ULONG UsageMinimum; + ULONG UsageMaximum; + ULONG Data; + UCHAR Valid; +}HID_REPORT_ITEM, *PHID_REPORT_ITEM; + +struct _HID_REPORT; + +typedef struct __HID_COLLECTION__ +{ + UCHAR Type; + ULONG Usage; + UCHAR StringID; + UCHAR PhysicalID; + ULONG ReportCount; + ULONG NodeCount; + + struct __HID_COLLECTION__ ** Nodes; + struct __HID_COLLECTION__ * Root; + struct _HID_REPORT ** Reports; + + ULONG Offsets[1]; + +}HID_COLLECTION, *PHID_COLLECTION; + +typedef struct _HID_REPORT +{ + UCHAR Type; + UCHAR ReportID; + ULONG ReportSize; + ULONG ItemCount; + ULONG ItemAllocated; + HID_REPORT_ITEM Items[1]; +}HID_REPORT, *PHID_REPORT; + +typedef struct +{ + // + // global item state + // + GLOBAL_ITEM_STATE GlobalItemState; + + // + // local item state + // + LOCAL_ITEM_STATE LocalItemState; + + // + // root collection + // + PHID_COLLECTION RootCollection; + + // + // uses report ids + // + UCHAR UseReportIDs; + + // + // collection index + // + ULONG CollectionIndex; + +}HID_PARSER_CONTEXT, *PHID_PARSER_CONTEXT; + +#define HID_REPORT_TYPE_INPUT 0x01 +#define HID_REPORT_TYPE_OUTPUT 0x02 +#define HID_REPORT_TYPE_FEATURE 0x04 + +ULONG +HidParser_UsesReportId( + IN PVOID CollectionContext, + IN UCHAR ReportType); + +HIDPARSER_STATUS +HidParser_GetCollectionUsagePage( + IN PVOID CollectionContext, + OUT PUSHORT Usage, + OUT PUSHORT UsagePage); + +ULONG +HidParser_GetReportLength( + IN PVOID CollectionContext, + IN UCHAR ReportType); + +UCHAR +HidParser_IsReportIDUsed( + IN PHID_PARSER Parser); + +ULONG +HidParser_GetReportItemCountFromReportType( + IN PVOID CollectionContext, + IN UCHAR ReportType); + +ULONG +HidParser_GetReportItemTypeCountFromReportType( + IN PVOID CollectionContext, + IN UCHAR ReportType, + IN ULONG bData); + +ULONG +HidParser_GetMaxUsageListLengthWithReportAndPage( + IN PVOID CollectionContext, + IN UCHAR ReportType, + IN USAGE UsagePage OPTIONAL); + +HIDPARSER_STATUS +HidParser_GetSpecificValueCapsWithReport( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN UCHAR ReportType, + IN USHORT UsagePage, + IN USHORT Usage, + OUT PHIDP_VALUE_CAPS ValueCaps, + IN OUT PULONG ValueCapsLength); + + +HIDPARSER_STATUS +HidParser_GetUsagesWithReport( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN UCHAR ReportType, + IN USAGE UsagePage, + OUT USAGE *UsageList, + IN OUT PULONG UsageLength, + IN PCHAR ReportDescriptor, + IN ULONG ReportDescriptorLength); + +HIDPARSER_STATUS +HidParser_GetScaledUsageValueWithReport( + IN PHID_PARSER Parser, + IN PVOID CollectionContext, + IN UCHAR ReportType, + IN USAGE UsagePage, + IN USAGE Usage, + OUT PLONG UsageValue, + IN PCHAR ReportDescriptor, + IN ULONG ReportDescriptorLength); + +/* parser.c */ + +HIDPARSER_STATUS +HidParser_BuildContext( + IN PHID_PARSER Parser, + IN PVOID ParserContext, + IN ULONG CollectionIndex, + IN ULONG ContextSize, + OUT PVOID *CollectionContext); + +ULONG +HidParser_CalculateContextSize( + IN PHID_COLLECTION Collection); + +HIDPARSER_STATUS +HidParser_ParseReportDescriptor( + PHID_PARSER Parser, + PUCHAR Report, + ULONG ReportSize, + OUT PVOID *ParserContext); + +ULONG +HidParser_NumberOfTopCollections( + IN PVOID ParserContext); + +ULONG +HidParser_GetContextSize( + IN PHID_PARSER Parser, + IN PVOID ParserContext, + IN ULONG CollectionNumber); + + +/* context.c */ + +PHID_COLLECTION +HidParser_GetCollectionFromContext( + IN PVOID Context); + +ULONG +HidParser_GetTotalCollectionCount( + IN PVOID CollectionContext); + +HIDPARSER_STATUS +HidParser_BuildCollectionContext( + IN PHID_PARSER Parser, + IN PHID_COLLECTION RootCollection, + IN PVOID Context, + IN ULONG ContextSize); + +PHID_REPORT +HidParser_GetReportInCollection( + IN PVOID Context, + IN UCHAR ReportType); diff --git a/reactos/lib/sdk/wdmguid/wdmguid.c b/reactos/lib/sdk/wdmguid/wdmguid.c index 3ae2ac42bff..57070116391 100644 --- a/reactos/lib/sdk/wdmguid/wdmguid.c +++ b/reactos/lib/sdk/wdmguid/wdmguid.c @@ -14,5 +14,7 @@ DEFINE_GUID(GUID_DEVINTERFACE_DISK, 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); DEFINE_GUID(GUID_DEVINTERFACE_CDROM, 0x53f56308L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +DEFINE_GUID(GUID_DEVINTERFACE_PARTITION, + 0x53f5630aL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); /* EOF */ diff --git a/reactos/media/inf/CMakeLists.txt b/reactos/media/inf/CMakeLists.txt index 716ed694c93..15b5331cd60 100644 --- a/reactos/media/inf/CMakeLists.txt +++ b/reactos/media/inf/CMakeLists.txt @@ -11,6 +11,7 @@ list(APPEND INF_FILES font.inf hal.inf hdc.inf + input.inf intl.inf keyboard.inf ks.inf diff --git a/reactos/media/inf/cdrom.inf b/reactos/media/inf/cdrom.inf index eca08d293fe..4b06ab1a1c7 100644 Binary files a/reactos/media/inf/cdrom.inf and b/reactos/media/inf/cdrom.inf differ diff --git a/reactos/media/inf/input.inf b/reactos/media/inf/input.inf new file mode 100644 index 00000000000..aa0d04302f7 --- /dev/null +++ b/reactos/media/inf/input.inf @@ -0,0 +1,94 @@ +[Version] +Signature="$WINDOWS NT$" +Class=HIDClass +ClassGuid={745a17a0-74d3-11d0-b6fe-00a0c90f57da} +Provider=%ReactOS% +LayoutFile=layout.inf +DriverVer=01/20/2012,1.00 + +[DestinationDirs] +DefaultDestDir = 12 +HID_Inst.CopyFilesDLL.NT = 11 + +[HIDClass.NT.AddReg] +HKR,,,0,%HID.ClassName% +HKR,,Icon,0,"-24" +HKR,,NoInstallClass,0,1 + +[ClassInstall32.NT] +AddReg=HIDClass.NT.AddReg + +[Manufacturer] +%StdMfg% = StdMfg +%MSMice% = MSMice + +[MSMice] +%USB\VID_045E&PID_0009.DeviceDesc%=HID_Inst,USB\VID_045E&PID_0009 +%USB\VID_045E&PID_001E.DeviceDesc%=HID_Inst,USB\VID_045E&PID_001E +%USB\VID_045E&PID_0023.DeviceDesc%=HID_Inst,USB\VID_045E&PID_0023 +%USB\VID_045E&PID_0024.DeviceDesc%=HID_Inst,USB\VID_045E&PID_0024 +%USB\VID_045E&PID_0025.DeviceDesc%=HID_Inst,USB\VID_045E&PID_0025 +%USB\VID_045E&PID_0029.DeviceDesc%=HID_Inst,USB\VID_045E&PID_0029 +%USB\VID_045E&PID_0039.DeviceDesc%=HID_Inst,USB\VID_045E&PID_0039 +%USB\VID_045E&PID_0040.DeviceDesc%=HID_Inst,USB\VID_045E&PID_0040 +%USB\VID_045E&PID_0047.DeviceDesc%=HID_Inst,USB\VID_045E&PID_0047 + +[StdMfg] +%HID.DeviceDesc% = HID_Inst,GENERIC_HID_DEVICE +%HID.DeviceDesc% = HID_Inst,USB\Class_03&SubClass_01 +%HID.DeviceDesc% = HID_Inst,USB\Class_03 +%HID_DEVICE%=HID_Raw_Inst,, HID_DEVICE + +;=========================== HID Raw Device ==================== + +[HID_Raw_Inst.NT] + +[HID_Raw_Inst.Services] +AddService = , 0x00000002 + +[HID_Raw_Inst.HW] + + +;=========================== HID Device ==================== + + +[HID_Inst.NT] +Copyfiles = HID_Inst.CopyFiles.NT, HID_Inst.CopyFilesDLL.NT + +[HID_Inst.CopyFiles.NT] +hidusb.sys +hidclass.sys +hidparse.sys + +[HID_INST.CopyFilesDLL.NT] +hid.dll + +[HID_Inst.NT.Services] +AddService = hidusb, 0x00000002, HID_Service_Inst + +[HID_Service_Inst] +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\hidusb.sys +LoadOrderGroup = Extended Base + +[Strings] +ReactOS = "ReactOS Team" +HID.ClassName = "Human Interface Devices" +HID.DeviceDesc = "USB Human Interface Device" + +HID_DEVICE = "HID-compatible Device" +StdMfg = "(Standard Human Interface Devices)" +MSMice = "Microsoft" + + +USB\VID_045E&PID_0009.DeviceDesc="Microsoft IntelliMouse (USB)" +USB\VID_045E&PID_001E.DeviceDesc="Microsoft IntelliMouse Explorer (USB)" +USB\VID_045E&PID_0023.DeviceDesc="Microsoft Trackball Optical (USB)" +USB\VID_045E&PID_0024.DeviceDesc="Microsoft Trackball Explorer (USB)" +USB\VID_045E&PID_0025.DeviceDesc="Microsoft IntelliMouse mit IntelliEye (USB)" +USB\VID_045E&PID_0029.DeviceDesc="Microsoft IntelliMouse Web (USB)" +USB\VID_045E&PID_0039.DeviceDesc="Microsoft IntelliMouse Optical (USB)" +USB\VID_045E&PID_0040.DeviceDesc="Microsoft Wheel Mouse Optical (USB)" +USB\VID_045E&PID_0047.DeviceDesc="Microsoft IntelliMouse Explorer 3.0 (USB)" \ No newline at end of file diff --git a/reactos/media/inf/keyboard.inf b/reactos/media/inf/keyboard.inf index 38a2f7a0187..41f79d35a77 100644 Binary files a/reactos/media/inf/keyboard.inf and b/reactos/media/inf/keyboard.inf differ diff --git a/reactos/media/inf/msmouse.inf b/reactos/media/inf/msmouse.inf index 2d5e6fed22c..e33232cce4e 100644 Binary files a/reactos/media/inf/msmouse.inf and b/reactos/media/inf/msmouse.inf differ diff --git a/reactos/media/inf/syssetup.inf b/reactos/media/inf/syssetup.inf index f015a13c8fa..1c7cbd5df5d 100644 --- a/reactos/media/inf/syssetup.inf +++ b/reactos/media/inf/syssetup.inf @@ -11,6 +11,7 @@ display.inf fdc.inf hal.inf hdc.inf +input.inf keyboard.inf machine.inf msmouse.inf diff --git a/reactos/media/inf/syssetup.inf.tpl b/reactos/media/inf/syssetup.inf.tpl index 5ae24870a65..4b2968a9dbb 100644 --- a/reactos/media/inf/syssetup.inf.tpl +++ b/reactos/media/inf/syssetup.inf.tpl @@ -22,6 +22,7 @@ display.inf fdc.inf hal.inf hdc.inf +input.inf keyboard.inf machine.inf msmouse.inf diff --git a/reactos/media/inf/usb.inf b/reactos/media/inf/usb.inf index c2c95ab4720..02adc85a262 100644 Binary files a/reactos/media/inf/usb.inf and b/reactos/media/inf/usb.inf differ diff --git a/reactos/media/inf/usbport.inf b/reactos/media/inf/usbport.inf index 246f6f7e775..d72a801c21e 100644 Binary files a/reactos/media/inf/usbport.inf and b/reactos/media/inf/usbport.inf differ diff --git a/reactos/media/inf/usbstor.inf b/reactos/media/inf/usbstor.inf index 4555b17050d..0e330984c72 100644 Binary files a/reactos/media/inf/usbstor.inf and b/reactos/media/inf/usbstor.inf differ diff --git a/reactos/ntoskrnl/include/internal/io.h b/reactos/ntoskrnl/include/internal/io.h index 69122da4d06..7fd74e1ca00 100644 --- a/reactos/ntoskrnl/include/internal/io.h +++ b/reactos/ntoskrnl/include/internal/io.h @@ -516,7 +516,7 @@ IopAssignDeviceResources( NTSTATUS NTAPI -IopCreateResourceListFromRequirements( +IopFixupResourceListWithRequirements( IN PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList, OUT PCM_RESOURCE_LIST *ResourceList ); diff --git a/reactos/ntoskrnl/include/ntoskrnl.h b/reactos/ntoskrnl/include/ntoskrnl.h index 4af4b96fd39..9a823e4776d 100644 --- a/reactos/ntoskrnl/include/ntoskrnl.h +++ b/reactos/ntoskrnl/include/ntoskrnl.h @@ -31,6 +31,7 @@ #include #include #include +#include #undef NTHALAPI #define NTHALAPI __declspec(dllimport) #include diff --git a/reactos/ntoskrnl/io/iomgr/device.c b/reactos/ntoskrnl/io/iomgr/device.c index f5f6dd6284b..e73f989bd7c 100644 --- a/reactos/ntoskrnl/io/iomgr/device.c +++ b/reactos/ntoskrnl/io/iomgr/device.c @@ -420,10 +420,10 @@ IopUnloadDevice(IN PDEVICE_OBJECT DeviceObject) /* * Check if we have an attached device and fail if we're attached - * and still have a reference count. + * or still have a reference count. */ AttachedDeviceObject = DeviceObject->AttachedDevice; - if ((AttachedDeviceObject) && (DeviceObject->ReferenceCount)) return; + if ((AttachedDeviceObject) || (DeviceObject->ReferenceCount)) return; /* Check if we have a Security Descriptor */ if (DeviceObject->SecurityDescriptor) diff --git a/reactos/ntoskrnl/io/iomgr/deviface.c b/reactos/ntoskrnl/io/iomgr/deviface.c index 973053631d9..9b40aabb7c3 100644 --- a/reactos/ntoskrnl/io/iomgr/deviface.c +++ b/reactos/ntoskrnl/io/iomgr/deviface.c @@ -1145,7 +1145,12 @@ IoRegisterDeviceInterface(IN PDEVICE_OBJECT PhysicalDeviceObject, /* If the symbolic link already exists, return an informational success status */ if (SymLinkStatus == STATUS_OBJECT_NAME_COLLISION) + { + /* HACK: Delete the existing symbolic link and update it to the new PDO name */ + IoDeleteSymbolicLink(SymbolicLinkName); + IoCreateSymbolicLink(SymbolicLinkName, &PdoNameInfo->Name); SymLinkStatus = STATUS_OBJECT_NAME_EXISTS; + } if (!NT_SUCCESS(SymLinkStatus)) { diff --git a/reactos/ntoskrnl/io/iomgr/driver.c b/reactos/ntoskrnl/io/iomgr/driver.c index edd8940866b..f33a22f38fa 100644 --- a/reactos/ntoskrnl/io/iomgr/driver.c +++ b/reactos/ntoskrnl/io/iomgr/driver.c @@ -32,6 +32,7 @@ POBJECT_TYPE IoDriverObjectType = NULL; #define TAG_RTLREGISTRY 'vrqR' extern BOOLEAN ExpInTextModeSetup; +extern BOOLEAN PnpSystemInit; USHORT IopGroupIndex; PLIST_ENTRY IopGroupTable; @@ -516,6 +517,8 @@ IopInitializeDriverModule( /* Set the driver as initialized */ IopReadyDeviceObjects(Driver); + if (PnpSystemInit) IopReinitializeDrivers(); + return STATUS_SUCCESS; } diff --git a/reactos/ntoskrnl/io/iomgr/iorsrce.c b/reactos/ntoskrnl/io/iomgr/iorsrce.c index db227cf5c61..978985b5fe0 100644 --- a/reactos/ntoskrnl/io/iomgr/iorsrce.c +++ b/reactos/ntoskrnl/io/iomgr/iorsrce.c @@ -930,9 +930,10 @@ IoAssignResources(PUNICODE_STRING RegistryPath, NTSTATUS Status; DPRINT1("IoAssignResources is halfplemented!\n"); - - Status = IopCreateResourceListFromRequirements(RequestedResources, - AllocatedResources); + + *AllocatedResources = NULL; + Status = IopFixupResourceListWithRequirements(RequestedResources, + AllocatedResources); if (!NT_SUCCESS(Status)) { if (Status == STATUS_CONFLICTING_ADDRESSES) diff --git a/reactos/ntoskrnl/io/iomgr/volume.c b/reactos/ntoskrnl/io/iomgr/volume.c index 9d7030e2adb..d3363858e89 100644 --- a/reactos/ntoskrnl/io/iomgr/volume.c +++ b/reactos/ntoskrnl/io/iomgr/volume.c @@ -1269,15 +1269,165 @@ IoSetSystemPartition(IN PUNICODE_STRING VolumeNameString) } /* - * @unimplemented + * @implemented */ NTSTATUS NTAPI IoVolumeDeviceToDosName(IN PVOID VolumeDeviceObject, OUT PUNICODE_STRING DosName) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + PIRP Irp; + ULONG Length; + KEVENT Event; + NTSTATUS Status; + PFILE_OBJECT FileObject; + PDEVICE_OBJECT DeviceObject; + IO_STATUS_BLOCK IoStatusBlock; + UNICODE_STRING MountMgrDevice; + MOUNTMGR_VOLUME_PATHS VolumePath; + PMOUNTMGR_VOLUME_PATHS VolumePathPtr; + /* + * This variable with be required to query device name. + * It's based on MOUNTDEV_NAME (mountmgr.h). + * Doing it that way will prevent dyn memory allocation. + * Device name won't be longer. + */ + struct + { + USHORT NameLength; + WCHAR DeviceName[256]; + } DeviceName; + + PAGED_CODE(); + + /* First step, getting device name */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, + VolumeDeviceObject, NULL, 0, + &DeviceName, sizeof(DeviceName), + FALSE, &Event, &IoStatusBlock); + if (!Irp) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = IoCallDriver(VolumeDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Now that we have the device name, we can query the MountMgr + * So, get its device object first. + */ + RtlInitUnicodeString(&MountMgrDevice, MOUNTMGR_DEVICE_NAME); + Status = IoGetDeviceObjectPointer(&MountMgrDevice, FILE_READ_ATTRIBUTES, + &FileObject, &DeviceObject); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Then, use the proper IOCTL to query the DOS name */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH, + DeviceObject, &DeviceName, sizeof(DeviceName), + &VolumePath, sizeof(VolumePath), + FALSE, &Event, &IoStatusBlock); + if (!Irp) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto DereferenceFO; + } + + Status = IoCallDriver(VolumeDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + /* Only tolerated failure here is buffer too small, which is + * expected. + */ + if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) + { + goto DereferenceFO; + } + + /* Compute needed size to store DOS name. + * Even if MOUNTMGR_VOLUME_PATHS allows bigger + * name lengths than MAXUSHORT, we can't use + * them, because we have to return this in an UNICODE_STRING + * that stores length on USHORT. + */ + Length = VolumePath.MultiSzLength + sizeof(VolumePath); + if (Length > MAXUSHORT) + { + Status = STATUS_INVALID_BUFFER_SIZE; + goto DereferenceFO; + } + + /* Reallocate memory, even in case of success, because + * that's the buffer that will be returned to caller + */ + VolumePathPtr = ExAllocatePoolWithTag(PagedPool, Length, 'D2d '); + if (!VolumePathPtr) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto DereferenceFO; + } + + /* Requery DOS path with proper size */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH, + DeviceObject, &DeviceName, sizeof(DeviceName), + VolumePathPtr, Length, + FALSE, &Event, &IoStatusBlock); + if (!Irp) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto ReleaseMemory; + } + + Status = IoCallDriver(VolumeDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + if (!NT_SUCCESS(Status)) + { + goto ReleaseMemory; + } + + /* Set output string */ + DosName->Length = VolumePathPtr->MultiSzLength; + DosName->MaximumLength = VolumePathPtr->MultiSzLength + sizeof(UNICODE_NULL); + /* Our MOUNTMGR_VOLUME_PATHS will be used as output buffer */ + DosName->Buffer = (PWSTR)VolumePathPtr; + /* Move name at the begin, RtlMoveMemory is OK with overlapping */ + RtlMoveMemory(DosName->Buffer, VolumePathPtr->MultiSz, VolumePathPtr->MultiSzLength); + DosName->Buffer[DosName->Length / sizeof(WCHAR)] = UNICODE_NULL; + + /* DON'T release buffer, just dereference FO, and return success */ + Status = STATUS_SUCCESS; + goto DereferenceFO; + +ReleaseMemory: + ExFreePoolWithTag(VolumePathPtr, 'D2d '); + +DereferenceFO: + ObDereferenceObject(FileObject); + + return Status; } /* EOF */ diff --git a/reactos/ntoskrnl/io/pnpmgr/plugplay.c b/reactos/ntoskrnl/io/pnpmgr/plugplay.c index bd52f4d4a87..b3ead8bb295 100644 --- a/reactos/ntoskrnl/io/pnpmgr/plugplay.c +++ b/reactos/ntoskrnl/io/pnpmgr/plugplay.c @@ -549,26 +549,42 @@ IopResetDevice(PPLUGPLAY_CONTROL_RESET_DEVICE_DATA ResetDeviceData) if (DeviceObject == NULL) return STATUS_NO_SUCH_DEVICE; + /* Get the device node */ DeviceNode = IopGetDeviceNode(DeviceObject); -#if 0 - /* Remove the device */ - if (DeviceNode->Flags & DNF_ENUMERATED) + /* Check if an FDO has been added to the stack */ + if (DeviceNode->Flags & DNF_ADDED) { + /* Remove the device node */ Status = IopRemoveDevice(DeviceNode); if (!NT_SUCCESS(Status)) { DPRINT1("WARNING: Ignoring failed IopRemoveDevice() for %wZ (likely a driver bug)\n", &DeviceNode->InstancePath); } + + /* Invalidate device relations for the parent to reenumerate the device */ + Status = IoSynchronousInvalidateDeviceRelations(DeviceNode->Parent->PhysicalDeviceObject, BusRelations); + + DPRINT1("Reset PDO with FDO present: 0x%x\n", Status); } -#endif + else + { + /* FIXME: We might clear some important flags */ + ASSERT(DeviceNode->Flags & DNF_ENUMERATED); + ASSERT(DeviceNode->Flags & DNF_PROCESSED); + DeviceNode->Flags = DNF_ENUMERATED | DNF_PROCESSED; - /* Reenumerate the device and its children */ - DeviceNode->Flags &= ~DNF_DISABLED; - Status = IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent); + /* Load service data from the registry */ + Status = IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent); - if (NT_SUCCESS(Status)) - Status = IopActionInitChildServices(DeviceNode, DeviceNode->Parent); + if (NT_SUCCESS(Status)) + { + /* Start the service and begin PnP initialization of the device again */ + Status = IopActionInitChildServices(DeviceNode, DeviceNode->Parent); + } + + DPRINT1("Reset PDO with no FDO present: 0x%x\n", Status); + } ObDereferenceObject(DeviceObject); diff --git a/reactos/ntoskrnl/io/pnpmgr/pnpmgr.c b/reactos/ntoskrnl/io/pnpmgr/pnpmgr.c index c4311ffe1a8..75188f98ab5 100644 --- a/reactos/ntoskrnl/io/pnpmgr/pnpmgr.c +++ b/reactos/ntoskrnl/io/pnpmgr/pnpmgr.c @@ -48,7 +48,7 @@ VOID IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject); NTSTATUS -IopPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject); +IopPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject, BOOLEAN Force); PDEVICE_NODE FASTCALL @@ -57,6 +57,367 @@ IopGetDeviceNode(PDEVICE_OBJECT DeviceObject) return ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode; } +VOID +IopFixupDeviceId(PWCHAR String) +{ + ULONG Length = wcslen(String), i; + + for (i = 0; i < Length; i++) + { + if (String[i] == L'\\') + String[i] = L'#'; + } +} + +VOID +NTAPI +IopInstallCriticalDevice(PDEVICE_NODE DeviceNode) +{ + NTSTATUS Status; + HANDLE CriticalDeviceKey, InstanceKey; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING CriticalDeviceKeyU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\CriticalDeviceDatabase"); + UNICODE_STRING CompatibleIdU = RTL_CONSTANT_STRING(L"CompatibleIDs"); + UNICODE_STRING HardwareIdU = RTL_CONSTANT_STRING(L"HardwareID"); + UNICODE_STRING ServiceU = RTL_CONSTANT_STRING(L"Service"); + UNICODE_STRING ClassGuidU = RTL_CONSTANT_STRING(L"ClassGUID"); + PKEY_VALUE_PARTIAL_INFORMATION PartialInfo; + ULONG HidLength = 0, CidLength = 0, BufferLength; + PWCHAR IdBuffer, OriginalIdBuffer; + + /* Open the device instance key */ + Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, 0, &InstanceKey); + if (Status != STATUS_SUCCESS) + return; + + Status = ZwQueryValueKey(InstanceKey, + &HardwareIdU, + KeyValuePartialInformation, + NULL, + 0, + &HidLength); + if (Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL) + { + ZwClose(InstanceKey); + return; + } + + Status = ZwQueryValueKey(InstanceKey, + &CompatibleIdU, + KeyValuePartialInformation, + NULL, + 0, + &CidLength); + if (Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL) + { + CidLength = 0; + } + + BufferLength = HidLength + CidLength; + BufferLength -= (((CidLength != 0) ? 2 : 1) * FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); + + /* Allocate a buffer to hold data from both */ + OriginalIdBuffer = IdBuffer = ExAllocatePool(PagedPool, BufferLength); + if (!IdBuffer) + { + ZwClose(InstanceKey); + return; + } + + /* Compute the buffer size */ + if (HidLength > CidLength) + BufferLength = HidLength; + else + BufferLength = CidLength; + + PartialInfo = ExAllocatePool(PagedPool, BufferLength); + if (!PartialInfo) + { + ZwClose(InstanceKey); + ExFreePool(OriginalIdBuffer); + return; + } + + Status = ZwQueryValueKey(InstanceKey, + &HardwareIdU, + KeyValuePartialInformation, + PartialInfo, + HidLength, + &HidLength); + if (Status != STATUS_SUCCESS) + { + ExFreePool(PartialInfo); + ExFreePool(OriginalIdBuffer); + ZwClose(InstanceKey); + return; + } + + /* Copy in HID info first (without 2nd terminating NULL if CID is present) */ + HidLength = PartialInfo->DataLength - ((CidLength != 0) ? sizeof(WCHAR) : 0); + RtlCopyMemory(IdBuffer, PartialInfo->Data, HidLength); + + if (CidLength != 0) + { + Status = ZwQueryValueKey(InstanceKey, + &CompatibleIdU, + KeyValuePartialInformation, + PartialInfo, + CidLength, + &CidLength); + if (Status != STATUS_SUCCESS) + { + ExFreePool(PartialInfo); + ExFreePool(OriginalIdBuffer); + ZwClose(InstanceKey); + return; + } + + /* Copy CID next */ + CidLength = PartialInfo->DataLength; + RtlCopyMemory(((PUCHAR)IdBuffer) + HidLength, PartialInfo->Data, CidLength); + } + + /* Free our temp buffer */ + ExFreePool(PartialInfo); + + InitializeObjectAttributes(&ObjectAttributes, + &CriticalDeviceKeyU, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = ZwOpenKey(&CriticalDeviceKey, + KEY_ENUMERATE_SUB_KEYS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + /* The critical device database doesn't exist because + * we're probably in 1st stage setup, but it's ok */ + ExFreePool(OriginalIdBuffer); + ZwClose(InstanceKey); + return; + } + + while (*IdBuffer) + { + ULONG StringLength = (ULONG)wcslen(IdBuffer) + 1, Index; + + IopFixupDeviceId(IdBuffer); + + /* Look through all subkeys for a match */ + for (Index = 0; TRUE; Index++) + { + ULONG NeededLength; + PKEY_BASIC_INFORMATION BasicInfo; + + Status = ZwEnumerateKey(CriticalDeviceKey, + Index, + KeyBasicInformation, + NULL, + 0, + &NeededLength); + if (Status == STATUS_NO_MORE_ENTRIES) + break; + else if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL) + { + UNICODE_STRING ChildIdNameU, RegKeyNameU; + + BasicInfo = ExAllocatePool(PagedPool, NeededLength); + if (!BasicInfo) + { + /* No memory */ + ExFreePool(OriginalIdBuffer); + ZwClose(CriticalDeviceKey); + ZwClose(InstanceKey); + return; + } + + Status = ZwEnumerateKey(CriticalDeviceKey, + Index, + KeyBasicInformation, + BasicInfo, + NeededLength, + &NeededLength); + if (Status != STATUS_SUCCESS) + { + /* This shouldn't happen */ + ExFreePool(BasicInfo); + continue; + } + + ChildIdNameU.Buffer = IdBuffer; + ChildIdNameU.MaximumLength = ChildIdNameU.Length = (StringLength - 1) * sizeof(WCHAR); + RegKeyNameU.Buffer = BasicInfo->Name; + RegKeyNameU.MaximumLength = RegKeyNameU.Length = BasicInfo->NameLength; + + if (RtlEqualUnicodeString(&ChildIdNameU, &RegKeyNameU, TRUE)) + { + HANDLE ChildKeyHandle; + + InitializeObjectAttributes(&ObjectAttributes, + &ChildIdNameU, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + CriticalDeviceKey, + NULL); + + Status = ZwOpenKey(&ChildKeyHandle, + KEY_QUERY_VALUE, + &ObjectAttributes); + if (Status != STATUS_SUCCESS) + { + ExFreePool(BasicInfo); + continue; + } + + /* Check if there's already a driver installed */ + Status = ZwQueryValueKey(InstanceKey, + &ClassGuidU, + KeyValuePartialInformation, + NULL, + 0, + &NeededLength); + if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL) + { + ExFreePool(BasicInfo); + continue; + } + + Status = ZwQueryValueKey(ChildKeyHandle, + &ClassGuidU, + KeyValuePartialInformation, + NULL, + 0, + &NeededLength); + if (Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL) + { + ExFreePool(BasicInfo); + continue; + } + + PartialInfo = ExAllocatePool(PagedPool, NeededLength); + if (!PartialInfo) + { + ExFreePool(OriginalIdBuffer); + ExFreePool(BasicInfo); + ZwClose(InstanceKey); + ZwClose(ChildKeyHandle); + ZwClose(CriticalDeviceKey); + return; + } + + /* Read ClassGUID entry in the CDDB */ + Status = ZwQueryValueKey(ChildKeyHandle, + &ClassGuidU, + KeyValuePartialInformation, + PartialInfo, + NeededLength, + &NeededLength); + if (Status != STATUS_SUCCESS) + { + ExFreePool(BasicInfo); + continue; + } + + /* Write it to the ENUM key */ + Status = ZwSetValueKey(InstanceKey, + &ClassGuidU, + 0, + REG_SZ, + PartialInfo->Data, + PartialInfo->DataLength); + if (Status != STATUS_SUCCESS) + { + ExFreePool(BasicInfo); + ExFreePool(PartialInfo); + ZwClose(ChildKeyHandle); + continue; + } + + Status = ZwQueryValueKey(ChildKeyHandle, + &ServiceU, + KeyValuePartialInformation, + NULL, + 0, + &NeededLength); + if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL) + { + ExFreePool(PartialInfo); + PartialInfo = ExAllocatePool(PagedPool, NeededLength); + if (!PartialInfo) + { + ExFreePool(OriginalIdBuffer); + ExFreePool(BasicInfo); + ZwClose(InstanceKey); + ZwClose(ChildKeyHandle); + ZwClose(CriticalDeviceKey); + return; + } + + /* Read the service entry from the CDDB */ + Status = ZwQueryValueKey(ChildKeyHandle, + &ServiceU, + KeyValuePartialInformation, + PartialInfo, + NeededLength, + &NeededLength); + if (Status != STATUS_SUCCESS) + { + ExFreePool(BasicInfo); + ExFreePool(PartialInfo); + ZwClose(ChildKeyHandle); + continue; + } + + /* Write it to the ENUM key */ + Status = ZwSetValueKey(InstanceKey, + &ServiceU, + 0, + REG_SZ, + PartialInfo->Data, + PartialInfo->DataLength); + if (Status != STATUS_SUCCESS) + { + ExFreePool(BasicInfo); + ExFreePool(PartialInfo); + ZwClose(ChildKeyHandle); + continue; + } + + DPRINT1("Installed service '%S' for critical device '%wZ'\n", PartialInfo->Data, &ChildIdNameU); + } + else + { + DPRINT1("Installed NULL service for critical device '%wZ'\n", &ChildIdNameU); + } + + ExFreePool(OriginalIdBuffer); + ExFreePool(PartialInfo); + ExFreePool(BasicInfo); + ZwClose(InstanceKey); + ZwClose(ChildKeyHandle); + ZwClose(CriticalDeviceKey); + + /* That's it */ + return; + } + + ExFreePool(BasicInfo); + } + else + { + /* Umm, not sure what happened here */ + continue; + } + } + + /* Advance to the next ID */ + IdBuffer += StringLength; + } + + ExFreePool(OriginalIdBuffer); + ZwClose(InstanceKey); + ZwClose(CriticalDeviceKey); +} + NTSTATUS FASTCALL IopInitializeDevice(PDEVICE_NODE DeviceNode, @@ -102,16 +463,7 @@ IopInitializeDevice(PDEVICE_NODE DeviceNode, return Status; } - /* Check if driver added a FDO above the PDO */ Fdo = IoGetAttachedDeviceReference(DeviceNode->PhysicalDeviceObject); - if (Fdo == DeviceNode->PhysicalDeviceObject) - { - /* FIXME: What do we do? Unload the driver or just disable the device? */ - DPRINT1("An FDO was not attached\n"); - ObDereferenceObject(Fdo); - IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED); - return STATUS_UNSUCCESSFUL; - } /* Check if we have a ACPI device (needed for power management) */ if (Fdo->DeviceType == FILE_DEVICE_ACPI) @@ -597,9 +949,9 @@ IopGetBusTypeGuidIndex(LPGUID BusTypeGuid) NewList = ExAllocatePool(PagedPool, NewSize); if (!NewList) { - /* Fail */ - ExFreePool(PnpBusTypeGuidList); - goto Quickie; + /* Fail */ + ExFreePool(PnpBusTypeGuidList); + goto Quickie; } /* Now copy them, decrease the size too */ @@ -629,7 +981,7 @@ Quickie: /* * DESCRIPTION - * Creates a device node + * Creates a device node * * ARGUMENTS * ParentNode = Pointer to parent device node @@ -639,7 +991,7 @@ Quickie: * DeviceNode = Pointer to storage for created device node * * RETURN VALUE - * Status + * Status */ NTSTATUS IopCreateDeviceNode(PDEVICE_NODE ParentNode, @@ -1378,11 +1730,11 @@ IopQueryHardwareIds(PDEVICE_NODE DeviceNode, RtlInitUnicodeString(&ValueName, L"HardwareID"); Status = ZwSetValueKey(InstanceKey, - &ValueName, - 0, - REG_MULTI_SZ, - (PVOID)IoStatusBlock.Information, - (TotalLength + 1) * sizeof(WCHAR)); + &ValueName, + 0, + REG_MULTI_SZ, + (PVOID)IoStatusBlock.Information, + (TotalLength + 1) * sizeof(WCHAR)); if (!NT_SUCCESS(Status)) { DPRINT1("ZwSetValueKey() failed (Status %lx)\n", Status); @@ -1819,6 +2171,9 @@ IopHandleDeviceRemoval( ULONG i; BOOLEAN Found; + if (DeviceNode == IopRootDeviceNode) + return; + while (Child != NULL) { NextChild = Child->Sibling; @@ -1833,14 +2188,19 @@ IopHandleDeviceRemoval( } } - if (!Found) + if (!Found && !(Child->Flags & DNF_WILL_BE_REMOVED)) { + /* Send removal IRPs to all of its children */ + IopPrepareDeviceForRemoval(Child->PhysicalDeviceObject, TRUE); + + /* Send the surprise removal IRP */ IopSendSurpriseRemoval(Child->PhysicalDeviceObject); /* Tell the user-mode PnP manager that a device was removed */ IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL, &Child->InstancePath); + /* Send the remove device IRP */ IopSendRemoveDevice(Child->PhysicalDeviceObject); } @@ -1873,6 +2233,8 @@ IopEnumerateDevice( &DeviceNode->InstancePath); } + DeviceNode->Flags &= ~DNF_NEED_TO_ENUM; + DPRINT("Sending IRP_MN_QUERY_DEVICE_RELATIONS to device stack\n"); Stack.Parameters.QueryDeviceRelations.Type = BusRelations; @@ -2057,6 +2419,9 @@ IopActionConfigureChildServices(PDEVICE_NODE DeviceNode, WCHAR RegKeyBuffer[MAX_PATH]; UNICODE_STRING RegKey; + /* Install the service for this if it's in the CDDB */ + IopInstallCriticalDevice(DeviceNode); + RegKey.Length = 0; RegKey.MaximumLength = sizeof(RegKeyBuffer); RegKey.Buffer = RegKeyBuffer; @@ -2169,16 +2534,12 @@ IopActionInitChildServices(PDEVICE_NODE DeviceNode, } /* - * Make sure this device node is a direct child of the parent device node - * that is given as an argument + * We don't want to check for a direct child because + * this function is called during boot to reinitialize + * devices with drivers that couldn't load yet due to + * stage 0 limitations (ie can't load from disk yet). */ - if (DeviceNode->Parent != ParentDeviceNode) - { - DPRINT("Skipping 2+ level child\n"); - return STATUS_SUCCESS; - } - if (!(DeviceNode->Flags & DNF_PROCESSED)) { DPRINT1("Child not ready to be added\n"); @@ -3908,7 +4269,7 @@ IoOpenDeviceRegistryKey(IN PDEVICE_OBJECT DeviceObject, static NTSTATUS -IopQueryRemoveChildDevices(PDEVICE_NODE ParentDeviceNode) +IopQueryRemoveChildDevices(PDEVICE_NODE ParentDeviceNode, BOOLEAN Force) { PDEVICE_NODE ChildDeviceNode, NextDeviceNode, FailedRemoveDevice; NTSTATUS Status; @@ -3921,7 +4282,7 @@ IopQueryRemoveChildDevices(PDEVICE_NODE ParentDeviceNode) NextDeviceNode = ChildDeviceNode->Sibling; KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql); - Status = IopPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject); + Status = IopPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject, Force); if (!NT_SUCCESS(Status)) { FailedRemoveDevice = ChildDeviceNode; @@ -4007,7 +4368,7 @@ IopCancelRemoveChildDevices(PDEVICE_NODE ParentDeviceNode) static NTSTATUS -IopQueryRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations) +IopQueryRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations, BOOLEAN Force) { /* This function DOES NOT dereference the device objects on SUCCESS * but it DOES dereference device objects on FAILURE */ @@ -4017,7 +4378,7 @@ IopQueryRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations) for (i = 0; i < DeviceRelations->Count; i++) { - Status = IopPrepareDeviceForRemoval(DeviceRelations->Objects[i]); + Status = IopPrepareDeviceForRemoval(DeviceRelations->Objects[i], Force); if (!NT_SUCCESS(Status)) { j = i; @@ -4113,7 +4474,7 @@ IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject) } NTSTATUS -IopPrepareDeviceForRemoval(IN PDEVICE_OBJECT DeviceObject) +IopPrepareDeviceForRemoval(IN PDEVICE_OBJECT DeviceObject, BOOLEAN Force) { PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject); IO_STACK_LOCATION Stack; @@ -4121,13 +4482,13 @@ IopPrepareDeviceForRemoval(IN PDEVICE_OBJECT DeviceObject) PDEVICE_RELATIONS DeviceRelations; NTSTATUS Status; - if (DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE) + if ((DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE) && !Force) { DPRINT1("Removal not allowed for %wZ\n", &DeviceNode->InstancePath); return STATUS_UNSUCCESSFUL; } - if (IopQueryRemoveDevice(DeviceObject) != STATUS_SUCCESS) + if (!Force && IopQueryRemoveDevice(DeviceObject) != STATUS_SUCCESS) { DPRINT1("Removal vetoed by failing the query remove request\n"); @@ -4154,19 +4515,20 @@ IopPrepareDeviceForRemoval(IN PDEVICE_OBJECT DeviceObject) if (DeviceRelations) { - Status = IopQueryRemoveDeviceRelations(DeviceRelations); + Status = IopQueryRemoveDeviceRelations(DeviceRelations, Force); if (!NT_SUCCESS(Status)) return Status; } - - Status = IopQueryRemoveChildDevices(DeviceNode); + + Status = IopQueryRemoveChildDevices(DeviceNode, Force); if (!NT_SUCCESS(Status)) { if (DeviceRelations) IopCancelRemoveDeviceRelations(DeviceRelations); return Status; } - + + DeviceNode->Flags |= DNF_WILL_BE_REMOVED; if (DeviceRelations) IopSendRemoveDeviceRelations(DeviceRelations); IopSendRemoveChildDevices(DeviceNode); @@ -4181,13 +4543,12 @@ IopRemoveDevice(PDEVICE_NODE DeviceNode) DPRINT("Removing device: %wZ\n", &DeviceNode->InstancePath); - Status = IopPrepareDeviceForRemoval(DeviceNode->PhysicalDeviceObject); + Status = IopPrepareDeviceForRemoval(DeviceNode->PhysicalDeviceObject, FALSE); if (NT_SUCCESS(Status)) { IopSendRemoveDevice(DeviceNode->PhysicalDeviceObject); IopQueueTargetDeviceEvent(&GUID_DEVICE_SAFE_REMOVAL, &DeviceNode->InstancePath); - DeviceNode->Flags |= DNF_WILL_BE_REMOVED; return STATUS_SUCCESS; } @@ -4234,12 +4595,12 @@ IoRequestDeviceEject(IN PDEVICE_OBJECT PhysicalDeviceObject) if (DeviceRelations) { - Status = IopQueryRemoveDeviceRelations(DeviceRelations); + Status = IopQueryRemoveDeviceRelations(DeviceRelations, FALSE); if (!NT_SUCCESS(Status)) goto cleanup; } - Status = IopQueryRemoveChildDevices(DeviceNode); + Status = IopQueryRemoveChildDevices(DeviceNode, FALSE); if (!NT_SUCCESS(Status)) { if (DeviceRelations) @@ -4247,7 +4608,7 @@ IoRequestDeviceEject(IN PDEVICE_OBJECT PhysicalDeviceObject) goto cleanup; } - if (IopPrepareDeviceForRemoval(PhysicalDeviceObject) != STATUS_SUCCESS) + if (IopPrepareDeviceForRemoval(PhysicalDeviceObject, FALSE) != STATUS_SUCCESS) { if (DeviceRelations) IopCancelRemoveDeviceRelations(DeviceRelations); diff --git a/reactos/ntoskrnl/io/pnpmgr/pnpres.c b/reactos/ntoskrnl/io/pnpmgr/pnpres.c index 63150247839..73272f5e771 100644 --- a/reactos/ntoskrnl/io/pnpmgr/pnpres.c +++ b/reactos/ntoskrnl/io/pnpmgr/pnpres.c @@ -47,7 +47,7 @@ IopFindBusNumberResource( ASSERT(IoDesc->Type == CmResourceTypeBusNumber); for (Start = IoDesc->u.BusNumber.MinBusNumber; - Start <= IoDesc->u.BusNumber.MaxBusNumber; + Start <= IoDesc->u.BusNumber.MaxBusNumber - IoDesc->u.BusNumber.Length + 1; Start++) { CmDesc->u.BusNumber.Length = IoDesc->u.BusNumber.Length; @@ -59,6 +59,7 @@ IopFindBusNumberResource( } else { + DPRINT1("Satisfying bus number requirement with 0x%x (length: 0x%x)\n", Start, CmDesc->u.BusNumber.Length); return TRUE; } } @@ -82,7 +83,7 @@ IopFindMemoryResource( if (IoDesc->u.Memory.Alignment == 0) IoDesc->u.Memory.Alignment = 1; for (Start = IoDesc->u.Memory.MinimumAddress.QuadPart; - Start <= IoDesc->u.Memory.MaximumAddress.QuadPart; + Start <= IoDesc->u.Memory.MaximumAddress.QuadPart - IoDesc->u.Memory.Length + 1; Start += IoDesc->u.Memory.Alignment) { CmDesc->u.Memory.Length = IoDesc->u.Memory.Length; @@ -95,6 +96,7 @@ IopFindMemoryResource( } else { + DPRINT1("Satisfying memory requirement with 0x%I64x (length: 0x%x)\n", Start, CmDesc->u.Memory.Length); return TRUE; } } @@ -118,7 +120,7 @@ IopFindPortResource( if (IoDesc->u.Port.Alignment == 0) IoDesc->u.Port.Alignment = 1; for (Start = IoDesc->u.Port.MinimumAddress.QuadPart; - Start <= IoDesc->u.Port.MaximumAddress.QuadPart; + Start <= IoDesc->u.Port.MaximumAddress.QuadPart - IoDesc->u.Port.Length + 1; Start += IoDesc->u.Port.Alignment) { CmDesc->u.Port.Length = IoDesc->u.Port.Length; @@ -130,6 +132,7 @@ IopFindPortResource( } else { + DPRINT1("Satisfying port requirement with 0x%I64x (length: 0x%x)\n", Start, CmDesc->u.Port.Length); return TRUE; } } @@ -156,7 +159,10 @@ IopFindDmaResource( CmDesc->u.Dma.Port = 0; if (!IopCheckDescriptorForConflict(CmDesc, NULL)) + { + DPRINT1("Satisfying DMA requirement with channel 0x%x\n", Channel); return TRUE; + } } return FALSE; @@ -182,163 +188,298 @@ IopFindInterruptResource( CmDesc->u.Interrupt.Affinity = (KAFFINITY)-1; if (!IopCheckDescriptorForConflict(CmDesc, NULL)) + { + DPRINT1("Satisfying interrupt requirement with IRQ 0x%x\n", Vector); return TRUE; + } } return FALSE; } - NTSTATUS NTAPI -IopCreateResourceListFromRequirements( +IopFixupResourceListWithRequirements( IN PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList, OUT PCM_RESOURCE_LIST *ResourceList) { - ULONG i, ii, Size; - PCM_PARTIAL_RESOURCE_DESCRIPTOR ResDesc; + ULONG i; + for (i = 0; i < RequirementsList->AlternativeLists; i++) + { + ULONG ii; + PIO_RESOURCE_LIST ResList = &RequirementsList->List[i]; + BOOLEAN AlternateRequired = FALSE; - Size = FIELD_OFFSET(CM_RESOURCE_LIST, List); - for (i = 0; i < RequirementsList->AlternativeLists; i++) - { - PIO_RESOURCE_LIST ResList = &RequirementsList->List[i]; - Size += FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList.PartialDescriptors) - + ResList->Count * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR); - } + for (ii = 0; ii < ResList->Count; ii++) + { + ULONG iii; + PCM_PARTIAL_RESOURCE_LIST PartialList = (*ResourceList) ? &(*ResourceList)->List[0].PartialResourceList : NULL; + PIO_RESOURCE_DESCRIPTOR IoDesc = &ResList->Descriptors[ii]; + BOOLEAN Matched = FALSE; + + /* Skip alternates if we don't need one */ + if (!AlternateRequired && (IoDesc->Option & IO_RESOURCE_ALTERNATIVE)) + { + DPRINT("Skipping unneeded alternate\n"); + continue; + } - *ResourceList = ExAllocatePool(PagedPool, Size); - if (!*ResourceList) - return STATUS_INSUFFICIENT_RESOURCES; + /* Check if we couldn't satsify a requirement or its alternates */ + if (AlternateRequired && !(IoDesc->Option & IO_RESOURCE_ALTERNATIVE)) + { + DPRINT1("Unable to satisfy preferred resource or alternates\n"); - (*ResourceList)->Count = 1; - (*ResourceList)->List[0].BusNumber = RequirementsList->BusNumber; - (*ResourceList)->List[0].InterfaceType = RequirementsList->InterfaceType; - (*ResourceList)->List[0].PartialResourceList.Version = 1; - (*ResourceList)->List[0].PartialResourceList.Revision = 1; - (*ResourceList)->List[0].PartialResourceList.Count = 0; + if (*ResourceList) + { + ExFreePool(*ResourceList); + *ResourceList = NULL; + } + return STATUS_CONFLICTING_ADDRESSES; + } - ResDesc = &(*ResourceList)->List[0].PartialResourceList.PartialDescriptors[0]; + for (iii = 0; PartialList && iii < PartialList->Count && !Matched; iii++) + { + PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDesc = &PartialList->PartialDescriptors[iii]; - for (i = 0; i < RequirementsList->AlternativeLists; i++) - { - PIO_RESOURCE_LIST ResList = &RequirementsList->List[i]; - for (ii = 0; ii < ResList->Count; ii++) - { - PIO_RESOURCE_DESCRIPTOR ReqDesc = &ResList->Descriptors[ii]; - BOOLEAN FoundResource = TRUE; + /* First check types */ + if (IoDesc->Type != CmDesc->Type) + continue; - /* FIXME: Handle alternate ranges */ - if (ReqDesc->Option == IO_RESOURCE_ALTERNATIVE) - continue; + switch (IoDesc->Type) + { + case CmResourceTypeInterrupt: + /* Make sure it satisfies our vector range */ + if (CmDesc->u.Interrupt.Vector >= IoDesc->u.Interrupt.MinimumVector && + CmDesc->u.Interrupt.Vector <= IoDesc->u.Interrupt.MaximumVector) + { + /* Found it */ + Matched = TRUE; + } + else + { + DPRINT("Interrupt - Not a match! 0x%x not inside 0x%x to 0x%x\n", + CmDesc->u.Interrupt.Vector, + IoDesc->u.Interrupt.MinimumVector, + IoDesc->u.Interrupt.MaximumVector); + } + break; - ResDesc->Type = ReqDesc->Type; - ResDesc->Flags = ReqDesc->Flags; - ResDesc->ShareDisposition = ReqDesc->ShareDisposition; + case CmResourceTypeMemory: + case CmResourceTypePort: + /* Make sure the length matches and it satisfies our address range */ + if (CmDesc->u.Memory.Length == IoDesc->u.Memory.Length && + CmDesc->u.Memory.Start.QuadPart >= IoDesc->u.Memory.MinimumAddress.QuadPart && + CmDesc->u.Memory.Start.QuadPart + CmDesc->u.Memory.Length - 1 <= IoDesc->u.Memory.MaximumAddress.QuadPart) + { + /* Found it */ + Matched = TRUE; + } + else + { + DPRINT("Memory/Port - Not a match! 0x%I64x with length 0x%x not inside 0x%I64x to 0x%I64x with length 0x%x\n", + CmDesc->u.Memory.Start.QuadPart, + CmDesc->u.Memory.Length, + IoDesc->u.Memory.MinimumAddress.QuadPart, + IoDesc->u.Memory.MaximumAddress.QuadPart, + IoDesc->u.Memory.Length); + } + break; - switch (ReqDesc->Type) - { - case CmResourceTypeInterrupt: - if (!IopFindInterruptResource(ReqDesc, ResDesc)) - { - DPRINT1("Failed to find an available interrupt resource (0x%x to 0x%x)\n", - ReqDesc->u.Interrupt.MinimumVector, ReqDesc->u.Interrupt.MaximumVector); + case CmResourceTypeBusNumber: + /* Make sure the length matches and it satisfies our bus number range */ + if (CmDesc->u.BusNumber.Length == IoDesc->u.BusNumber.Length && + CmDesc->u.BusNumber.Start >= IoDesc->u.BusNumber.MinBusNumber && + CmDesc->u.BusNumber.Start + CmDesc->u.BusNumber.Length - 1 <= IoDesc->u.BusNumber.MaxBusNumber) + { + /* Found it */ + Matched = TRUE; + } + else + { + DPRINT("Bus Number - Not a match! 0x%x with length 0x%x not inside 0x%x to 0x%x with length 0x%x\n", + CmDesc->u.BusNumber.Start, + CmDesc->u.BusNumber.Length, + IoDesc->u.BusNumber.MinBusNumber, + IoDesc->u.BusNumber.MaxBusNumber, + IoDesc->u.BusNumber.Length); + } + break; - if (ReqDesc->Option == 0) - { - ExFreePool(*ResourceList); - *ResourceList = NULL; - return STATUS_CONFLICTING_ADDRESSES; - } + case CmResourceTypeDma: + /* Make sure it fits in our channel range */ + if (CmDesc->u.Dma.Channel >= IoDesc->u.Dma.MinimumChannel && + CmDesc->u.Dma.Channel <= IoDesc->u.Dma.MaximumChannel) + { + /* Found it */ + Matched = TRUE; + } + else + { + DPRINT("DMA - Not a match! 0x%x not inside 0x%x to 0x%x\n", + CmDesc->u.Dma.Channel, + IoDesc->u.Dma.MinimumChannel, + IoDesc->u.Dma.MaximumChannel); + } + break; - FoundResource = FALSE; - } - break; + default: + /* Other stuff is fine */ + Matched = TRUE; + break; + } + } - case CmResourceTypePort: - if (!IopFindPortResource(ReqDesc, ResDesc)) - { - DPRINT1("Failed to find an available port resource (0x%I64x to 0x%I64x length: 0x%x)\n", - ReqDesc->u.Port.MinimumAddress.QuadPart, ReqDesc->u.Port.MaximumAddress.QuadPart, - ReqDesc->u.Port.Length); + /* Check if we found a matching descriptor */ + if (!Matched) + { + PCM_RESOURCE_LIST NewList; + CM_PARTIAL_RESOURCE_DESCRIPTOR NewDesc; + PCM_PARTIAL_RESOURCE_DESCRIPTOR DescPtr; + BOOLEAN FoundResource = TRUE; - if (ReqDesc->Option == 0) - { - ExFreePool(*ResourceList); - *ResourceList = NULL; - return STATUS_CONFLICTING_ADDRESSES; - } + /* Setup the new CM descriptor */ + NewDesc.Type = IoDesc->Type; + NewDesc.Flags = IoDesc->Flags; + NewDesc.ShareDisposition = IoDesc->ShareDisposition; - FoundResource = FALSE; - } - break; + /* Let'se see if we can find a resource to satisfy this */ + switch (IoDesc->Type) + { + case CmResourceTypeInterrupt: + /* Find an available interrupt */ + if (!IopFindInterruptResource(IoDesc, &NewDesc)) + { + DPRINT1("Failed to find an available interrupt resource (0x%x to 0x%x)\n", + IoDesc->u.Interrupt.MinimumVector, IoDesc->u.Interrupt.MaximumVector); - case CmResourceTypeMemory: - if (!IopFindMemoryResource(ReqDesc, ResDesc)) - { - DPRINT1("Failed to find an available memory resource (0x%I64x to 0x%I64x length: 0x%x)\n", - ReqDesc->u.Memory.MinimumAddress.QuadPart, ReqDesc->u.Memory.MaximumAddress.QuadPart, - ReqDesc->u.Memory.Length); + FoundResource = FALSE; + } + break; + + case CmResourceTypePort: + /* Find an available port range */ + if (!IopFindPortResource(IoDesc, &NewDesc)) + { + DPRINT1("Failed to find an available port resource (0x%I64x to 0x%I64x length: 0x%x)\n", + IoDesc->u.Port.MinimumAddress.QuadPart, IoDesc->u.Port.MaximumAddress.QuadPart, + IoDesc->u.Port.Length); - if (ReqDesc->Option == 0) - { - ExFreePool(*ResourceList); - *ResourceList = NULL; - return STATUS_CONFLICTING_ADDRESSES; - } + FoundResource = FALSE; + } + break; + + case CmResourceTypeMemory: + /* Find an available memory range */ + if (!IopFindMemoryResource(IoDesc, &NewDesc)) + { + DPRINT1("Failed to find an available memory resource (0x%I64x to 0x%I64x length: 0x%x)\n", + IoDesc->u.Memory.MinimumAddress.QuadPart, IoDesc->u.Memory.MaximumAddress.QuadPart, + IoDesc->u.Memory.Length); - FoundResource = FALSE; - } - break; + FoundResource = FALSE; + } + break; + + case CmResourceTypeBusNumber: + /* Find an available bus address range */ + if (!IopFindBusNumberResource(IoDesc, &NewDesc)) + { + DPRINT1("Failed to find an available bus number resource (0x%x to 0x%x length: 0x%x)\n", + IoDesc->u.BusNumber.MinBusNumber, IoDesc->u.BusNumber.MaxBusNumber, + IoDesc->u.BusNumber.Length); - case CmResourceTypeBusNumber: - if (!IopFindBusNumberResource(ReqDesc, ResDesc)) - { - DPRINT1("Failed to find an available bus number resource (0x%x to 0x%x length: 0x%x)\n", - ReqDesc->u.BusNumber.MinBusNumber, ReqDesc->u.BusNumber.MaxBusNumber, - ReqDesc->u.BusNumber.Length); + FoundResource = FALSE; + } + break; + + case CmResourceTypeDma: + /* Find an available DMA channel */ + if (!IopFindDmaResource(IoDesc, &NewDesc)) + { + DPRINT1("Failed to find an available dma resource (0x%x to 0x%x)\n", + IoDesc->u.Dma.MinimumChannel, IoDesc->u.Dma.MaximumChannel); - if (ReqDesc->Option == 0) - { - ExFreePool(*ResourceList); - *ResourceList = NULL; - return STATUS_CONFLICTING_ADDRESSES; - } + FoundResource = FALSE; + } + break; + + default: + DPRINT1("Unsupported resource type: %x\n", IoDesc->Type); + FoundResource = FALSE; + break; + } - FoundResource = FALSE; - } - break; + /* Check if it's missing and required */ + if (!FoundResource && IoDesc->Option == 0) + { + if (*ResourceList) + { + ExFreePool(*ResourceList); + *ResourceList = NULL; + } + return STATUS_CONFLICTING_ADDRESSES; + } + else if (!FoundResource) + { + /* Try an alternate for this preferred descriptor */ + AlternateRequired = TRUE; + continue; + } + else + { + /* Move on to the next preferred or required descriptor after this one */ + AlternateRequired = FALSE; + } - case CmResourceTypeDma: - if (!IopFindDmaResource(ReqDesc, ResDesc)) - { - DPRINT1("Failed to find an available dma resource (0x%x to 0x%x)\n", - ReqDesc->u.Dma.MinimumChannel, ReqDesc->u.Dma.MaximumChannel); + /* Figure out what we need */ + if (PartialList == NULL) + { + /* We need a new list */ + NewList = ExAllocatePool(PagedPool, sizeof(CM_RESOURCE_LIST)); + if (!NewList) + return STATUS_NO_MEMORY; - if (ReqDesc->Option == 0) - { - ExFreePool(*ResourceList); - *ResourceList = NULL; - return STATUS_CONFLICTING_ADDRESSES; - } + /* Set it up */ + NewList->Count = 1; + NewList->List[0].InterfaceType = RequirementsList->InterfaceType; + NewList->List[0].BusNumber = RequirementsList->BusNumber; + NewList->List[0].PartialResourceList.Version = 1; + NewList->List[0].PartialResourceList.Revision = 1; + NewList->List[0].PartialResourceList.Count = 1; - FoundResource = FALSE; - } - break; + /* Set our pointer */ + DescPtr = &NewList->List[0].PartialResourceList.PartialDescriptors[0]; + } + else + { + /* Allocate the new larger list */ + NewList = ExAllocatePool(PagedPool, PnpDetermineResourceListSize(*ResourceList) + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)); + if (!NewList) + return STATUS_NO_MEMORY; - default: - DPRINT1("Unsupported resource type: %x\n", ReqDesc->Type); - FoundResource = FALSE; - break; - } + /* Copy the old stuff back */ + RtlCopyMemory(NewList, *ResourceList, PnpDetermineResourceListSize(*ResourceList)); - if (FoundResource) - { - (*ResourceList)->List[0].PartialResourceList.Count++; - ResDesc++; - } - } - } + /* Set our pointer */ + DescPtr = &NewList->List[0].PartialResourceList.PartialDescriptors[NewList->List[0].PartialResourceList.Count]; - return STATUS_SUCCESS; + /* Increment the descriptor count */ + NewList->List[0].PartialResourceList.Count++; + + /* Free the old list */ + ExFreePool(*ResourceList); + } + + /* Copy the descriptor in */ + *DescPtr = NewDesc; + + /* Store the new list */ + *ResourceList = NewList; + } + } + } + + /* Done */ + return STATUS_SUCCESS; } static @@ -888,50 +1029,50 @@ IopAssignDeviceResources( return STATUS_SUCCESS; } - /* Fill DeviceNode->ResourceList - * FIXME: the PnP arbiter should go there! - * Actually, use the BootResources if provided, else the resource requirements - */ - if (DeviceNode->BootResources) { - ListSize = PnpDetermineResourceListSize(DeviceNode->BootResources); + ListSize = PnpDetermineResourceListSize(DeviceNode->BootResources); - DeviceNode->ResourceList = ExAllocatePool(PagedPool, ListSize); - if (!DeviceNode->ResourceList) - { - Status = STATUS_NO_MEMORY; - goto ByeBye; - } - RtlCopyMemory(DeviceNode->ResourceList, DeviceNode->BootResources, ListSize); + DeviceNode->ResourceList = ExAllocatePool(PagedPool, ListSize); + if (!DeviceNode->ResourceList) + { + Status = STATUS_NO_MEMORY; + goto ByeBye; + } - Status = IopDetectResourceConflict(DeviceNode->ResourceList, FALSE, NULL); - if (NT_SUCCESS(Status) || !DeviceNode->ResourceRequirements) - { - if (!NT_SUCCESS(Status) && !DeviceNode->ResourceRequirements) - { - DPRINT1("Using conflicting boot resources because no requirements were supplied!\n"); - } + RtlCopyMemory(DeviceNode->ResourceList, DeviceNode->BootResources, ListSize); - goto Finish; - } - else - { - DPRINT1("Boot resources for %wZ cause a resource conflict!\n", &DeviceNode->InstancePath); - ExFreePool(DeviceNode->ResourceList); - DeviceNode->ResourceList = NULL; - } + Status = IopDetectResourceConflict(DeviceNode->ResourceList, FALSE, NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Boot resources for %wZ cause a resource conflict!\n", &DeviceNode->InstancePath); + ExFreePool(DeviceNode->ResourceList); + DeviceNode->ResourceList = NULL; + } + } + else + { + /* We'll make this from the requirements */ + DeviceNode->ResourceList = NULL; } - Status = IopCreateResourceListFromRequirements(DeviceNode->ResourceRequirements, - &DeviceNode->ResourceList); + /* No resources requirements */ + if (!DeviceNode->ResourceRequirements) + goto Finish; + + /* Call HAL to fixup our resource requirements list */ + HalAdjustResourceList(&DeviceNode->ResourceRequirements); + + /* Add resource requirements that aren't in the list we already got */ + Status = IopFixupResourceListWithRequirements(DeviceNode->ResourceRequirements, + &DeviceNode->ResourceList); if (!NT_SUCCESS(Status)) { - DPRINT1("Failed to create a resource list from supplied resources for %wZ\n", &DeviceNode->InstancePath); + DPRINT1("Failed to fixup a resource list from supplied resources for %wZ\n", &DeviceNode->InstancePath); goto ByeBye; } - /* IopCreateResourceListFromRequirements should NEVER succeed with a conflicting list */ + /* IopFixupResourceListWithRequirements should NEVER give us a conflicting list */ ASSERT(IopDetectResourceConflict(DeviceNode->ResourceList, FALSE, NULL) != STATUS_CONFLICTING_ADDRESSES); Finish: diff --git a/reactos/ntoskrnl/ob/obname.c b/reactos/ntoskrnl/ob/obname.c index b0d4ca59f2c..8d601eed77b 100644 --- a/reactos/ntoskrnl/ob/obname.c +++ b/reactos/ntoskrnl/ob/obname.c @@ -1208,4 +1208,30 @@ ObQueryDeviceMapInformation(IN PEPROCESS Process, KeReleaseGuardedMutex(&ObpDeviceMapLock); } +NTSTATUS +NTAPI +ObIsDosDeviceLocallyMapped( + IN ULONG Index, + OUT PUCHAR DosDeviceState) +{ + /* check parameters */ + if (Index < 1 || Index > 26) + { + /* invalid index */ + return STATUS_INVALID_PARAMETER; + } + + /* acquire lock */ + KeAcquireGuardedMutex(&ObpDeviceMapLock); + + /* get drive mapping status */ + *DosDeviceState = (ObSystemDeviceMap->DriveMap & (1 << Index)) != 0; + + /* release lock */ + KeReleaseGuardedMutex(&ObpDeviceMapLock); + + /* done */ + return STATUS_SUCCESS; +} + /* EOF */