move keyboard and mouse out of HAL into a standalone ps2.sys driver, implement usb stack with xhci controller discovery, hid keyboard/mouse, and bulk-only mass storage, add pci msi support with capability chain walking and software irq vector allocation (slots 16-23), rewrite user virtual memory as a USER_VA_REGION descriptor arena with per-process ownership tracking and guard page support, replacing the old bump allocator and top-down image region, add MmCreateUserRegion/MmDestroyAllUserRegions so process teardown reclaims all user va in one pass, allocate a pool-backed shadow framebuffer in hal_screen to fix cursor tearing and scroll corruption, reserve ap trampoline page below 1mb in uefi bootloader before boot services exit, rewrite memset/memcpy/memmove/memcmp with __stosq/__movsq intrinsics, promote driver flag literals to macros and add multi-tu STANDALONE_DRIVERS table to build.c, split rtl8139/rtl816x into header+source, rename KbReadScancode to KbReadKey, improve page fault output with MmDebugDescribeAddress and user region dump, fix %p to %016llX throughout.

This commit is contained in:
maksw committed 2026-06-02 00:01:20 +02:00
1 parent b970d7f59c
commit b838f8d83d
130 files changed
+8137 -2932

No files matched your search

+82 -5
View File
@@ -1,9 +1,86 @@
#include <ntlite/ddi/hal_api.h>
#include <ntlite/status.h>
#include <kernel/ke_debug.h>
// usb.c - USB bus driver entry point and PCI controller discovery.
#include "usb.h"
XHCI_CONTROLLER gXhci;
bool gHaveXhci = false;
const char* UsbControllerName(uint8_t progIf) {
switch (progIf) {
case 0x00: return "UHCI";
case 0x10: return "OHCI";
case 0x20: return "EHCI";
case 0x30: return "xHCI";
default: return "USB";
}
}
void UsbDiscoverControllers(const HAL_API* hal) {
uint32_t found = 0;
uint32_t xhciOk = 0;
for (uint16_t bus = 0; bus < 256; bus++) {
for (uint8_t dev = 0; dev < 32; dev++) {
for (uint8_t fn = 0; fn < 8; fn++) {
uint32_t vd = hal->Pci.Read32((uint8_t)bus, dev, fn, PCI_REG_VENDOR_DEVICE);
if ((vd & 0xFFFFu) == PCI_VENDOR_INVALID)
continue;
uint32_t classReg = hal->Pci.Read32((uint8_t)bus, dev, fn, PCI_REG_CLASS);
uint8_t classCode = (uint8_t)(classReg >> 24);
uint8_t subclass = (uint8_t)(classReg >> 16);
uint8_t progIf = (uint8_t)(classReg >> 8);
if (classCode != PCI_CLASS_SERIAL_BUS || subclass != PCI_SUBCLASS_USB)
continue;
USB_BAR bar0;
bool hasBar0 = UsbReadBar(hal, (uint8_t)bus, dev, fn, 0, &bar0);
uint32_t rawBar0 = hal->Pci.Read32((uint8_t)bus, dev, fn, PCI_REG_BAR0);
uint32_t rawBar1 = hal->Pci.Read32((uint8_t)bus, dev, fn, PCI_REG_BAR0 + 4u);
uint8_t irq = hal->Pci.GetIrqLine((uint8_t)bus, dev, fn);
DbgPrint("usb: found %s controller at %u:%u.%u vendor=%04x device=%04x irq=%u bar0raw=%08x bar1raw=%08x",
UsbControllerName(progIf),
bus, dev, fn,
vd & 0xFFFFu, vd >> 16,
irq, rawBar0, rawBar1);
if (hasBar0 && bar0.Kind == UsbBarMmio) {
DbgPrint(" mmio=0x%016llX\n", (unsigned long long)bar0.Base);
} else if (hasBar0 && bar0.Kind == UsbBarIo) {
DbgPrint(" io=0x%016llX\n", (unsigned long long)bar0.Base);
} else {
DbgPrint(" no-bar0\n");
}
if (progIf == PCI_PROGIF_XHCI) {
KSTATUS st = XhciInitialise(hal, (uint8_t)bus, dev, fn,
(uint16_t)(vd & 0xFFFFu),
(uint16_t)(vd >> 16));
if (KSUCCESS(st) || st == STATUS_ALREADY_EXISTS) xhciOk++;
else DbgPrint("usb: xHCI init failed st=0x%x\n", st);
}
found++;
}
}
}
if (!found)
DbgPrint("usb: no PCI USB controllers discovered\n");
else
DbgPrint("usb: discovery complete; controllers=%u xhci_initialised=%u\n", found, xhciOk);
}
KSTATUS DriverEntry(const HAL_API* hal) {
(void)hal;
DbgPrint("Test runtime driver\n");
if (!hal) return STATUS_INVALID_PARAMETER;
DbgPrint("usb: discovery starting\n");
KSTATUS st = UsbRegisterClassMatchers();
if (!KSUCCESS(st)) {
DbgPrint("usb: class matcher registration failed st=0x%x\n", st);
return st;
}
UsbDiscoverControllers(hal);
return STATUS_SUCCESS;
}
+593
View File
@@ -0,0 +1,593 @@
// usb.h - internal USB/xHCI driver interfaces shared across usb.sys translation units.
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/ddi/hal_api.h>
#include <ntlite/ddi/input_device.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/status.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_irq.h>
#include <kernel/ke_dpc.h>
#include <kernel/ke_irp.h>
#include <kernel/ke_spinlock.h>
#include <kernel/mm_phys.h>
#include <kernel/mm_virt.h>
#ifndef USB_DEBUG_VERBOSE
#define USB_DEBUG_VERBOSE 0
#endif
#if USB_DEBUG_VERBOSE
#define UsbTrace DbgPrint
#else
static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; }
#endif
#define PCI_VENDOR_INVALID 0xFFFFu
#define PCI_CLASS_SERIAL_BUS 0x0Cu
#define PCI_SUBCLASS_USB 0x03u
#define PCI_PROGIF_XHCI 0x30u
#define PCI_REG_VENDOR_DEVICE 0x00u
#define PCI_REG_COMMAND 0x04u
#define PCI_REG_CLASS 0x08u
#define PCI_REG_BAR0 0x10u
#define PCI_COMMAND_MEMORY (1u << 1)
#define PCI_COMMAND_MASTER (1u << 2)
#define PCI_COMMAND_INTX_DISABLE (1u << 10)
#define XHCI_CAP_CAPLENGTH 0x00u
#define XHCI_CAP_HCSPARAMS1 0x04u
#define XHCI_CAP_HCSPARAMS2 0x08u
#define XHCI_CAP_HCCPARAMS1 0x10u
#define XHCI_CAP_DBOFF 0x14u
#define XHCI_CAP_RTSOFF 0x18u
#define XHCI_OP_USBCMD 0x00u
#define XHCI_OP_USBSTS 0x04u
#define XHCI_OP_PAGESIZE 0x08u
#define XHCI_OP_DNCTRL 0x14u
#define XHCI_OP_CRCR 0x18u
#define XHCI_OP_DCBAAP 0x30u
#define XHCI_OP_CONFIG 0x38u
#define XHCI_OP_PORT_BASE 0x400u
#define XHCI_OP_PORT_STRIDE 0x10u
#define XHCI_USBCMD_RS (1u << 0)
#define XHCI_USBCMD_HCRST (1u << 1)
#define XHCI_USBCMD_INTE (1u << 2)
#define XHCI_USBCMD_HSEE (1u << 3)
#define XHCI_USBSTS_HCH (1u << 0)
#define XHCI_USBSTS_HSE (1u << 2)
#define XHCI_USBSTS_EINT (1u << 3)
#define XHCI_USBSTS_PCD (1u << 4)
#define XHCI_USBSTS_CNR (1u << 11)
#define XHCI_USBSTS_HCE (1u << 12)
#define XHCI_USBSTS_CLEAR_MASK (XHCI_USBSTS_HSE | XHCI_USBSTS_EINT | \
XHCI_USBSTS_PCD | XHCI_USBSTS_HCE)
#define XHCI_IMAN_IP (1u << 0)
#define XHCI_IMAN_IE (1u << 1)
#define XHCI_PORTSC_CCS (1u << 0)
#define XHCI_PORTSC_PED (1u << 1)
#define XHCI_PORTSC_OCA (1u << 3)
#define XHCI_PORTSC_PR (1u << 4)
#define XHCI_PORTSC_PP (1u << 9)
#define XHCI_PORTSC_SPEED_SHIFT 10u
#define XHCI_PORTSC_SPEED_MASK (0xFu << XHCI_PORTSC_SPEED_SHIFT)
#define XHCI_PORTSC_CSC (1u << 17)
#define XHCI_PORTSC_PEC (1u << 18)
#define XHCI_PORTSC_WRC (1u << 19)
#define XHCI_PORTSC_OCC (1u << 20)
#define XHCI_PORTSC_PRC (1u << 21)
#define XHCI_PORTSC_PLC (1u << 22)
#define XHCI_PORTSC_CEC (1u << 23)
#define XHCI_PORTSC_CHANGE_MASK (XHCI_PORTSC_CSC | XHCI_PORTSC_PEC | \
XHCI_PORTSC_WRC | XHCI_PORTSC_OCC | \
XHCI_PORTSC_PRC | XHCI_PORTSC_PLC | \
XHCI_PORTSC_CEC)
#define XHCI_EXT_CAP_LEGACY 0x01u
#define XHCI_EXT_CAP_PROTOCOL 0x02u
#define XHCI_LEGSUP_BIOS_OWNED (1u << 16)
#define XHCI_LEGSUP_OS_OWNED (1u << 24)
#define XHCI_TRB_TYPE_SHIFT 10u
#define XHCI_TRB_TYPE_MASK 0x3Fu
#define XHCI_TRB_CYCLE (1u << 0)
#define XHCI_TRB_LINK_TC (1u << 1)
#define XHCI_TRB_TYPE_LINK 6u
#define XHCI_TRB_TYPE_NOOP_CMD 23u
#define XHCI_TRB_TYPE_CMD_DONE 33u
#define XHCI_TRB_TYPE_PORT_SC 34u
#define XHCI_TRB_TYPE_TRANSFER 32u
#define XHCI_TRB_TYPE_NORMAL 1u
#define XHCI_TRB_TYPE_SETUP 2u
#define XHCI_TRB_TYPE_DATA 3u
#define XHCI_TRB_TYPE_STATUS 4u
#define XHCI_TRB_TYPE_ENABLE_SLOT 9u
#define XHCI_TRB_TYPE_ADDRESS_DEVICE 11u
#define XHCI_TRB_TYPE_CONFIGURE_ENDPOINT 12u
#define XHCI_CC_SUCCESS 1u
#define XHCI_TRB_ISP (1u << 2)
#define XHCI_TRB_IOC (1u << 5)
#define XHCI_TRB_IDT (1u << 6)
#define XHCI_TRB_DATA_DIR_IN (1u << 16)
#define XHCI_TRB_SETUP_TRT_NONE (0u << 16)
#define XHCI_TRB_SETUP_TRT_IN (3u << 16)
#define XHCI_TRB_STATUS_DIR_IN (1u << 16)
#define XHCI_HCC1_CSZ (1u << 2)
#define XHCI_CONTEXT_ENTRIES(n) ((uint32_t)(n) << 27)
#define XHCI_SLOT_SPEED(s) ((uint32_t)(s) << 20)
#define XHCI_SLOT_ROOT_PORT(p) ((uint32_t)(p) << 16)
#define XHCI_EP_TYPE_CONTROL (4u << 3)
#define XHCI_EP_TYPE_BULK_OUT (2u << 3)
#define XHCI_EP_TYPE_BULK_IN (6u << 3)
#define XHCI_EP_TYPE_INT_IN (7u << 3)
#define XHCI_EP_CERR_3 (3u << 1)
#define XHCI_EP_DCS 1ull
#define USB_REQ_GET_DESCRIPTOR 0x06u
#define USB_REQ_SET_CONFIGURATION 0x09u
#define USB_REQ_SET_IDLE 0x0Au
#define USB_REQ_SET_PROTOCOL 0x0Bu
#define USB_DESC_DEVICE 0x01u
#define USB_DESC_CONFIGURATION 0x02u
#define USB_DESC_INTERFACE 0x04u
#define USB_DESC_ENDPOINT 0x05u
#define USB_DESC_HID 0x21u
#define USB_DESC_CS_INTERFACE 0x24u
#define USB_DIR_OUT 0x00u
#define USB_DIR_IN 0x80u
#define USB_TYPE_STANDARD 0x00u
#define USB_TYPE_CLASS 0x20u
#define USB_RECIP_DEVICE 0x00u
#define USB_RECIP_INTERFACE 0x01u
#define USB_ENDPOINT_DIR_IN 0x80u
#define USB_ENDPOINT_NUMBER_MASK 0x0Fu
#define USB_ENDPOINT_ATTR_TYPE_MASK 0x03u
#define USB_ENDPOINT_TYPE_CONTROL 0x00u
#define USB_ENDPOINT_TYPE_ISOCH 0x01u
#define USB_ENDPOINT_TYPE_BULK 0x02u
#define USB_ENDPOINT_TYPE_INTERRUPT 0x03u
#define USB_CLASS_HID 0x03u
#define USB_CLASS_MASS_STORAGE 0x08u
#define USB_MASS_SUBCLASS_SCSI 0x06u
#define USB_MASS_PROTOCOL_BOT 0x50u
#define USB_HID_SUBCLASS_BOOT 0x01u
#define USB_HID_PROTOCOL_KEYBOARD 0x01u
#define USB_HID_PROTOCOL_MOUSE 0x02u
#define USB_MASS_BOT_CBW_SIGNATURE 0x43425355u
#define USB_MASS_BOT_CSW_SIGNATURE 0x53425355u
#define USB_MASS_BOT_CSW_PASSED 0u
#define USB_MASS_BOT_CSW_FAILED 1u
#define SCSI_TEST_UNIT_READY 0x00u
#define SCSI_REQUEST_SENSE 0x03u
#define SCSI_INQUIRY 0x12u
#define SCSI_READ_10 0x28u
#define SCSI_WRITE_10 0x2Au
#define SCSI_READ_CAPACITY_10 0x25u
#define XHCI_RING_TRBS 256u
#define XHCI_EVENT_TRBS 256u
#define XHCI_DCBAA_ENTRIES 256u
#define XHCI_MMIO_FLOOR_BYTES 0x10000u
#define XHCI_MAX_SCRATCHPADS 1024u
#define XHCI_MAX_TRACKED_SLOTS 64u
#define XHCI_MAX_ROOT_PORTS 255u
#define XHCI_EP0_RING_TRBS 256u
#define XHCI_HID_RING_TRBS 256u
#define XHCI_BULK_RING_TRBS 256u
#define XHCI_MAX_HID_REPORT 16u
#define USB_STORAGE_DMA_PAGES 1u
#define USB_STORAGE_MAX_BYTES (USB_STORAGE_DMA_PAGES * PAGE_SIZE)
#define USB_STORAGE_SECTOR_SIZE 512u
#define USB_MAX_TRACKED_INTERFACES 8u
#define USB_MAX_TRACKED_ENDPOINTS 8u
#pragma pack(push, 1)
typedef struct XHCI_TRB {
uint32_t ParameterLo;
uint32_t ParameterHi;
uint32_t Status;
uint32_t Control;
} XHCI_TRB;
typedef struct XHCI_ERST_ENTRY {
uint64_t RingSegmentBase;
uint32_t RingSegmentSize;
uint32_t Reserved;
} XHCI_ERST_ENTRY;
typedef struct USB_SETUP_PACKET {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} USB_SETUP_PACKET;
typedef struct USB_DEVICE_DESCRIPTOR {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} USB_DEVICE_DESCRIPTOR;
typedef struct USB_CONFIGURATION_DESCRIPTOR {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
} USB_CONFIGURATION_DESCRIPTOR;
typedef struct USB_INTERFACE_DESCRIPTOR {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
} USB_INTERFACE_DESCRIPTOR;
typedef struct USB_ENDPOINT_DESCRIPTOR {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} USB_ENDPOINT_DESCRIPTOR;
typedef struct USB_MASS_CBW {
uint32_t Signature;
uint32_t Tag;
uint32_t DataTransferLength;
uint8_t Flags;
uint8_t Lun;
uint8_t CommandBlockLength;
uint8_t CommandBlock[16];
} USB_MASS_CBW;
typedef struct USB_MASS_CSW {
uint32_t Signature;
uint32_t Tag;
uint32_t DataResidue;
uint8_t Status;
} USB_MASS_CSW;
#pragma pack(pop)
typedef struct USB_TRACKED_INTERFACE {
bool Present;
USB_INTERFACE_DESCRIPTOR Descriptor;
uint8_t EndpointCount;
USB_ENDPOINT_DESCRIPTOR Endpoints[USB_MAX_TRACKED_ENDPOINTS];
} USB_TRACKED_INTERFACE;
typedef enum USB_BAR_KIND {
UsbBarNone,
UsbBarIo,
UsbBarMmio
} USB_BAR_KIND;
typedef struct USB_BAR {
USB_BAR_KIND Kind;
uint64_t Base;
uint8_t Index;
} USB_BAR;
typedef struct XHCI_DEVICE {
bool Allocated;
bool Addressed;
uint8_t SlotId;
uint8_t Port;
uint8_t Speed;
uint16_t MaxPacketSize0;
uint16_t VendorId;
uint16_t ProductId;
uint16_t BcdUsb;
uint16_t BcdDevice;
uint8_t DeviceClass;
uint8_t DeviceSubclass;
uint8_t DeviceProtocol;
uint8_t ConfigurationValue;
bool Configured;
uint8_t InterfaceCount;
USB_TRACKED_INTERFACE Interfaces[USB_MAX_TRACKED_INTERFACES];
bool HidCandidate;
bool HidActive;
bool HidKeyboard;
bool HidMouse;
bool HidArmed;
uint8_t HidInterface;
uint8_t HidProtocol;
uint8_t HidEndpointAddress;
uint8_t HidEndpointId;
uint8_t HidInterval;
uint16_t HidMaxPacket;
uint16_t HidReportLength;
uint32_t HidEnqueue;
uint8_t HidCycle;
uint8_t HidLastModifiers;
uint8_t HidLastKeys[6];
uint8_t HidLastButtons;
uint64_t HidLastTrbPhys;
bool MassCandidate;
bool MassActive;
uint8_t MassInterface;
uint8_t MassBulkInAddress;
uint8_t MassBulkOutAddress;
uint8_t MassBulkInEndpointId;
uint8_t MassBulkOutEndpointId;
uint16_t MassBulkInMaxPacket;
uint16_t MassBulkOutMaxPacket;
uint32_t MassInEnqueue;
uint8_t MassInCycle;
uint32_t MassOutEnqueue;
uint8_t MassOutCycle;
uint32_t MassTag;
uint32_t MassBlockSize;
uint64_t MassBlockCount;
DEVICE_OBJECT* MassDeviceObject;
volatile long MassIoBusy;
PHYSICAL_ADDRESS InputContextPhys;
PHYSICAL_ADDRESS DeviceContextPhys;
PHYSICAL_ADDRESS Ep0RingPhys;
PHYSICAL_ADDRESS HidRingPhys;
PHYSICAL_ADDRESS MassInRingPhys;
PHYSICAL_ADDRESS MassOutRingPhys;
PHYSICAL_ADDRESS DescriptorPhys;
PHYSICAL_ADDRESS HidBufferPhys;
PHYSICAL_ADDRESS MassBufferPhys;
uint8_t* InputContext;
uint8_t* DeviceContext;
XHCI_TRB* Ep0Ring;
XHCI_TRB* HidRing;
XHCI_TRB* MassInRing;
XHCI_TRB* MassOutRing;
uint8_t* DescriptorBuffer;
uint8_t* HidBuffer;
uint8_t* MassBuffer;
uint32_t Ep0Enqueue;
uint8_t Ep0Cycle;
} XHCI_DEVICE;
typedef struct XHCI_COMMAND_RESULT {
uint32_t CompletionCode;
uint8_t SlotId;
} XHCI_COMMAND_RESULT;
typedef struct XHCI_TRANSFER_RESULT {
uint32_t CompletionCode;
uint8_t SlotId;
uint8_t EndpointId;
uint32_t Residue;
} XHCI_TRANSFER_RESULT;
typedef struct XHCI_WAIT_TARGET {
uint64_t CommandPhys;
uint64_t TransferPhys;
uint8_t TransferSlotId;
uint8_t TransferEndpointId;
bool CommandMatched;
bool TransferMatched;
XHCI_COMMAND_RESULT Command;
XHCI_TRANSFER_RESULT Transfer;
} XHCI_WAIT_TARGET;
typedef struct XHCI_CONTROLLER {
const HAL_API* Hal;
uint8_t Bus;
uint8_t Dev;
uint8_t Fn;
uint8_t Irq;
bool UsingMsi;
uint64_t MmioPhys;
uint32_t MmioBytes;
volatile uint8_t* Mmio;
volatile uint8_t* Cap;
volatile uint8_t* Op;
volatile uint8_t* Runtime;
volatile uint8_t* Doorbells;
uint8_t CapLength;
uint16_t Version;
uint32_t Hcs1;
uint32_t Hcs2;
uint32_t Hcc1;
uint32_t DbOff;
uint32_t RtsOff;
uint8_t MaxSlots;
uint16_t MaxIntrs;
uint8_t MaxPorts;
uint16_t ScratchpadCount;
uint8_t PortProtocolMajor[XHCI_MAX_ROOT_PORTS + 1u];
uint8_t PortProtocolMinor[XHCI_MAX_ROOT_PORTS + 1u];
PHYSICAL_ADDRESS DcbaaPhys;
PHYSICAL_ADDRESS ScratchpadArrayPhys;
PHYSICAL_ADDRESS ScratchpadPhys[XHCI_MAX_SCRATCHPADS];
PHYSICAL_ADDRESS CommandRingPhys;
PHYSICAL_ADDRESS EventRingPhys;
PHYSICAL_ADDRESS ErstPhys;
uint64_t* Dcbaa;
uint64_t* ScratchpadArray;
XHCI_TRB* CommandRing;
XHCI_TRB* EventRing;
XHCI_ERST_ENTRY* Erst;
uint32_t CommandEnqueue;
uint8_t CommandCycle;
uint32_t EventDequeue;
uint8_t EventCycle;
uint8_t ContextBytes;
XHCI_DEVICE Devices[XHCI_MAX_TRACKED_SLOTS + 1u];
uint32_t HidDeviceCount;
KDPC EventDpc;
volatile bool InterruptsEnabled;
volatile uint32_t InterruptCount;
volatile uint32_t EventDpcCount;
SPINLOCK EventLock;
volatile XHCI_WAIT_TARGET* ActiveWait;
volatile bool Running;
} XHCI_CONTROLLER;
extern XHCI_CONTROLLER gXhci;
extern bool gHaveXhci;
extern DRIVER_OBJECT gUsbStorageDriver;
uint32_t UsbAlignUp32(uint32_t value, uint32_t align);
void XhciBarrier(void);
uint8_t XhciRead8(volatile uint8_t* base, uint32_t reg);
uint16_t XhciRead16(volatile uint8_t* base, uint32_t reg);
uint32_t XhciRead32(volatile uint8_t* base, uint32_t reg);
void XhciWrite32(volatile uint8_t* base, uint32_t reg, uint32_t value);
uint64_t XhciRead64(volatile uint8_t* base, uint32_t reg);
void XhciWrite64(volatile uint8_t* base, uint32_t reg, uint64_t value);
void UsbDelayUs(const HAL_API* hal, uint32_t us);
bool UsbWaitSet32(const HAL_API* hal, volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs);
bool UsbWaitClear32(const HAL_API* hal, volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs);
bool UsbReadBar(const HAL_API* hal, uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, USB_BAR* out);
KSTATUS UsbMapMmio(const HAL_API* hal, uint64_t physBase, uint32_t bytes, volatile uint8_t** outMmio);
void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci, size_t pages, PHYSICAL_ADDRESS* outPhys);
uint16_t XhciScratchpadCount(uint32_t hcs2);
uint32_t XhciTrbType(const XHCI_TRB* trb);
uint32_t XhciCompletionCode(const XHCI_TRB* trb);
uint8_t XhciEventSlotId(const XHCI_TRB* trb);
uint8_t XhciEventEndpointId(const XHCI_TRB* trb);
uint32_t XhciTransferResidue(const XHCI_TRB* trb);
uint32_t* XhciInputControlContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
uint32_t* XhciInputSlotContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
uint32_t* XhciInputEndpointContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, uint8_t endpointId);
uint32_t XhciEpMaxPacket(uint8_t speed);
uint64_t UsbSetupPacketToU64(const USB_SETUP_PACKET* setup);
const char* XhciPortSpeedName(uint32_t speed);
void XhciParseSupportedProtocols(XHCI_CONTROLLER* xhci);
uint8_t XhciPortProtocolMajor(XHCI_CONTROLLER* xhci, uint8_t port, uint32_t portsc);
const char* UsbEndpointTypeName(uint8_t type);
const char* UsbDirectionName(uint8_t endpointAddress);
const char* UsbInterfaceClassName(uint8_t klass, uint8_t subclass, uint8_t proto);
void XhciLogPortStatus(XHCI_CONTROLLER* xhci);
void XhciAckInterrupter0(XHCI_CONTROLLER* xhci);
void XhciUpdateErdp(XHCI_CONTROLLER* xhci);
bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target);
bool XhciDrainEventRingLocked(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target);
bool XhciWaitTargetComplete(const XHCI_WAIT_TARGET* target);
KSTATUS XhciWaitForTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target, uint32_t timeoutUs);
void XhciEventDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2);
void XhciIsr(void);
KSTATUS XhciBiosHandoff(XHCI_CONTROLLER* xhci);
KSTATUS XhciStopAndReset(XHCI_CONTROLLER* xhci);
KSTATUS XhciAllocateScratchpads(XHCI_CONTROLLER* xhci);
KSTATUS XhciAllocateRuntimeMemory(XHCI_CONTROLLER* xhci);
KSTATUS XhciProgramRuntime(XHCI_CONTROLLER* xhci);
uint64_t XhciCommandTrbPhys(XHCI_CONTROLLER* xhci, uint32_t index);
KSTATUS XhciSubmitCommand(XHCI_CONTROLLER* xhci, uint64_t parameter, uint32_t status, uint32_t controlWithoutCycle, XHCI_COMMAND_RESULT* outResult);
KSTATUS XhciSubmitNoopCommand(XHCI_CONTROLLER* xhci);
KSTATUS XhciEnableSlot(XHCI_CONTROLLER* xhci, uint8_t* outSlotId);
KSTATUS XhciAddressDevice(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
KSTATUS XhciStart(XHCI_CONTROLLER* xhci);
void XhciEnableInterrupts(XHCI_CONTROLLER* xhci);
uint32_t XhciPortReg(uint8_t port);
void XhciClearPortChanges(XHCI_CONTROLLER* xhci, uint8_t port);
KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed);
KSTATUS XhciAllocateDeviceMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
void XhciBuildDefaultControlContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
uint64_t XhciEp0TrbPhys(XHCI_DEVICE* dev, uint32_t index);
KSTATUS XhciEp0AppendTrb(XHCI_DEVICE* dev, uint64_t parameter, uint32_t status, uint32_t controlWithoutCycle, uint64_t* outPhys);
KSTATUS XhciControlIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, const USB_SETUP_PACKET* setup, void* buffer, PHYSICAL_ADDRESS bufferPhys, uint16_t length);
KSTATUS XhciControlNoData(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, const USB_SETUP_PACKET* setup);
KSTATUS XhciGetDeviceDescriptor(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, USB_DEVICE_DESCRIPTOR* outDesc);
KSTATUS XhciGetConfigurationDescriptor(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, uint8_t configurationIndex, USB_CONFIGURATION_DESCRIPTOR* outConfig, uint16_t* outTotalLength);
uint8_t XhciEndpointIdFromAddress(uint8_t endpointAddress);
uint8_t XhciInterruptInterval(uint8_t speed, uint8_t usbInterval);
KSTATUS XhciRingAppend(XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys, uint32_t trbCount, uint32_t* enqueue, uint8_t* cycle, uint64_t parameter, uint32_t status, uint32_t controlWithoutCycle, uint64_t* outPhys);
bool XhciHidKeyInReport(const uint8_t* keys, uint8_t key);
INPUT_KEY XhciHidModifierKey(uint8_t bit);
void XhciSubmitHidKey(INPUT_KEY key, bool pressed);
void XhciProcessBootKeyboardReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_t len);
void XhciProcessBootMouseReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_t len);
KSTATUS XhciArmHidInterruptIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
void XhciArmAllHid(XHCI_CONTROLLER* xhci);
void XhciHandleAsyncTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbPhys, uint32_t completionCode, uint8_t slotId, uint8_t endpointId, uint32_t residue);
KSTATUS XhciStartHidInterrupts(XHCI_CONTROLLER* xhci);
KSTATUS XhciSetHidBootProtocol(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
KSTATUS XhciAllocateHidMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
KSTATUS XhciConfigureHidEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
const IO_PUBLISHED_USB_ENDPOINT* UsbFindEndpoint(const IO_PUBLISHED_DEVICE* pub, uint8_t endpointType, bool wantIn);
bool UsbHidMatch(const IO_PUBLISHED_DEVICE* pub, void* context);
KSTATUS UsbHidBind(IO_PUBLISHED_DEVICE* pub, void* context);
void XhciInitialiseTransferRing(XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys, uint32_t trbCount, uint8_t cycle);
KSTATUS XhciBulkTransfer(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, uint8_t endpointId, XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys, uint32_t* enqueue, uint8_t* cycle, PHYSICAL_ADDRESS bufferPhys, uint32_t length, uint32_t* outActual);
KSTATUS UsbStorageBulkIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, PHYSICAL_ADDRESS phys, uint32_t length, uint32_t* outActual);
KSTATUS UsbStorageBulkOut(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, PHYSICAL_ADDRESS phys, uint32_t length, uint32_t* outActual);
KSTATUS UsbStorageBotCommand(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, const uint8_t* cdb, uint8_t cdbLen, void* data, PHYSICAL_ADDRESS dataPhys, uint32_t dataLength, bool dataIn);
uint32_t UsbBe32(const uint8_t* p);
void UsbStoreBe32(uint8_t* p, uint32_t v);
void UsbStoreBe16(uint8_t* p, uint16_t v);
KSTATUS UsbStorageInquiry(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
KSTATUS UsbStorageRequestSense(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
KSTATUS UsbStorageTestUnitReady(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
KSTATUS UsbStorageReadCapacity(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
KSTATUS UsbStorageReadWriteSectors(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, bool write, uint64_t lba, uint32_t sectors, void* buffer);
void UsbStorageAcquireIo(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
void UsbStorageReleaseIo(XHCI_DEVICE* dev);
KSTATUS UsbStorageDispatchReadWrite(DEVICE_OBJECT* device, IRP* irp);
KSTATUS XhciAllocateMassStorageMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
KSTATUS XhciConfigureMassStorageEndpoints(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
uint8_t UsbFindFreeDiskIndex(void);
KSTATUS UsbStorageRegisterDisk(XHCI_DEVICE* dev);
bool UsbStorageMatch(const IO_PUBLISHED_DEVICE* pub, void* context);
KSTATUS UsbStorageBind(IO_PUBLISHED_DEVICE* pub, void* context);
KSTATUS UsbRegisterClassMatchers(void);
KSTATUS XhciSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, uint8_t configurationValue);
void XhciLogConfigurationTree(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, const USB_CONFIGURATION_DESCRIPTOR* config, uint16_t totalLength);
void XhciPublishUsbInterfaces(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, const USB_DEVICE_DESCRIPTOR* devDesc);
KSTATUS XhciEnumeratePort(XHCI_CONTROLLER* xhci, uint8_t port);
void XhciEnumerateRootPorts(XHCI_CONTROLLER* xhci);
KSTATUS XhciInitialise(const HAL_API* hal, uint8_t bus, uint8_t dev, uint8_t fn, uint16_t vendor, uint16_t device);
+361
View File
@@ -0,0 +1,361 @@
// usb_core.c - USB descriptor/configuration parsing, interface publishing, and root-port enumeration.
#include "usb.h"
KSTATUS XhciSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
uint8_t configurationValue) {
USB_SETUP_PACKET setup;
setup.bmRequestType = USB_DIR_OUT;
setup.bRequest = USB_REQ_SET_CONFIGURATION;
setup.wValue = configurationValue;
setup.wIndex = 0;
setup.wLength = 0;
KSTATUS st = XhciControlNoData(xhci, dev, &setup);
if (!KSUCCESS(st)) {
DbgPrint("usb: SET_CONFIGURATION slot=%u value=%u failed st=0x%x\n",
dev->SlotId, configurationValue, st);
return st;
}
dev->ConfigurationValue = configurationValue;
dev->Configured = true;
UsbTrace("usb: SET_CONFIGURATION slot=%u value=%u complete\n",
dev->SlotId, configurationValue);
return STATUS_SUCCESS;
}
void XhciLogConfigurationTree(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
const USB_CONFIGURATION_DESCRIPTOR* config,
uint16_t totalLength) {
(void)xhci;
memset(dev->Interfaces, 0, sizeof(dev->Interfaces));
dev->InterfaceCount = 0;
UsbTrace("usb: config slot=%u value=%u total=%u interfaces=%u attrs=%02x maxpower=%umA\n",
dev->SlotId,
config->bConfigurationValue,
totalLength,
config->bNumInterfaces,
config->bmAttributes,
(uint32_t)config->bMaxPower * 2u);
uint16_t off = config->bLength;
uint8_t currentInterface = 0xFFu;
uint8_t currentClass = 0;
uint8_t currentSubClass = 0;
uint8_t currentProtocol = 0;
bool hidCandidate = false;
bool massCandidate = false;
uint8_t hidInterruptIn = 0;
uint8_t massBulkIn = 0;
uint8_t massBulkOut = 0;
USB_TRACKED_INTERFACE* tracked = (USB_TRACKED_INTERFACE*)0;
while (off + 2u <= totalLength) {
const uint8_t* desc = dev->DescriptorBuffer + off;
uint8_t len = desc[0];
uint8_t type = desc[1];
if (len < 2 || off + len > totalLength) {
DbgPrint("usb: descriptor parse stopped slot=%u off=%u len=%u type=%u total=%u\n",
dev->SlotId, off, len, type, totalLength);
break;
}
if (type == USB_DESC_INTERFACE && len >= sizeof(USB_INTERFACE_DESCRIPTOR)) {
const USB_INTERFACE_DESCRIPTOR* iface = (const USB_INTERFACE_DESCRIPTOR*)desc;
currentInterface = iface->bInterfaceNumber;
currentClass = iface->bInterfaceClass;
currentSubClass = iface->bInterfaceSubClass;
currentProtocol = iface->bInterfaceProtocol;
hidCandidate = (currentClass == USB_CLASS_HID);
massCandidate = (currentClass == USB_CLASS_MASS_STORAGE &&
currentSubClass == USB_MASS_SUBCLASS_SCSI &&
currentProtocol == USB_MASS_PROTOCOL_BOT);
hidInterruptIn = 0;
massBulkIn = 0;
massBulkOut = 0;
tracked = (USB_TRACKED_INTERFACE*)0;
if (dev->InterfaceCount < USB_MAX_TRACKED_INTERFACES) {
tracked = &dev->Interfaces[dev->InterfaceCount++];
memset(tracked, 0, sizeof(*tracked));
tracked->Present = true;
memcpy(&tracked->Descriptor, iface, sizeof(*iface));
} else {
DbgPrint("usb: slot=%u too many interfaces, interface %u not published\n",
dev->SlotId, iface->bInterfaceNumber);
}
UsbTrace("usb: interface slot=%u if=%u alt=%u eps=%u class=%02x subclass=%02x proto=%02x (%s)\n",
dev->SlotId,
iface->bInterfaceNumber,
iface->bAlternateSetting,
iface->bNumEndpoints,
currentClass,
currentSubClass,
currentProtocol,
UsbInterfaceClassName(currentClass, currentSubClass, currentProtocol));
} else if (type == USB_DESC_ENDPOINT && len >= sizeof(USB_ENDPOINT_DESCRIPTOR)) {
const USB_ENDPOINT_DESCRIPTOR* ep = (const USB_ENDPOINT_DESCRIPTOR*)desc;
uint8_t epType = ep->bmAttributes & USB_ENDPOINT_ATTR_TYPE_MASK;
uint8_t epNumber = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
bool in = (ep->bEndpointAddress & USB_ENDPOINT_DIR_IN) != 0;
if (tracked && tracked->EndpointCount < USB_MAX_TRACKED_ENDPOINTS) {
memcpy(&tracked->Endpoints[tracked->EndpointCount++], ep, sizeof(*ep));
} else if (tracked) {
DbgPrint("usb: slot=%u if=%u too many endpoints, endpoint %02x not published\n",
dev->SlotId, currentInterface, ep->bEndpointAddress);
}
UsbTrace("usb: endpoint slot=%u if=%u addr=%02x ep=%u dir=%s type=%s maxpkt=%u interval=%u\n",
dev->SlotId,
currentInterface,
ep->bEndpointAddress,
epNumber,
UsbDirectionName(ep->bEndpointAddress),
UsbEndpointTypeName(epType),
ep->wMaxPacketSize,
ep->bInterval);
if (hidCandidate && in && epType == USB_ENDPOINT_TYPE_INTERRUPT && hidInterruptIn == 0) {
hidInterruptIn = ep->bEndpointAddress;
}
if (massCandidate && epType == USB_ENDPOINT_TYPE_BULK) {
if (in && massBulkIn == 0) massBulkIn = ep->bEndpointAddress;
if (!in && massBulkOut == 0) massBulkOut = ep->bEndpointAddress;
}
} else if (type == USB_DESC_HID) {
UsbTrace("usb: hid descriptor slot=%u if=%u len=%u\n",
dev->SlotId, currentInterface, len);
} else if (type == USB_DESC_CS_INTERFACE) {
UsbTrace("usb: class-specific interface descriptor slot=%u if=%u len=%u subtype=%u\n",
dev->SlotId, currentInterface, len, len >= 3 ? desc[2] : 0);
}
uint16_t next = (uint16_t)(off + len);
if (hidCandidate && hidInterruptIn &&
(next >= totalLength || (next + 1u < totalLength && dev->DescriptorBuffer[next + 1u] == USB_DESC_INTERFACE))) {
UsbTrace("usb: class candidate slot=%u if=%u hid interrupt-in=%02x subclass=%02x proto=%02x\n",
dev->SlotId,
currentInterface,
hidInterruptIn,
currentSubClass,
currentProtocol);
hidCandidate = false;
}
if (massCandidate && massBulkIn && massBulkOut &&
(next >= totalLength || (next + 1u < totalLength && dev->DescriptorBuffer[next + 1u] == USB_DESC_INTERFACE))) {
UsbTrace("usb: class candidate slot=%u if=%u mass-storage bulk-in=%02x bulk-out=%02x\n",
dev->SlotId,
currentInterface,
massBulkIn,
massBulkOut);
massCandidate = false;
}
off = next;
}
if (hidCandidate && hidInterruptIn) {
UsbTrace("usb: class candidate slot=%u if=%u hid interrupt-in=%02x subclass=%02x proto=%02x\n",
dev->SlotId,
currentInterface,
hidInterruptIn,
currentSubClass,
currentProtocol);
}
if (massCandidate && massBulkIn && massBulkOut) {
UsbTrace("usb: class candidate slot=%u if=%u mass-storage bulk-in=%02x bulk-out=%02x\n",
dev->SlotId,
currentInterface,
massBulkIn,
massBulkOut);
}
}
void XhciPublishUsbInterfaces(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
for (uint8_t i = 0; i < dev->InterfaceCount; i++) {
USB_TRACKED_INTERFACE* iface = &dev->Interfaces[i];
if (!iface->Present)
continue;
IO_PUBLISHED_DEVICE pub;
memset(&pub, 0, sizeof(pub));
pub.BusType = IoBusUsb;
pub.Name = "usb-interface";
pub.BusContext = xhci;
pub.DeviceContext = dev;
pub.u.Usb.VendorId = dev->VendorId;
pub.u.Usb.ProductId = dev->ProductId;
pub.u.Usb.BcdUsb = dev->BcdUsb;
pub.u.Usb.BcdDevice = dev->BcdDevice;
pub.u.Usb.DeviceClass = dev->DeviceClass;
pub.u.Usb.DeviceSubclass = dev->DeviceSubclass;
pub.u.Usb.DeviceProtocol = dev->DeviceProtocol;
pub.u.Usb.SlotId = dev->SlotId;
pub.u.Usb.Port = dev->Port;
pub.u.Usb.Speed = dev->Speed;
pub.u.Usb.ConfigurationValue = dev->ConfigurationValue;
pub.u.Usb.InterfaceNumber = iface->Descriptor.bInterfaceNumber;
pub.u.Usb.AlternateSetting = iface->Descriptor.bAlternateSetting;
pub.u.Usb.InterfaceClass = iface->Descriptor.bInterfaceClass;
pub.u.Usb.InterfaceSubclass = iface->Descriptor.bInterfaceSubClass;
pub.u.Usb.InterfaceProtocol = iface->Descriptor.bInterfaceProtocol;
pub.u.Usb.EndpointCount = iface->EndpointCount;
if (pub.u.Usb.EndpointCount > IO_PUBLISHED_USB_MAX_ENDPOINTS)
pub.u.Usb.EndpointCount = IO_PUBLISHED_USB_MAX_ENDPOINTS;
for (uint8_t ep = 0; ep < pub.u.Usb.EndpointCount; ep++) {
pub.u.Usb.Endpoints[ep].Address = iface->Endpoints[ep].bEndpointAddress;
pub.u.Usb.Endpoints[ep].Attributes = iface->Endpoints[ep].bmAttributes;
pub.u.Usb.Endpoints[ep].MaxPacketSize = iface->Endpoints[ep].wMaxPacketSize;
pub.u.Usb.Endpoints[ep].Interval = iface->Endpoints[ep].bInterval;
}
KSTATUS st = IoPublishDevice(&pub);
if (!KSUCCESS(st)) {
DbgPrint("usb: publish interface slot=%u if=%u failed st=0x%x\n",
dev->SlotId, iface->Descriptor.bInterfaceNumber, st);
}
}
}
KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
const USB_DEVICE_DESCRIPTOR* devDesc) {
if (devDesc->bNumConfigurations == 0) {
DbgPrint("usb: slot=%u has no configurations\n", dev->SlotId);
return STATUS_NOT_FOUND;
}
USB_CONFIGURATION_DESCRIPTOR config;
memset(&config, 0, sizeof(config));
uint16_t totalLength = 0;
KSTATUS st = XhciGetConfigurationDescriptor(xhci, dev, 0, &config, &totalLength);
if (!KSUCCESS(st)) {
DbgPrint("usb: slot=%u configuration descriptor read failed st=0x%x\n",
dev->SlotId, st);
return st;
}
XhciLogConfigurationTree(xhci, dev, &config, totalLength);
if (config.bConfigurationValue == 0) {
DbgPrint("usb: slot=%u configuration value 0 is invalid for SET_CONFIGURATION\n",
dev->SlotId);
return STATUS_DEVICE_ERROR;
}
st = XhciSetConfiguration(xhci, dev, config.bConfigurationValue);
if (!KSUCCESS(st)) return st;
XhciPublishUsbInterfaces(xhci, dev);
return STATUS_SUCCESS;
}
KSTATUS XhciEnumeratePort(XHCI_CONTROLLER* xhci, uint8_t port) {
uint8_t speed = 0;
KSTATUS st = XhciResetPort(xhci, port, &speed);
if (!KSUCCESS(st)) return st;
uint8_t slotId = 0;
st = XhciEnableSlot(xhci, &slotId);
if (!KSUCCESS(st)) return st;
XHCI_DEVICE* dev = &xhci->Devices[slotId];
memset(dev, 0, sizeof(*dev));
dev->Allocated = true;
dev->SlotId = slotId;
dev->Port = port;
dev->Speed = speed;
st = XhciAllocateDeviceMemory(xhci, dev);
if (!KSUCCESS(st)) return st;
XhciBuildDefaultControlContext(xhci, dev);
st = XhciAddressDevice(xhci, dev);
if (!KSUCCESS(st)) return st;
dev->Addressed = true;
USB_DEVICE_DESCRIPTOR desc;
memset(&desc, 0, sizeof(desc));
st = XhciGetDeviceDescriptor(xhci, dev, &desc);
if (!KSUCCESS(st)) {
DbgPrint("usb: port %u slot %u descriptor read failed st=0x%x\n",
port, slotId, st);
return st;
}
dev->MaxPacketSize0 = desc.bMaxPacketSize0;
dev->VendorId = desc.idVendor;
dev->ProductId = desc.idProduct;
dev->BcdUsb = desc.bcdUSB;
dev->BcdDevice = desc.bcdDevice;
dev->DeviceClass = desc.bDeviceClass;
dev->DeviceSubclass = desc.bDeviceSubClass;
dev->DeviceProtocol = desc.bDeviceProtocol;
DbgPrint("usb: device port=%u slot=%u speed=%s(%u) vid=%04x pid=%04x class=%02x subclass=%02x proto=%02x mps0=%u configs=%u usb=%04x\n",
port,
slotId,
XhciPortSpeedName(speed),
speed,
desc.idVendor,
desc.idProduct,
desc.bDeviceClass,
desc.bDeviceSubClass,
desc.bDeviceProtocol,
desc.bMaxPacketSize0,
desc.bNumConfigurations,
desc.bcdUSB);
st = XhciDiscoverAndSetConfiguration(xhci, dev, &desc);
if (!KSUCCESS(st)) return st;
return STATUS_SUCCESS;
}
void XhciEnumerateRootPorts(XHCI_CONTROLLER* xhci) {
uint32_t attempted = 0;
uint32_t configured = 0;
for (uint8_t port = 1; port <= xhci->MaxPorts; port++) {
uint32_t portsc = XhciRead32(xhci->Op, XhciPortReg(port));
if (!(portsc & XHCI_PORTSC_CCS))
continue;
attempted++;
KSTATUS st = XhciEnumeratePort(xhci, port);
if (KSUCCESS(st)) configured++;
else DbgPrint("usb: root-port %u enumeration failed st=0x%x\n", port, st);
}
DbgPrint("usb: xHCI root-port enumeration complete; attempted=%u configured=%u\n",
attempted, configured);
}
KSTATUS UsbRegisterClassMatchers(void) {
memset(&gUsbStorageDriver, 0, sizeof(gUsbStorageDriver));
gUsbStorageDriver.Name = "usbstor";
gUsbStorageDriver.MajorFunction[IRP_MJ_READ] = UsbStorageDispatchReadWrite;
gUsbStorageDriver.MajorFunction[IRP_MJ_WRITE] = UsbStorageDispatchReadWrite;
KSTATUS st = IoRegisterDriverMatcher("usbhid", IoBusUsb, UsbHidMatch, UsbHidBind, NULL);
if (!KSUCCESS(st)) return st;
st = IoRegisterDriverMatcher("usbstor", IoBusUsb, UsbStorageMatch, UsbStorageBind, NULL);
if (!KSUCCESS(st)) return st;
return STATUS_SUCCESS;
}
+367
View File
@@ -0,0 +1,367 @@
// usb_hid.c - USB HID boot keyboard/mouse class binding and report handling.
#include "usb.h"
bool XhciHidKeyInReport(const uint8_t* keys, uint8_t key) {
if (key == 0 || key == 1) return false;
for (uint8_t i = 0; i < 6; i++) {
if (keys[i] == key) return true;
}
return false;
}
INPUT_KEY XhciHidModifierKey(uint8_t bit) {
switch (bit) {
case 0: return INPUT_KEY_LEFT_CTRL;
case 1: return INPUT_KEY_LEFT_SHIFT;
case 2: return INPUT_KEY_LEFT_ALT;
case 3: return INPUT_KEY_LEFT_GUI;
case 4: return INPUT_KEY_RIGHT_CTRL;
case 5: return INPUT_KEY_RIGHT_SHIFT;
case 6: return INPUT_KEY_RIGHT_ALT;
case 7: return INPUT_KEY_RIGHT_GUI;
default: return INPUT_KEY_NONE;
}
}
void XhciSubmitHidKey(INPUT_KEY key, bool pressed) {
if (key == INPUT_KEY_NONE) return;
INPUT_KEY_EVENT ev;
ev.Key = key;
ev.Pressed = pressed;
KeInputSubmitKey(&ev);
}
void XhciProcessBootKeyboardReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_t len) {
if (len < 8) return;
uint8_t modifiers = report[0];
for (uint8_t bit = 0; bit < 8; bit++) {
uint8_t mask = (uint8_t)(1u << bit);
bool wasDown = (dev->HidLastModifiers & mask) != 0;
bool isDown = (modifiers & mask) != 0;
if (wasDown != isDown)
XhciSubmitHidKey(XhciHidModifierKey(bit), isDown);
}
const uint8_t* keys = &report[2];
for (uint8_t i = 0; i < 6; i++) {
uint8_t oldKey = dev->HidLastKeys[i];
if (oldKey > 1 && !XhciHidKeyInReport(keys, oldKey))
XhciSubmitHidKey((INPUT_KEY)oldKey, false);
}
for (uint8_t i = 0; i < 6; i++) {
uint8_t newKey = keys[i];
if (newKey > 1 && !XhciHidKeyInReport(dev->HidLastKeys, newKey))
XhciSubmitHidKey((INPUT_KEY)newKey, true);
}
dev->HidLastModifiers = modifiers;
memcpy(dev->HidLastKeys, keys, sizeof(dev->HidLastKeys));
}
void XhciProcessBootMouseReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_t len) {
if (len < 3) return;
int dx = (int)(int8_t)report[1];
int dy = (int)(int8_t)report[2];
int wheel = (len >= 4) ? (int)(int8_t)report[3] : 0;
uint8_t buttons = report[0] & 0x07u;
if (dx || dy || wheel || buttons != dev->HidLastButtons) {
INPUT_MOUSE_EVENT ev;
ev.dx = dx;
ev.dy = dy;
ev.wheel = wheel;
ev.buttons = buttons;
KeInputSubmitMouse(&ev);
}
dev->HidLastButtons = buttons;
}
KSTATUS XhciArmHidInterruptIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
if (!dev->HidActive || dev->HidArmed || !dev->HidRing || !dev->HidBuffer)
return STATUS_SUCCESS;
memset(dev->HidBuffer, 0, dev->HidReportLength);
uint64_t trbPhys = 0;
KSTATUS st = XhciRingAppend(dev->HidRing,
dev->HidRingPhys,
XHCI_HID_RING_TRBS,
&dev->HidEnqueue,
&dev->HidCycle,
dev->HidBufferPhys,
dev->HidReportLength,
(XHCI_TRB_TYPE_NORMAL << XHCI_TRB_TYPE_SHIFT) |
XHCI_TRB_ISP |
XHCI_TRB_IOC,
&trbPhys);
if (!KSUCCESS(st)) return st;
dev->HidLastTrbPhys = trbPhys;
dev->HidArmed = true;
XhciBarrier();
XhciWrite32(xhci->Doorbells, (uint32_t)dev->SlotId * 4u, dev->HidEndpointId);
return STATUS_SUCCESS;
}
void XhciArmAllHid(XHCI_CONTROLLER* xhci) {
for (uint8_t slot = 1; slot <= XHCI_MAX_TRACKED_SLOTS; slot++) {
XHCI_DEVICE* dev = &xhci->Devices[slot];
if (!dev->HidActive) continue;
KSTATUS st = XhciArmHidInterruptIn(xhci, dev);
if (!KSUCCESS(st)) {
DbgPrint("usb: HID arm failed slot=%u ep=%u st=0x%x\n",
dev->SlotId, dev->HidEndpointId, st);
}
}
}
void XhciHandleAsyncTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbPhys,
uint32_t completionCode, uint8_t slotId,
uint8_t endpointId, uint32_t residue) {
if (slotId == 0 || slotId > XHCI_MAX_TRACKED_SLOTS) return;
XHCI_DEVICE* dev = &xhci->Devices[slotId];
if (!dev->HidActive || endpointId != dev->HidEndpointId || trbPhys != dev->HidLastTrbPhys)
return;
dev->HidArmed = false;
if (completionCode != XHCI_CC_SUCCESS) {
DbgPrint("usb: HID transfer failed slot=%u ep=%u cc=%u residue=%u\n",
slotId, endpointId, completionCode, residue);
return;
}
uint16_t length = dev->HidReportLength;
if (residue < length) length = (uint16_t)(length - residue);
if (dev->HidKeyboard)
XhciProcessBootKeyboardReport(dev, dev->HidBuffer, length);
else if (dev->HidMouse)
XhciProcessBootMouseReport(dev, dev->HidBuffer, length);
KSTATUS st = XhciArmHidInterruptIn(xhci, dev);
if (!KSUCCESS(st)) {
DbgPrint("usb: HID re-arm failed slot=%u ep=%u st=0x%x\n",
slotId, endpointId, st);
}
}
KSTATUS XhciStartHidInterrupts(XHCI_CONTROLLER* xhci) {
if (xhci->HidDeviceCount == 0)
return STATUS_SUCCESS;
XhciArmAllHid(xhci);
DbgPrint("usb: HID interrupt input armed devices=%u\n", xhci->HidDeviceCount);
return STATUS_SUCCESS;
}
KSTATUS XhciSetHidBootProtocol(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
USB_SETUP_PACKET setup;
setup.bmRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
setup.bRequest = USB_REQ_SET_PROTOCOL;
setup.wValue = 0; // boot protocol
setup.wIndex = dev->HidInterface;
setup.wLength = 0;
KSTATUS st = XhciControlNoData(xhci, dev, &setup);
if (!KSUCCESS(st)) {
DbgPrint("usb: HID SET_PROTOCOL boot failed slot=%u if=%u st=0x%x\n",
dev->SlotId, dev->HidInterface, st);
return st;
}
setup.bRequest = USB_REQ_SET_IDLE;
setup.wValue = 0;
st = XhciControlNoData(xhci, dev, &setup);
if (!KSUCCESS(st)) {
// SET_IDLE is useful for keyboards but not worth rejecting an otherwise
// usable boot device in the early stack.
DbgPrint("usb: HID SET_IDLE failed slot=%u if=%u st=0x%x; continuing\n",
dev->SlotId, dev->HidInterface, st);
}
DbgPrint("usb: HID boot protocol selected slot=%u if=%u proto=%u\n",
dev->SlotId, dev->HidInterface, dev->HidProtocol);
return STATUS_SUCCESS;
}
KSTATUS XhciAllocateHidMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
if (dev->HidRing && dev->HidBuffer) return STATUS_SUCCESS;
dev->HidRing = (XHCI_TRB*)UsbDmaAllocPages(xhci, 1, &dev->HidRingPhys);
dev->HidBuffer = (uint8_t*)UsbDmaAllocPages(xhci, 1, &dev->HidBufferPhys);
if (!dev->HidRing || !dev->HidBuffer)
return STATUS_NO_MEMORY;
dev->HidEnqueue = 0;
dev->HidCycle = 1;
XHCI_TRB* link = &dev->HidRing[XHCI_HID_RING_TRBS - 1u];
link->ParameterLo = (uint32_t)dev->HidRingPhys;
link->ParameterHi = (uint32_t)(dev->HidRingPhys >> 32);
link->Status = 0;
link->Control = (XHCI_TRB_TYPE_LINK << XHCI_TRB_TYPE_SHIFT) |
XHCI_TRB_LINK_TC |
dev->HidCycle;
memset(dev->HidLastKeys, 0, sizeof(dev->HidLastKeys));
dev->HidLastModifiers = 0;
dev->HidLastButtons = 0;
return STATUS_SUCCESS;
}
KSTATUS XhciConfigureHidEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
if (!dev->HidCandidate)
return STATUS_SUCCESS;
KSTATUS st = XhciAllocateHidMemory(xhci, dev);
if (!KSUCCESS(st)) return st;
uint8_t dci = dev->HidEndpointId;
if (dci < 2 || dci > 31)
return STATUS_INVALID_PARAMETER;
memset(dev->InputContext, 0, PAGE_SIZE);
uint32_t* icc = XhciInputControlContext(xhci, dev);
uint32_t* slot = XhciInputSlotContext(xhci, dev);
uint32_t* ep = XhciInputEndpointContext(xhci, dev, dci);
icc[0] = 0;
icc[1] = (1u << 0) | (1u << dci);
slot[0] = XHCI_CONTEXT_ENTRIES(dci) | XHCI_SLOT_SPEED(dev->Speed);
slot[1] = XHCI_SLOT_ROOT_PORT(dev->Port);
slot[2] = 0;
slot[3] = 0;
uint16_t maxPacket = dev->HidMaxPacket & 0x07FFu;
if (maxPacket == 0) maxPacket = dev->HidKeyboard ? 8u : 4u;
uint8_t interval = XhciInterruptInterval(dev->Speed, dev->HidInterval);
ep[0] = (uint32_t)interval << 16;
ep[1] = XHCI_EP_CERR_3 | XHCI_EP_TYPE_INT_IN | ((uint32_t)maxPacket << 16);
ep[2] = (uint32_t)(dev->HidRingPhys | XHCI_EP_DCS);
ep[3] = (uint32_t)((dev->HidRingPhys | XHCI_EP_DCS) >> 32);
ep[4] = (uint32_t)maxPacket | ((uint32_t)maxPacket << 16);
XHCI_COMMAND_RESULT result;
memset(&result, 0, sizeof(result));
st = XhciSubmitCommand(
xhci,
dev->InputContextPhys,
0,
(XHCI_TRB_TYPE_CONFIGURE_ENDPOINT << XHCI_TRB_TYPE_SHIFT) |
((uint32_t)dev->SlotId << 24),
&result);
if (!KSUCCESS(st)) {
DbgPrint("usb: Configure Endpoint failed slot=%u ep=%u st=0x%x cc=%u\n",
dev->SlotId, dci, st, result.CompletionCode);
return st;
}
dev->HidReportLength = maxPacket;
if (dev->HidKeyboard && dev->HidReportLength < 8) dev->HidReportLength = 8;
if (dev->HidMouse && dev->HidReportLength < 3) dev->HidReportLength = 3;
if (dev->HidReportLength > XHCI_MAX_HID_REPORT) dev->HidReportLength = XHCI_MAX_HID_REPORT;
st = XhciSetHidBootProtocol(xhci, dev);
if (!KSUCCESS(st)) return st;
dev->HidActive = true;
xhci->HidDeviceCount++;
uint32_t inputId = 0;
KeInputRegisterDevice(dev->HidKeyboard ? "usb-hid-keyboard" : "usb-hid-mouse", 0, &inputId);
DbgPrint("usb: HID boot %s bound slot=%u if=%u ep=%02x dci=%u maxpkt=%u interval=%u input=%u\n",
dev->HidKeyboard ? "keyboard" : "mouse",
dev->SlotId,
dev->HidInterface,
dev->HidEndpointAddress,
dci,
maxPacket,
dev->HidInterval,
inputId);
return STATUS_SUCCESS;
}
const IO_PUBLISHED_USB_ENDPOINT* UsbFindEndpoint(const IO_PUBLISHED_DEVICE* pub,
uint8_t endpointType,
bool wantIn) {
if (!pub || pub->BusType != IoBusUsb) return (const IO_PUBLISHED_USB_ENDPOINT*)0;
for (uint8_t i = 0; i < pub->u.Usb.EndpointCount; i++) {
const IO_PUBLISHED_USB_ENDPOINT* ep = &pub->u.Usb.Endpoints[i];
uint8_t type = ep->Attributes & USB_ENDPOINT_ATTR_TYPE_MASK;
bool in = (ep->Address & USB_ENDPOINT_DIR_IN) != 0;
if (type == endpointType && in == wantIn)
return ep;
}
return (const IO_PUBLISHED_USB_ENDPOINT*)0;
}
bool UsbHidMatch(const IO_PUBLISHED_DEVICE* pub, void* context) {
(void)context;
if (!pub || pub->BusType != IoBusUsb)
return false;
if (pub->u.Usb.InterfaceClass != USB_CLASS_HID)
return false;
if (pub->u.Usb.InterfaceSubclass != USB_HID_SUBCLASS_BOOT)
return false;
if (pub->u.Usb.InterfaceProtocol != USB_HID_PROTOCOL_KEYBOARD &&
pub->u.Usb.InterfaceProtocol != USB_HID_PROTOCOL_MOUSE)
return false;
return UsbFindEndpoint(pub, USB_ENDPOINT_TYPE_INTERRUPT, true) != (const IO_PUBLISHED_USB_ENDPOINT*)0;
}
KSTATUS UsbHidBind(IO_PUBLISHED_DEVICE* pub, void* context) {
(void)context;
if (!pub || !pub->BusContext || !pub->DeviceContext)
return STATUS_INVALID_PARAMETER;
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)pub->BusContext;
XHCI_DEVICE* dev = (XHCI_DEVICE*)pub->DeviceContext;
if (dev->HidActive || dev->HidCandidate)
return STATUS_ALREADY_EXISTS;
const IO_PUBLISHED_USB_ENDPOINT* ep = UsbFindEndpoint(pub, USB_ENDPOINT_TYPE_INTERRUPT, true);
if (!ep)
return STATUS_NOT_FOUND;
dev->HidCandidate = true;
dev->HidKeyboard = (pub->u.Usb.InterfaceProtocol == USB_HID_PROTOCOL_KEYBOARD);
dev->HidMouse = (pub->u.Usb.InterfaceProtocol == USB_HID_PROTOCOL_MOUSE);
dev->HidInterface = pub->u.Usb.InterfaceNumber;
dev->HidProtocol = pub->u.Usb.InterfaceProtocol;
dev->HidEndpointAddress = ep->Address;
dev->HidEndpointId = XhciEndpointIdFromAddress(ep->Address);
dev->HidInterval = ep->Interval;
dev->HidMaxPacket = ep->MaxPacketSize;
KSTATUS st = XhciConfigureHidEndpoint(xhci, dev);
if (!KSUCCESS(st)) {
dev->HidCandidate = false;
dev->HidKeyboard = false;
dev->HidMouse = false;
}
return st;
}
+472
View File
@@ -0,0 +1,472 @@
// usb_storage.c - USB mass-storage BOT/SCSI class binding and disk bridge.
#include "usb.h"
DRIVER_OBJECT gUsbStorageDriver;
KSTATUS UsbStorageBulkIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
PHYSICAL_ADDRESS phys, uint32_t length,
uint32_t* outActual) {
return XhciBulkTransfer(xhci,
dev,
dev->MassBulkInEndpointId,
dev->MassInRing,
dev->MassInRingPhys,
&dev->MassInEnqueue,
&dev->MassInCycle,
phys,
length,
outActual);
}
KSTATUS UsbStorageBulkOut(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
PHYSICAL_ADDRESS phys, uint32_t length,
uint32_t* outActual) {
return XhciBulkTransfer(xhci,
dev,
dev->MassBulkOutEndpointId,
dev->MassOutRing,
dev->MassOutRingPhys,
&dev->MassOutEnqueue,
&dev->MassOutCycle,
phys,
length,
outActual);
}
KSTATUS UsbStorageBotCommand(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
const uint8_t* cdb, uint8_t cdbLen,
void* data, PHYSICAL_ADDRESS dataPhys,
uint32_t dataLength, bool dataIn) {
if (!xhci || !dev || !cdb || cdbLen == 0 || cdbLen > 16)
return STATUS_INVALID_PARAMETER;
if (dataLength && (!data || !dataPhys))
return STATUS_INVALID_PARAMETER;
USB_MASS_CBW* cbw = (USB_MASS_CBW*)dev->DescriptorBuffer;
memset(cbw, 0, sizeof(*cbw));
cbw->Signature = USB_MASS_BOT_CBW_SIGNATURE;
cbw->Tag = ++dev->MassTag;
cbw->DataTransferLength = dataLength;
cbw->Flags = dataIn ? USB_DIR_IN : USB_DIR_OUT;
cbw->Lun = 0;
cbw->CommandBlockLength = cdbLen;
memcpy(cbw->CommandBlock, cdb, cdbLen);
uint32_t actual = 0;
KSTATUS st = UsbStorageBulkOut(xhci, dev, dev->DescriptorPhys, sizeof(*cbw), &actual);
if (!KSUCCESS(st) || actual != sizeof(*cbw))
return KSUCCESS(st) ? STATUS_DEVICE_ERROR : st;
if (dataLength) {
actual = 0;
if (dataIn)
st = UsbStorageBulkIn(xhci, dev, dataPhys, dataLength, &actual);
else
st = UsbStorageBulkOut(xhci, dev, dataPhys, dataLength, &actual);
if (!KSUCCESS(st))
return st;
}
USB_MASS_CSW* csw = (USB_MASS_CSW*)dev->DescriptorBuffer;
memset(csw, 0, sizeof(*csw));
actual = 0;
st = UsbStorageBulkIn(xhci, dev, dev->DescriptorPhys, sizeof(*csw), &actual);
if (!KSUCCESS(st) || actual != sizeof(*csw))
return KSUCCESS(st) ? STATUS_DEVICE_ERROR : st;
if (csw->Signature != USB_MASS_BOT_CSW_SIGNATURE || csw->Tag != dev->MassTag) {
DbgPrint("usb-storage: invalid CSW slot=%u sig=%08x tag=%08x expected=%08x\n",
dev->SlotId, csw->Signature, csw->Tag, dev->MassTag);
return STATUS_DEVICE_ERROR;
}
if (csw->Status != USB_MASS_BOT_CSW_PASSED) {
DbgPrint("usb-storage: command %02x failed slot=%u csw_status=%u residue=%u\n",
cdb[0], dev->SlotId, csw->Status, csw->DataResidue);
return STATUS_DEVICE_ERROR;
}
return STATUS_SUCCESS;
}
uint32_t UsbBe32(const uint8_t* p) {
return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) |
((uint32_t)p[2] << 8) | (uint32_t)p[3];
}
void UsbStoreBe32(uint8_t* p, uint32_t v) {
p[0] = (uint8_t)(v >> 24);
p[1] = (uint8_t)(v >> 16);
p[2] = (uint8_t)(v >> 8);
p[3] = (uint8_t)v;
}
void UsbStoreBe16(uint8_t* p, uint16_t v) {
p[0] = (uint8_t)(v >> 8);
p[1] = (uint8_t)v;
}
KSTATUS UsbStorageInquiry(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
uint8_t cdb[6] = { SCSI_INQUIRY, 0, 0, 0, 36, 0 };
memset(dev->MassBuffer, 0, 36);
KSTATUS st = UsbStorageBotCommand(xhci, dev, cdb, sizeof(cdb),
dev->MassBuffer, dev->MassBufferPhys,
36, true);
if (!KSUCCESS(st)) return st;
char vendor[9];
char product[17];
memcpy(vendor, dev->MassBuffer + 8, 8); vendor[8] = 0;
memcpy(product, dev->MassBuffer + 16, 16); product[16] = 0;
DbgPrint("usb-storage: INQUIRY slot=%u vendor='%s' product='%s'\n",
dev->SlotId, vendor, product);
return STATUS_SUCCESS;
}
KSTATUS UsbStorageRequestSense(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
uint8_t rs[6] = { SCSI_REQUEST_SENSE, 0, 0, 0, 18, 0 };
memset(dev->MassBuffer, 0, 18);
return UsbStorageBotCommand(xhci, dev, rs, sizeof(rs),
dev->MassBuffer, dev->MassBufferPhys, 18, true);
}
KSTATUS UsbStorageTestUnitReady(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
uint8_t cdb[6] = { SCSI_TEST_UNIT_READY, 0, 0, 0, 0, 0 };
KSTATUS last = STATUS_DEVICE_ERROR;
for (uint32_t attempt = 0; attempt < 10; attempt++) {
KSTATUS st = UsbStorageBotCommand(xhci, dev, cdb, sizeof(cdb),
(void*)0, 0, 0, false);
if (KSUCCESS(st)) return STATUS_SUCCESS;
last = st;
(void)UsbStorageRequestSense(xhci, dev);
UsbDelayUs(xhci->Hal, 10000u);
}
return last;
}
KSTATUS UsbStorageReadCapacity(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
uint8_t cdb[10] = { SCSI_READ_CAPACITY_10, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
memset(dev->MassBuffer, 0, 8);
KSTATUS st = UsbStorageBotCommand(xhci, dev, cdb, sizeof(cdb),
dev->MassBuffer, dev->MassBufferPhys,
8, true);
if (!KSUCCESS(st)) return st;
uint32_t lastLba = UsbBe32(dev->MassBuffer + 0);
uint32_t blockSize = UsbBe32(dev->MassBuffer + 4);
if (blockSize == 0)
return STATUS_DEVICE_ERROR;
dev->MassBlockSize = blockSize;
dev->MassBlockCount = (uint64_t)lastLba + 1ull;
DbgPrint("usb-storage: capacity slot=%u blocks=%llu block_size=%u (%llu MB)\n",
dev->SlotId,
(unsigned long long)dev->MassBlockCount,
dev->MassBlockSize,
(unsigned long long)((dev->MassBlockCount * (uint64_t)dev->MassBlockSize) / (1024ull * 1024ull)));
return STATUS_SUCCESS;
}
KSTATUS UsbStorageReadWriteSectors(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
bool write, uint64_t lba,
uint32_t sectors, void* buffer) {
if (!xhci || !dev || !buffer || sectors == 0)
return STATUS_INVALID_PARAMETER;
if (dev->MassBlockSize != USB_STORAGE_SECTOR_SIZE)
return STATUS_NOT_SUPPORTED;
if (lba > 0xFFFFFFFFull || sectors > 0xFFFFu)
return STATUS_INVALID_PARAMETER;
uint32_t bytes = sectors * USB_STORAGE_SECTOR_SIZE;
if (bytes > USB_STORAGE_MAX_BYTES)
return STATUS_INVALID_PARAMETER;
uint8_t cdb[10];
memset(cdb, 0, sizeof(cdb));
cdb[0] = write ? SCSI_WRITE_10 : SCSI_READ_10;
UsbStoreBe32(&cdb[2], (uint32_t)lba);
UsbStoreBe16(&cdb[7], (uint16_t)sectors);
if (write)
memcpy(dev->MassBuffer, buffer, bytes);
KSTATUS st = UsbStorageBotCommand(xhci, dev, cdb, sizeof(cdb),
dev->MassBuffer, dev->MassBufferPhys,
bytes, !write);
if (!KSUCCESS(st)) return st;
if (!write)
memcpy(buffer, dev->MassBuffer, bytes);
return STATUS_SUCCESS;
}
void UsbStorageAcquireIo(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
while (_InterlockedCompareExchange(&dev->MassIoBusy, 1, 0) != 0) {
UsbDelayUs(xhci->Hal, 10);
}
}
void UsbStorageReleaseIo(XHCI_DEVICE* dev) {
_InterlockedExchange(&dev->MassIoBusy, 0);
}
KSTATUS UsbStorageDispatchReadWrite(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp || !device->DeviceExtension)
return STATUS_INVALID_PARAMETER;
XHCI_DEVICE* dev = (XHCI_DEVICE*)device->DeviceExtension;
if (!dev->MassActive || dev->MassBlockSize != USB_STORAGE_SECTOR_SIZE) {
IoCompleteRequest(irp, STATUS_DEVICE_ERROR, 0);
return STATUS_PENDING;
}
uint8_t* buffer = (uint8_t*)irp->Buffer;
uint64_t lba = irp->Lba;
uint32_t remaining = irp->SectorCount;
uint32_t done = 0;
bool write = (irp->Flags & IRP_FLAG_WRITE) != 0;
uint32_t maxSectors = USB_STORAGE_MAX_BYTES / USB_STORAGE_SECTOR_SIZE;
KSTATUS st = STATUS_SUCCESS;
UsbStorageAcquireIo(&gXhci, dev);
while (remaining) {
uint32_t chunk = remaining;
if (chunk > maxSectors) chunk = maxSectors;
st = UsbStorageReadWriteSectors(&gXhci, dev, write, lba, chunk,
buffer + (done * USB_STORAGE_SECTOR_SIZE));
if (!KSUCCESS(st))
break;
lba += chunk;
done += chunk;
remaining -= chunk;
}
UsbStorageReleaseIo(dev);
IoCompleteRequest(irp, st, KSUCCESS(st) ? (size_t)done * USB_STORAGE_SECTOR_SIZE : 0);
return STATUS_PENDING;
}
KSTATUS XhciAllocateMassStorageMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
if (dev->MassInRing && dev->MassOutRing && dev->MassBuffer)
return STATUS_SUCCESS;
dev->MassInRing = (XHCI_TRB*)UsbDmaAllocPages(xhci, 1, &dev->MassInRingPhys);
dev->MassOutRing = (XHCI_TRB*)UsbDmaAllocPages(xhci, 1, &dev->MassOutRingPhys);
dev->MassBuffer = (uint8_t*)UsbDmaAllocPages(xhci, USB_STORAGE_DMA_PAGES, &dev->MassBufferPhys);
if (!dev->MassInRing || !dev->MassOutRing || !dev->MassBuffer)
return STATUS_NO_MEMORY;
dev->MassInEnqueue = 0;
dev->MassInCycle = 1;
dev->MassOutEnqueue = 0;
dev->MassOutCycle = 1;
dev->MassTag = 0x1000u + dev->SlotId;
XhciInitialiseTransferRing(dev->MassInRing, dev->MassInRingPhys, XHCI_BULK_RING_TRBS, dev->MassInCycle);
XhciInitialiseTransferRing(dev->MassOutRing, dev->MassOutRingPhys, XHCI_BULK_RING_TRBS, dev->MassOutCycle);
return STATUS_SUCCESS;
}
KSTATUS XhciConfigureMassStorageEndpoints(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
if (!dev->MassCandidate)
return STATUS_SUCCESS;
KSTATUS st = XhciAllocateMassStorageMemory(xhci, dev);
if (!KSUCCESS(st)) return st;
uint8_t inDci = dev->MassBulkInEndpointId;
uint8_t outDci = dev->MassBulkOutEndpointId;
if (inDci < 2 || inDci > 31 || outDci < 2 || outDci > 31)
return STATUS_INVALID_PARAMETER;
memset(dev->InputContext, 0, PAGE_SIZE);
uint32_t* icc = XhciInputControlContext(xhci, dev);
uint32_t* slot = XhciInputSlotContext(xhci, dev);
uint32_t* epIn = XhciInputEndpointContext(xhci, dev, inDci);
uint32_t* epOut = XhciInputEndpointContext(xhci, dev, outDci);
icc[0] = 0;
icc[1] = (1u << 0) | (1u << inDci) | (1u << outDci);
uint8_t maxDci = inDci > outDci ? inDci : outDci;
slot[0] = XHCI_CONTEXT_ENTRIES(maxDci) | XHCI_SLOT_SPEED(dev->Speed);
slot[1] = XHCI_SLOT_ROOT_PORT(dev->Port);
slot[2] = 0;
slot[3] = 0;
uint16_t inMaxPacket = dev->MassBulkInMaxPacket & 0x07FFu;
uint16_t outMaxPacket = dev->MassBulkOutMaxPacket & 0x07FFu;
if (inMaxPacket == 0) inMaxPacket = 512;
if (outMaxPacket == 0) outMaxPacket = 512;
epIn[0] = 0;
epIn[1] = XHCI_EP_CERR_3 | XHCI_EP_TYPE_BULK_IN | ((uint32_t)inMaxPacket << 16);
epIn[2] = (uint32_t)(dev->MassInRingPhys | XHCI_EP_DCS);
epIn[3] = (uint32_t)((dev->MassInRingPhys | XHCI_EP_DCS) >> 32);
epIn[4] = inMaxPacket;
epOut[0] = 0;
epOut[1] = XHCI_EP_CERR_3 | XHCI_EP_TYPE_BULK_OUT | ((uint32_t)outMaxPacket << 16);
epOut[2] = (uint32_t)(dev->MassOutRingPhys | XHCI_EP_DCS);
epOut[3] = (uint32_t)((dev->MassOutRingPhys | XHCI_EP_DCS) >> 32);
epOut[4] = outMaxPacket;
XHCI_COMMAND_RESULT result;
memset(&result, 0, sizeof(result));
st = XhciSubmitCommand(
xhci,
dev->InputContextPhys,
0,
(XHCI_TRB_TYPE_CONFIGURE_ENDPOINT << XHCI_TRB_TYPE_SHIFT) |
((uint32_t)dev->SlotId << 24),
&result);
if (!KSUCCESS(st)) {
DbgPrint("usb-storage: Configure Endpoint failed slot=%u in=%u out=%u st=0x%x cc=%u\n",
dev->SlotId, inDci, outDci, st, result.CompletionCode);
return st;
}
UsbTrace("usb-storage: bulk endpoints configured slot=%u if=%u in=%02x/dci=%u out=%02x/dci=%u\n",
dev->SlotId,
dev->MassInterface,
dev->MassBulkInAddress,
inDci,
dev->MassBulkOutAddress,
outDci);
return STATUS_SUCCESS;
}
uint8_t UsbFindFreeDiskIndex(void) {
for (uint8_t i = 0; i < 8; i++) {
if (!IoDiskDevicePresent(i))
return i;
}
return 0xFFu;
}
KSTATUS UsbStorageRegisterDisk(XHCI_DEVICE* dev) {
if (!dev || dev->MassBlockSize != USB_STORAGE_SECTOR_SIZE || dev->MassBlockCount == 0)
return STATUS_NOT_SUPPORTED;
DEVICE_OBJECT* disk = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreateDevice(&gUsbStorageDriver, 0, &disk);
if (!KSUCCESS(st)) return st;
disk->DeviceExtension = dev;
disk->SectorCount = dev->MassBlockCount;
disk->AlignmentRequirement = USB_STORAGE_SECTOR_SIZE;
disk->Flags |= DEVICE_OBJECT_REMOVABLE;
uint8_t index = UsbFindFreeDiskIndex();
if (index == 0xFFu) {
IoDeleteDevice(disk);
return STATUS_NO_MEMORY;
}
st = IoRegisterDiskDevice(index, disk);
if (!KSUCCESS(st)) {
IoDeleteDevice(disk);
return st;
}
dev->MassDeviceObject = disk;
DbgPrint("usb-storage: registered disk %u slot=%u sectors=%llu\n",
index, dev->SlotId, (unsigned long long)dev->MassBlockCount);
return STATUS_SUCCESS;
}
bool UsbStorageMatch(const IO_PUBLISHED_DEVICE* pub, void* context) {
(void)context;
if (!pub || pub->BusType != IoBusUsb)
return false;
if (pub->u.Usb.InterfaceClass != USB_CLASS_MASS_STORAGE)
return false;
if (pub->u.Usb.InterfaceSubclass != USB_MASS_SUBCLASS_SCSI)
return false;
if (pub->u.Usb.InterfaceProtocol != USB_MASS_PROTOCOL_BOT)
return false;
return UsbFindEndpoint(pub, USB_ENDPOINT_TYPE_BULK, true) &&
UsbFindEndpoint(pub, USB_ENDPOINT_TYPE_BULK, false);
}
KSTATUS UsbStorageBind(IO_PUBLISHED_DEVICE* pub, void* context) {
(void)context;
if (!pub || !pub->BusContext || !pub->DeviceContext)
return STATUS_INVALID_PARAMETER;
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)pub->BusContext;
XHCI_DEVICE* dev = (XHCI_DEVICE*)pub->DeviceContext;
if (dev->MassActive || dev->MassCandidate)
return STATUS_ALREADY_EXISTS;
const IO_PUBLISHED_USB_ENDPOINT* bulkIn = UsbFindEndpoint(pub, USB_ENDPOINT_TYPE_BULK, true);
const IO_PUBLISHED_USB_ENDPOINT* bulkOut = UsbFindEndpoint(pub, USB_ENDPOINT_TYPE_BULK, false);
if (!bulkIn || !bulkOut)
return STATUS_NOT_FOUND;
dev->MassCandidate = true;
dev->MassInterface = pub->u.Usb.InterfaceNumber;
dev->MassBulkInAddress = bulkIn->Address;
dev->MassBulkOutAddress = bulkOut->Address;
dev->MassBulkInEndpointId = XhciEndpointIdFromAddress(bulkIn->Address);
dev->MassBulkOutEndpointId = XhciEndpointIdFromAddress(bulkOut->Address);
dev->MassBulkInMaxPacket = bulkIn->MaxPacketSize;
dev->MassBulkOutMaxPacket = bulkOut->MaxPacketSize;
KSTATUS st = XhciConfigureMassStorageEndpoints(xhci, dev);
if (!KSUCCESS(st)) goto fail;
st = UsbStorageInquiry(xhci, dev);
if (!KSUCCESS(st)) goto fail;
st = UsbStorageTestUnitReady(xhci, dev);
if (!KSUCCESS(st)) {
DbgPrint("usb-storage: TEST UNIT READY failed slot=%u st=0x%x\n", dev->SlotId, st);
goto fail;
}
st = UsbStorageReadCapacity(xhci, dev);
if (!KSUCCESS(st)) goto fail;
if (dev->MassBlockSize != USB_STORAGE_SECTOR_SIZE) {
DbgPrint("usb-storage: unsupported block size slot=%u block_size=%u\n",
dev->SlotId, dev->MassBlockSize);
st = STATUS_NOT_SUPPORTED;
goto fail;
}
dev->MassActive = true;
st = UsbStorageRegisterDisk(dev);
if (!KSUCCESS(st)) goto fail;
DbgPrint("usb-storage: bound slot=%u if=%u\n", dev->SlotId, dev->MassInterface);
return STATUS_SUCCESS;
fail:
dev->MassCandidate = false;
dev->MassActive = false;
DbgPrint("usb-storage: bind failed slot=%u if=%u st=0x%x\n",
dev->SlotId, pub->u.Usb.InterfaceNumber, st);
return st;
}
File diff suppressed because it is too large. Load diff