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:
1 parent
b970d7f59c
commit
b838f8d83d
130 files changed
+8137
-2932
No files matched your search
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -1,9 +1,10 @@
|
||||
[boot]
|
||||
ps2.sys
|
||||
ide.sys
|
||||
ahci.sys
|
||||
usb.sys
|
||||
fat32.sys
|
||||
|
||||
[runtime]
|
||||
lwip.sys
|
||||
usb.sys
|
||||
rtl8139.sys
|
||||
+119
-154
@@ -1,54 +1,5 @@
|
||||
// fat32.c - FAT32 filesystem driver
|
||||
// Read + write support: file data, cluster allocation, directory entries.
|
||||
//
|
||||
// Performance improvements over the original:
|
||||
//
|
||||
// 1. FAT sector cache (FAT_CACHE_SLOTS write-back entries).
|
||||
// FatEntry() and WriteFatEntry() go through the cache; the hot FAT
|
||||
// region stays in RAM instead of being read from disk on every chain
|
||||
// traversal step. A 32-slot cache covers 32 x (512/4) = 4096
|
||||
// consecutive cluster entries — enough to keep the entire FAT of a
|
||||
// small partition in memory, and to avoid repeated reads even on large
|
||||
// ones.
|
||||
//
|
||||
// 2. Fat32ReadDir() cluster buffer cached in FAT32_DIR_CTX.
|
||||
// The original re-read the same cluster from disk on every call
|
||||
// (allocate -> read -> return one entry -> free). Now the buffer is
|
||||
// allocated once in Fat32OpenDir, freed in Fat32CloseDir, and only
|
||||
// re-read when the iteration moves to a new cluster.
|
||||
//
|
||||
// 3. AllocCluster() next-free hint.
|
||||
// The search now starts from the cluster after the last allocation
|
||||
// (volume NextFreeHint) and wraps around once, so repeated allocations
|
||||
// on a fragmented volume don't rescan from sector 0 every time.
|
||||
//
|
||||
// 4. Contiguous-cluster run coalescing in Fat32Read() and Fat32Write().
|
||||
// After fetching a cluster's FAT entry the code peeks ahead: if the
|
||||
// next cluster is physically adjacent (cluster+1) it extends the run
|
||||
// rather than issuing a separate ReadSectors/WriteSectors call. A
|
||||
// freshly-written unfragmented file now transfers in one or a handful
|
||||
// of large DMA bursts instead of SectorsPerCluster-sized slices.
|
||||
//
|
||||
// 5. FAT write batching via a dirty-sector cache.
|
||||
// WriteFatEntry() marks modified FAT cache slots dirty instead of
|
||||
// flushing immediately. FlushFatCache() (called from Fat32Close,
|
||||
// Fat32Truncate, and Fat32Delete) writes each dirty slot once for
|
||||
// every FAT copy, collapsing the 2x(read+write) per-entry cost of the
|
||||
// original into a single write per dirty sector per FAT copy.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_irp.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
// fat32.c - FAT32 filesystem core, directory, cache, and I/O implementation.
|
||||
#include "fat32.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// On-disk structures
|
||||
@@ -1042,7 +993,7 @@ static int FindFreeDirentRun(FAT32_VOL *v, uint32_t dirCluster, uint32_t needed,
|
||||
// ---------------------------------------------------------------------------
|
||||
// Open / Close
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||
KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -1059,7 +1010,7 @@ static KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void Fat32Close(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
void Fat32Close(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
(void)fs;
|
||||
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
|
||||
FlushFatCache(f->Vol);
|
||||
@@ -1079,7 +1030,7 @@ static void Fat32Close(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
|
||||
#define MAX_RUN_SECTORS 128u
|
||||
|
||||
static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
uint64_t offset, void *buf,
|
||||
size_t size, size_t *bytesRead) {
|
||||
(void)fs;
|
||||
@@ -1174,7 +1125,7 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
// Same strategy as read. Whole-cluster-aligned runs skip the read-modify-
|
||||
// write; partial head/tail clusters still require it.
|
||||
|
||||
static KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
uint64_t offset, const void *buf,
|
||||
size_t size, size_t *bytesWritten) {
|
||||
(void)fs;
|
||||
@@ -1279,9 +1230,9 @@ static KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
// ---------------------------------------------------------------------------
|
||||
// Create
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize);
|
||||
KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize);
|
||||
|
||||
static KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||
KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -1385,7 +1336,7 @@ static KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileC
|
||||
// ---------------------------------------------------------------------------
|
||||
// Truncate
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize) {
|
||||
KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize) {
|
||||
(void)fs;
|
||||
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
|
||||
FAT32_VOL *v = f->Vol;
|
||||
@@ -1445,7 +1396,7 @@ update_size:
|
||||
// ---------------------------------------------------------------------------
|
||||
// Delete
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
|
||||
KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
|
||||
if (!fs || !SysSvIsValid(path) || path.Length == 0)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -1467,7 +1418,7 @@ static KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint64_t Fat32GetSize(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
uint64_t Fat32GetSize(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
(void)fs; return ((FAT32_FILE *)fileCtx)->FileSize;
|
||||
}
|
||||
|
||||
@@ -1489,7 +1440,7 @@ typedef struct {
|
||||
uint8_t *ClusterBuf; // allocated once in OpenDir, freed in CloseDir
|
||||
} FAT32_DIR_CTX;
|
||||
|
||||
static KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **dirCtx) {
|
||||
KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **dirCtx) {
|
||||
if (!fs || !dirCtx || !SysSvIsValid(dirPath))
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -1520,7 +1471,7 @@ static KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **d
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS Fat32ReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out) {
|
||||
KSTATUS Fat32ReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out) {
|
||||
(void)fs;
|
||||
FAT32_DIR_CTX *ctx = (FAT32_DIR_CTX *)dirCtx;
|
||||
FAT32_VOL *v = ctx->Vol;
|
||||
@@ -1575,7 +1526,7 @@ static KSTATUS Fat32ReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out) {
|
||||
return STATUS_NO_MORE_FILES;
|
||||
}
|
||||
|
||||
static void Fat32CloseDir(IO_FILESYSTEM *fs, void *dirCtx) {
|
||||
void Fat32CloseDir(IO_FILESYSTEM *fs, void *dirCtx) {
|
||||
(void)fs;
|
||||
FAT32_DIR_CTX *ctx = (FAT32_DIR_CTX *)dirCtx;
|
||||
MmFreePool(ctx->ClusterBuf);
|
||||
@@ -1934,7 +1885,7 @@ static void Fat32AsyncReadSubmitNext(FAT32_ASYNC_RW* ctx) {
|
||||
Fat32AsyncFinish(ctx, s, ctx->Done);
|
||||
}
|
||||
|
||||
static KSTATUS Fat32ReadAsync(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
KSTATUS Fat32ReadAsync(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
uint64_t offset, void *buf, size_t size,
|
||||
IO_FS_ASYNC_COMPLETE complete,
|
||||
void *completeContext) {
|
||||
@@ -2087,7 +2038,7 @@ static void Fat32AsyncWriteSubmitNext(FAT32_ASYNC_RW* ctx) {
|
||||
Fat32AsyncFinish(ctx, s, ctx->Done);
|
||||
}
|
||||
|
||||
static KSTATUS Fat32WriteAsync(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
KSTATUS Fat32WriteAsync(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
uint64_t offset, const void *buf, size_t size,
|
||||
IO_FS_ASYNC_COMPLETE complete,
|
||||
void *completeContext) {
|
||||
@@ -2174,22 +2125,101 @@ static KSTATUS Fat32WriteAsync(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
static FAT32_VOL gVols[FAT32_MAX_VOLUMES];
|
||||
static IO_FILESYSTEM gFs[FAT32_MAX_VOLUMES];
|
||||
|
||||
static void Fat32InitVtable(IO_FILESYSTEM* fs, FAT32_VOL* vol) {
|
||||
memset(fs, 0, sizeof(*fs));
|
||||
fs->Open = Fat32Open;
|
||||
fs->Read = Fat32Read;
|
||||
fs->Write = Fat32Write;
|
||||
fs->ReadAsync = Fat32ReadAsync;
|
||||
fs->WriteAsync = Fat32WriteAsync;
|
||||
fs->Create = Fat32Create;
|
||||
fs->Delete = Fat32Delete;
|
||||
fs->Truncate = Fat32Truncate;
|
||||
fs->GetSize = Fat32GetSize;
|
||||
fs->Close = Fat32Close;
|
||||
fs->DriverCtx = vol;
|
||||
fs->OpenDir = Fat32OpenDir;
|
||||
fs->ReadDir = Fat32ReadDir;
|
||||
fs->CloseDir = Fat32CloseDir;
|
||||
|
||||
static uint32_t gMountedCount;
|
||||
static bool gDiskScanned[FAT32_MAX_VOLUMES];
|
||||
|
||||
static uint32_t Fat32TryMountDisk(uint8_t disk) {
|
||||
if (disk >= FAT32_MAX_VOLUMES)
|
||||
return 0;
|
||||
if (gDiskScanned[disk])
|
||||
return 0;
|
||||
gDiskScanned[disk] = true;
|
||||
|
||||
if (!IoDiskDevicePresent(disk))
|
||||
return 0;
|
||||
|
||||
uint32_t mountedHere = 0;
|
||||
FAT32_PARTITION_CANDIDATE parts[FAT32_MAX_PARTITIONS_PER_DISK];
|
||||
uint32_t partCount = Fat32EnumerateDiskVolumes(disk, parts, FAT32_MAX_PARTITIONS_PER_DISK);
|
||||
if (!partCount) {
|
||||
DbgPrint("FAT32: disk %u: no FAT32 volumes\n", disk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (uint32_t part = 0; part < partCount && gMountedCount < FAT32_MAX_VOLUMES; part++) {
|
||||
uint8_t volumeIndex = (uint8_t)gMountedCount;
|
||||
FAT32_VOL* v = &gVols[volumeIndex];
|
||||
memset(v, 0, sizeof(*v));
|
||||
v->DriveIndex = disk;
|
||||
v->PartitionLba = parts[part].StartLba;
|
||||
v->PartitionSize = parts[part].SectorCount;
|
||||
|
||||
uint8_t sector[512];
|
||||
if (!KSUCCESS(ReadSectors(v, v->PartitionLba, 1, sector))) {
|
||||
DbgPrint("FAT32: disk %u part %u: failed to read VBR\n", disk, part);
|
||||
continue;
|
||||
}
|
||||
|
||||
BPB *bpb = (BPB *)sector;
|
||||
if (!IsFat32Vbr(v, v->PartitionLba)) {
|
||||
DbgPrint("FAT32: disk %u part %u: unsupported FAT variant\n", disk, part);
|
||||
continue;
|
||||
}
|
||||
|
||||
v->BytesPerSector = bpb->BytesPerSector;
|
||||
v->SectorsPerCluster = bpb->SectorsPerCluster;
|
||||
v->BytesPerCluster = bpb->BytesPerSector * bpb->SectorsPerCluster;
|
||||
v->ReservedSectors = bpb->ReservedSectors;
|
||||
v->NumFats = bpb->NumFats;
|
||||
v->FatSize = bpb->FatSize32;
|
||||
v->RootCluster = bpb->RootCluster;
|
||||
v->DataStart = v->PartitionLba + bpb->ReservedSectors + (uint64_t)bpb->NumFats * bpb->FatSize32;
|
||||
v->NextFreeHint = 2;
|
||||
|
||||
uint32_t totalSectors = bpb->TotalSectors16 ? bpb->TotalSectors16 : bpb->TotalSectors32;
|
||||
uint32_t dataSectors = totalSectors - bpb->ReservedSectors - bpb->NumFats * bpb->FatSize32;
|
||||
v->TotalClusters = dataSectors / bpb->SectorsPerCluster;
|
||||
if (v->PartitionSize == 0 || v->PartitionSize > totalSectors)
|
||||
v->PartitionSize = totalSectors;
|
||||
|
||||
Fat32InitVtable(&gFs[volumeIndex], v);
|
||||
|
||||
IO_VOLUME_PARAMS volParams;
|
||||
volParams.DiskIndex = v->DriveIndex;
|
||||
volParams.PartitionLba = v->PartitionLba;
|
||||
volParams.PartitionSize = v->PartitionSize;
|
||||
|
||||
KSTATUS status = IoRegisterFilesystem(volumeIndex, &gFs[volumeIndex], &volParams);
|
||||
if (KSUCCESS(status)) {
|
||||
gMountedCount++;
|
||||
mountedHere++;
|
||||
DbgPrint("FAT32: disk %u part %u mounted as drive %u (lba=%llu clusters=%u).\n",
|
||||
disk, part, volumeIndex, (unsigned long long)v->PartitionLba, v->TotalClusters);
|
||||
} else {
|
||||
DbgPrint("FAT32: disk %u part %u mount as drive %u failed status=%d.\n",
|
||||
disk, part, volumeIndex, status);
|
||||
}
|
||||
}
|
||||
|
||||
if (partCount && !mountedHere && gMountedCount >= FAT32_MAX_VOLUMES) {
|
||||
DbgPrint("FAT32: disk %u: volume table full\n", disk);
|
||||
}
|
||||
|
||||
return mountedHere;
|
||||
}
|
||||
|
||||
static void Fat32DiskArrival(uint8_t driveIndex, DEVICE_OBJECT* device, void* context) {
|
||||
(void)device;
|
||||
(void)context;
|
||||
uint32_t before = gMountedCount;
|
||||
uint32_t mounted = Fat32TryMountDisk(driveIndex);
|
||||
if (mounted) {
|
||||
DbgPrint("FAT32: hotplug scan mounted %u new volume(s); total=%u\n",
|
||||
mounted, gMountedCount);
|
||||
} else if (gMountedCount != before) {
|
||||
DbgPrint("FAT32: hotplug scan total=%u\n", gMountedCount);
|
||||
}
|
||||
}
|
||||
|
||||
KSTATUS DriverEntry(const HAL_API *hal) {
|
||||
@@ -2198,84 +2228,19 @@ KSTATUS DriverEntry(const HAL_API *hal) {
|
||||
|
||||
FatCacheInvalidate();
|
||||
(void)hal;
|
||||
gMountedCount = 0;
|
||||
memset(gDiskScanned, 0, sizeof(gDiskScanned));
|
||||
|
||||
uint32_t mounted = 0;
|
||||
KSTATUS lastStatus = STATUS_NOT_SUPPORTED;
|
||||
|
||||
for (uint8_t disk = 0; disk < FAT32_MAX_VOLUMES && mounted < FAT32_MAX_VOLUMES; disk++) {
|
||||
if (!IoDiskDevicePresent(disk))
|
||||
continue;
|
||||
|
||||
FAT32_PARTITION_CANDIDATE parts[FAT32_MAX_PARTITIONS_PER_DISK];
|
||||
uint32_t partCount = Fat32EnumerateDiskVolumes(disk, parts, FAT32_MAX_PARTITIONS_PER_DISK);
|
||||
if (!partCount) {
|
||||
DbgPrint("FAT32: disk %u: no FAT32 volumes\n", disk);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t part = 0; part < partCount && mounted < FAT32_MAX_VOLUMES; part++) {
|
||||
uint8_t volumeIndex = (uint8_t)mounted;
|
||||
FAT32_VOL* v = &gVols[volumeIndex];
|
||||
memset(v, 0, sizeof(*v));
|
||||
v->DriveIndex = disk;
|
||||
v->PartitionLba = parts[part].StartLba;
|
||||
v->PartitionSize = parts[part].SectorCount;
|
||||
|
||||
uint8_t sector[512];
|
||||
if (!KSUCCESS(ReadSectors(v, v->PartitionLba, 1, sector))) {
|
||||
lastStatus = STATUS_DEVICE_ERROR;
|
||||
DbgPrint("FAT32: disk %u part %u: failed to read VBR\n", disk, part);
|
||||
continue;
|
||||
}
|
||||
|
||||
BPB *bpb = (BPB *)sector;
|
||||
if (!IsFat32Vbr(v, v->PartitionLba)) {
|
||||
lastStatus = STATUS_NOT_SUPPORTED;
|
||||
DbgPrint("FAT32: disk %u part %u: unsupported FAT variant\n", disk, part);
|
||||
continue;
|
||||
}
|
||||
|
||||
v->BytesPerSector = bpb->BytesPerSector;
|
||||
v->SectorsPerCluster = bpb->SectorsPerCluster;
|
||||
v->BytesPerCluster = bpb->BytesPerSector * bpb->SectorsPerCluster;
|
||||
v->ReservedSectors = bpb->ReservedSectors;
|
||||
v->NumFats = bpb->NumFats;
|
||||
v->FatSize = bpb->FatSize32;
|
||||
v->RootCluster = bpb->RootCluster;
|
||||
v->DataStart = v->PartitionLba + bpb->ReservedSectors + (uint64_t)bpb->NumFats * bpb->FatSize32;
|
||||
v->NextFreeHint = 2;
|
||||
|
||||
uint32_t totalSectors = bpb->TotalSectors16 ? bpb->TotalSectors16 : bpb->TotalSectors32;
|
||||
uint32_t dataSectors = totalSectors - bpb->ReservedSectors - bpb->NumFats * bpb->FatSize32;
|
||||
v->TotalClusters = dataSectors / bpb->SectorsPerCluster;
|
||||
if (v->PartitionSize == 0 || v->PartitionSize > totalSectors)
|
||||
v->PartitionSize = totalSectors;
|
||||
|
||||
Fat32InitVtable(&gFs[volumeIndex], v);
|
||||
|
||||
IO_VOLUME_PARAMS volParams;
|
||||
volParams.DiskIndex = v->DriveIndex;
|
||||
volParams.PartitionLba = v->PartitionLba;
|
||||
volParams.PartitionSize = v->PartitionSize;
|
||||
|
||||
KSTATUS status = IoRegisterFilesystem(volumeIndex, &gFs[volumeIndex], &volParams);
|
||||
if (KSUCCESS(status)) {
|
||||
mounted++;
|
||||
DbgPrint("FAT32: disk %u part %u mounted as drive %u (lba=%llu clusters=%u).\n",
|
||||
disk, part, volumeIndex, (unsigned long long)v->PartitionLba, v->TotalClusters);
|
||||
} else {
|
||||
lastStatus = status;
|
||||
DbgPrint("FAT32: disk %u part %u mount as drive %u failed status=%d.\n",
|
||||
disk, part, volumeIndex, status);
|
||||
}
|
||||
}
|
||||
KSTATUS cbStatus = IoRegisterDiskArrivalCallback(Fat32DiskArrival, (void*)0);
|
||||
if (!KSUCCESS(cbStatus)) {
|
||||
DbgPrint("FAT32: disk-arrival callback registration failed st=0x%x\n", cbStatus);
|
||||
}
|
||||
|
||||
if (!mounted) {
|
||||
if (!gMountedCount) {
|
||||
DbgPrint("FAT32: no mountable FAT32 volumes found.\n");
|
||||
return lastStatus;
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
DbgPrint("FAT32: Init complete; mounted %u volume(s).\n", mounted);
|
||||
DbgPrint("FAT32: Init complete; mounted %u volume(s).\n", gMountedCount);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// fat32.h - shared FAT32 driver internals.
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_irp.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
|
||||
typedef struct FAT32_VOL_S FAT32_VOL;
|
||||
|
||||
KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx);
|
||||
void Fat32Close(IO_FILESYSTEM *fs, void *fileCtx);
|
||||
KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, void *buf, size_t size, size_t *bytesRead);
|
||||
KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, const void *buf, size_t size, size_t *bytesWritten);
|
||||
KSTATUS Fat32ReadAsync(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, void *buf, size_t size, IO_FS_ASYNC_COMPLETE complete, void *completeContext);
|
||||
KSTATUS Fat32WriteAsync(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, const void *buf, size_t size, IO_FS_ASYNC_COMPLETE complete, void *completeContext);
|
||||
KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx);
|
||||
KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path);
|
||||
KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize);
|
||||
uint64_t Fat32GetSize(IO_FILESYSTEM *fs, void *fileCtx);
|
||||
KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **dirCtx);
|
||||
KSTATUS Fat32ReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out);
|
||||
void Fat32CloseDir(IO_FILESYSTEM *fs, void *dirCtx);
|
||||
|
||||
void Fat32InitVtable(IO_FILESYSTEM* fs, FAT32_VOL* vol);
|
||||
@@ -0,0 +1,20 @@
|
||||
// fat32_mount.c - filesystem vtable/mount wiring.
|
||||
#include "fat32.h"
|
||||
|
||||
void Fat32InitVtable(IO_FILESYSTEM* fs, FAT32_VOL* vol) {
|
||||
memset(fs, 0, sizeof(*fs));
|
||||
fs->Open = Fat32Open;
|
||||
fs->Read = Fat32Read;
|
||||
fs->Write = Fat32Write;
|
||||
fs->ReadAsync = Fat32ReadAsync;
|
||||
fs->WriteAsync = Fat32WriteAsync;
|
||||
fs->Create = Fat32Create;
|
||||
fs->Delete = Fat32Delete;
|
||||
fs->Truncate = Fat32Truncate;
|
||||
fs->GetSize = Fat32GetSize;
|
||||
fs->Close = Fat32Close;
|
||||
fs->DriverCtx = vol;
|
||||
fs->OpenDir = Fat32OpenDir;
|
||||
fs->ReadDir = Fat32ReadDir;
|
||||
fs->CloseDir = Fat32CloseDir;
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
// ps2.c - PS/2 keyboard/mouse event decoding and kernel input binding.
|
||||
#include "ps2.h"
|
||||
|
||||
static KDPC gMouseDpc;
|
||||
static volatile bool gKeyboardNextExtended;
|
||||
static volatile uint8_t gMousePacket[3];
|
||||
static volatile int gMousePacketIndex;
|
||||
static PS2_MOUSE_PACKET gMouseDpcQueue[MOUSE_DPC_QUEUE_SIZE];
|
||||
static volatile int gMouseDpcHead;
|
||||
static volatile int gMouseDpcTail;
|
||||
|
||||
static INPUT_KEY Ps2Set1ToKey(uint8_t make, bool extended) {
|
||||
if (extended) {
|
||||
switch (make) {
|
||||
case 0x1C: return INPUT_KEY_KP_ENTER;
|
||||
case 0x1D: return INPUT_KEY_RIGHT_CTRL;
|
||||
case 0x35: return INPUT_KEY_KP_DIVIDE;
|
||||
case 0x38: return INPUT_KEY_RIGHT_ALT;
|
||||
case 0x47: return INPUT_KEY_HOME;
|
||||
case 0x48: return INPUT_KEY_UP;
|
||||
case 0x49: return INPUT_KEY_PAGE_UP;
|
||||
case 0x4B: return INPUT_KEY_LEFT;
|
||||
case 0x4D: return INPUT_KEY_RIGHT;
|
||||
case 0x4F: return INPUT_KEY_END;
|
||||
case 0x50: return INPUT_KEY_DOWN;
|
||||
case 0x51: return INPUT_KEY_PAGE_DOWN;
|
||||
case 0x52: return INPUT_KEY_INSERT;
|
||||
case 0x53: return INPUT_KEY_DELETE;
|
||||
default: return INPUT_KEY_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
switch (make) {
|
||||
case 0x01: return INPUT_KEY_ESCAPE;
|
||||
case 0x02: return INPUT_KEY_1;
|
||||
case 0x03: return INPUT_KEY_2;
|
||||
case 0x04: return INPUT_KEY_3;
|
||||
case 0x05: return INPUT_KEY_4;
|
||||
case 0x06: return INPUT_KEY_5;
|
||||
case 0x07: return INPUT_KEY_6;
|
||||
case 0x08: return INPUT_KEY_7;
|
||||
case 0x09: return INPUT_KEY_8;
|
||||
case 0x0A: return INPUT_KEY_9;
|
||||
case 0x0B: return INPUT_KEY_0;
|
||||
case 0x0C: return INPUT_KEY_MINUS;
|
||||
case 0x0D: return INPUT_KEY_EQUAL;
|
||||
case 0x0E: return INPUT_KEY_BACKSPACE;
|
||||
case 0x0F: return INPUT_KEY_TAB;
|
||||
case 0x10: return INPUT_KEY_Q;
|
||||
case 0x11: return INPUT_KEY_W;
|
||||
case 0x12: return INPUT_KEY_E;
|
||||
case 0x13: return INPUT_KEY_R;
|
||||
case 0x14: return INPUT_KEY_T;
|
||||
case 0x15: return INPUT_KEY_Y;
|
||||
case 0x16: return INPUT_KEY_U;
|
||||
case 0x17: return INPUT_KEY_I;
|
||||
case 0x18: return INPUT_KEY_O;
|
||||
case 0x19: return INPUT_KEY_P;
|
||||
case 0x1A: return INPUT_KEY_LEFT_BRACKET;
|
||||
case 0x1B: return INPUT_KEY_RIGHT_BRACKET;
|
||||
case 0x1C: return INPUT_KEY_ENTER;
|
||||
case 0x1D: return INPUT_KEY_LEFT_CTRL;
|
||||
case 0x1E: return INPUT_KEY_A;
|
||||
case 0x1F: return INPUT_KEY_S;
|
||||
case 0x20: return INPUT_KEY_D;
|
||||
case 0x21: return INPUT_KEY_F;
|
||||
case 0x22: return INPUT_KEY_G;
|
||||
case 0x23: return INPUT_KEY_H;
|
||||
case 0x24: return INPUT_KEY_J;
|
||||
case 0x25: return INPUT_KEY_K;
|
||||
case 0x26: return INPUT_KEY_L;
|
||||
case 0x27: return INPUT_KEY_SEMICOLON;
|
||||
case 0x28: return INPUT_KEY_APOSTROPHE;
|
||||
case 0x29: return INPUT_KEY_GRAVE;
|
||||
case 0x2A: return INPUT_KEY_LEFT_SHIFT;
|
||||
case 0x2B: return INPUT_KEY_BACKSLASH;
|
||||
case 0x2C: return INPUT_KEY_Z;
|
||||
case 0x2D: return INPUT_KEY_X;
|
||||
case 0x2E: return INPUT_KEY_C;
|
||||
case 0x2F: return INPUT_KEY_V;
|
||||
case 0x30: return INPUT_KEY_B;
|
||||
case 0x31: return INPUT_KEY_N;
|
||||
case 0x32: return INPUT_KEY_M;
|
||||
case 0x33: return INPUT_KEY_COMMA;
|
||||
case 0x34: return INPUT_KEY_PERIOD;
|
||||
case 0x35: return INPUT_KEY_SLASH;
|
||||
case 0x36: return INPUT_KEY_RIGHT_SHIFT;
|
||||
case 0x37: return INPUT_KEY_KP_MULTIPLY;
|
||||
case 0x38: return INPUT_KEY_LEFT_ALT;
|
||||
case 0x39: return INPUT_KEY_SPACE;
|
||||
case 0x3A: return INPUT_KEY_CAPS_LOCK;
|
||||
case 0x3B: return INPUT_KEY_F1;
|
||||
case 0x3C: return INPUT_KEY_F2;
|
||||
case 0x3D: return INPUT_KEY_F3;
|
||||
case 0x3E: return INPUT_KEY_F4;
|
||||
case 0x3F: return INPUT_KEY_F5;
|
||||
case 0x40: return INPUT_KEY_F6;
|
||||
case 0x41: return INPUT_KEY_F7;
|
||||
case 0x42: return INPUT_KEY_F8;
|
||||
case 0x43: return INPUT_KEY_F9;
|
||||
case 0x44: return INPUT_KEY_F10;
|
||||
case 0x45: return INPUT_KEY_NUM_LOCK;
|
||||
case 0x47: return INPUT_KEY_KP_7;
|
||||
case 0x48: return INPUT_KEY_KP_8;
|
||||
case 0x49: return INPUT_KEY_KP_9;
|
||||
case 0x4A: return INPUT_KEY_KP_MINUS;
|
||||
case 0x4B: return INPUT_KEY_KP_4;
|
||||
case 0x4C: return INPUT_KEY_KP_5;
|
||||
case 0x4D: return INPUT_KEY_KP_6;
|
||||
case 0x4E: return INPUT_KEY_KP_PLUS;
|
||||
case 0x4F: return INPUT_KEY_KP_1;
|
||||
case 0x50: return INPUT_KEY_KP_2;
|
||||
case 0x51: return INPUT_KEY_KP_3;
|
||||
case 0x52: return INPUT_KEY_KP_0;
|
||||
case 0x53: return INPUT_KEY_KP_PERIOD;
|
||||
case 0x57: return INPUT_KEY_F11;
|
||||
case 0x58: return INPUT_KEY_F12;
|
||||
default: return INPUT_KEY_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static void Ps2KeyboardIsr(void) {
|
||||
uint8_t sc = __inbyte(PS2_DATA);
|
||||
if (sc == SC1_EXT_PREFIX) {
|
||||
gKeyboardNextExtended = true;
|
||||
return;
|
||||
}
|
||||
|
||||
bool extended = gKeyboardNextExtended;
|
||||
bool released = (sc & SC1_BREAK_BIT) != 0;
|
||||
uint8_t make = sc & SC1_MAKE_MASK;
|
||||
gKeyboardNextExtended = false;
|
||||
|
||||
INPUT_KEY key = Ps2Set1ToKey(make, extended);
|
||||
if (key == INPUT_KEY_NONE) return;
|
||||
|
||||
INPUT_KEY_EVENT event = {
|
||||
.Key = key,
|
||||
.Pressed = !released,
|
||||
};
|
||||
KeInputSubmitKey(&event);
|
||||
}
|
||||
|
||||
static void Ps2MouseDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) {
|
||||
(void)dpc;
|
||||
(void)context;
|
||||
(void)systemArgument1;
|
||||
(void)systemArgument2;
|
||||
|
||||
while (gMouseDpcHead != gMouseDpcTail) {
|
||||
PS2_MOUSE_PACKET packet = gMouseDpcQueue[gMouseDpcHead];
|
||||
gMouseDpcHead = (gMouseDpcHead + 1) & (int)MOUSE_DPC_QUEUE_MASK;
|
||||
|
||||
INPUT_MOUSE_EVENT event = {
|
||||
.dx = packet.dx,
|
||||
.dy = packet.dy,
|
||||
.wheel = 0,
|
||||
.buttons = packet.buttons,
|
||||
};
|
||||
KeInputSubmitMouse(&event);
|
||||
}
|
||||
}
|
||||
|
||||
static void Ps2MouseIsr(void) {
|
||||
uint8_t data = __inbyte(PS2_DATA);
|
||||
|
||||
if (gMousePacketIndex == 0 && !(data & 0x08u))
|
||||
return;
|
||||
|
||||
gMousePacket[gMousePacketIndex++] = data;
|
||||
if (gMousePacketIndex < 3)
|
||||
return;
|
||||
gMousePacketIndex = 0;
|
||||
|
||||
uint8_t flags = gMousePacket[0];
|
||||
if (flags & 0xC0u)
|
||||
return;
|
||||
|
||||
int dx = (int)gMousePacket[1] - ((flags & 0x10u) ? 256 : 0);
|
||||
int dy = (int)gMousePacket[2] - ((flags & 0x20u) ? 256 : 0);
|
||||
dy = -dy;
|
||||
|
||||
int next = (gMouseDpcTail + 1) & (int)MOUSE_DPC_QUEUE_MASK;
|
||||
if (next != gMouseDpcHead) {
|
||||
gMouseDpcQueue[gMouseDpcTail].dx = dx;
|
||||
gMouseDpcQueue[gMouseDpcTail].dy = dy;
|
||||
gMouseDpcQueue[gMouseDpcTail].buttons = flags & 0x07u;
|
||||
gMouseDpcTail = next;
|
||||
KeInsertQueueDpc(&gMouseDpc, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void Ps2ControllerPrepare(void) {
|
||||
Ps2WriteCommand(0xADu); // disable keyboard port
|
||||
Ps2WriteCommand(0xA7u); // disable aux port
|
||||
Ps2FlushOutput();
|
||||
|
||||
uint8_t commandByte = Ps2ReadCommandByte();
|
||||
commandByte &= (uint8_t)~0x01u; // keep keyboard IRQ disabled during polled init
|
||||
commandByte &= (uint8_t)~0x02u; // keep mouse IRQ disabled during polled init
|
||||
commandByte |= 0x40u; // translate keyboard stream to set 1
|
||||
commandByte &= (uint8_t)~0x10u; // keyboard clock enabled
|
||||
commandByte &= (uint8_t)~0x20u; // mouse clock enabled
|
||||
Ps2WriteCommandByte(commandByte);
|
||||
|
||||
Ps2WriteCommand(0xAEu); // enable keyboard port
|
||||
Ps2WriteCommand(0xA8u); // enable aux port
|
||||
Ps2FlushOutput();
|
||||
}
|
||||
|
||||
static void Ps2ControllerEnableInterrupts(void) {
|
||||
uint8_t commandByte = Ps2ReadCommandByte();
|
||||
commandByte |= 0x01u;
|
||||
commandByte |= 0x02u;
|
||||
Ps2WriteCommandByte(commandByte);
|
||||
}
|
||||
|
||||
static void Ps2KeyboardInitialize(void) {
|
||||
// Enable scanning. If the keyboard is absent, the timeout/ACK failure is
|
||||
// non-fatal; the IRQ path simply never produces events.
|
||||
if (!Ps2SendKeyboard(0xF4u))
|
||||
DbgPrint("ps2: keyboard enable did not ACK\n");
|
||||
}
|
||||
|
||||
static void Ps2MouseInitialize(void) {
|
||||
if (!Ps2SendMouse(0xFFu)) {
|
||||
DbgPrint("ps2: mouse reset did not ACK\n");
|
||||
return;
|
||||
}
|
||||
|
||||
(void)Ps2ReadData(); // BAT completion, usually 0xAA
|
||||
(void)Ps2ReadData(); // device id, usually 0x00
|
||||
|
||||
if (!Ps2SendMouse(0xF6u))
|
||||
DbgPrint("ps2: mouse set-defaults did not ACK\n");
|
||||
if (!Ps2SendMouse(0xF4u))
|
||||
DbgPrint("ps2: mouse enable did not ACK\n");
|
||||
}
|
||||
|
||||
KSTATUS DriverEntry(const HAL_API* hal) {
|
||||
(void)hal;
|
||||
|
||||
uint32_t keyboardId = 0, mouseId = 0;
|
||||
KeInputRegisterDevice("ps2-keyboard", 0, &keyboardId);
|
||||
KeInputRegisterDevice("ps2-mouse", 0, &mouseId);
|
||||
|
||||
KeInitializeDpc(&gMouseDpc, Ps2MouseDpcRoutine, 0);
|
||||
|
||||
// Keep 8042 IRQ generation disabled while doing polled controller/device
|
||||
// setup. The old ordering routed IRQ1/IRQ12 first, which let ACK/BAT bytes
|
||||
// race the ISR and get consumed as fake key/mouse input before the polled
|
||||
// init path could read them.
|
||||
Ps2ControllerPrepare();
|
||||
Ps2KeyboardInitialize();
|
||||
Ps2MouseInitialize();
|
||||
|
||||
KeIrqRegister(PS2_IRQ_KEYBOARD, Ps2KeyboardIsr);
|
||||
KeIrqRegister(PS2_IRQ_MOUSE, Ps2MouseIsr);
|
||||
Ps2ControllerEnableInterrupts();
|
||||
|
||||
DbgPrint("ps2: initialized keyboard=%u mouse=%u\n", keyboardId, mouseId);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// ps2.h - shared i8042/PS/2 driver internals.
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <intrin.h>
|
||||
|
||||
#include <ntlite/status.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <ntlite/ddi/input_device.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
|
||||
#define PS2_DATA 0x60u
|
||||
#define PS2_STAT 0x64u
|
||||
#define PS2_CMD 0x64u
|
||||
|
||||
#define PS2_STAT_OUT_FULL 0x01u
|
||||
#define PS2_STAT_IN_FULL 0x02u
|
||||
|
||||
#define PS2_IRQ_KEYBOARD 1u
|
||||
#define PS2_IRQ_MOUSE 12u
|
||||
|
||||
#define SC1_BREAK_BIT 0x80u
|
||||
#define SC1_MAKE_MASK 0x7Fu
|
||||
#define SC1_EXT_PREFIX 0xE0u
|
||||
|
||||
#define MOUSE_DPC_QUEUE_SIZE 64u
|
||||
#define MOUSE_DPC_QUEUE_MASK (MOUSE_DPC_QUEUE_SIZE - 1u)
|
||||
|
||||
typedef struct PS2_MOUSE_PACKET {
|
||||
int dx;
|
||||
int dy;
|
||||
uint8_t buttons;
|
||||
} PS2_MOUSE_PACKET;
|
||||
|
||||
void Ps2Pause(void);
|
||||
void Ps2WaitInput(void);
|
||||
bool Ps2WaitOutput(void);
|
||||
void Ps2FlushOutput(void);
|
||||
uint8_t Ps2ReadData(void);
|
||||
void Ps2WriteCommand(uint8_t command);
|
||||
void Ps2WriteData(uint8_t data);
|
||||
uint8_t Ps2ReadCommandByte(void);
|
||||
void Ps2WriteCommandByte(uint8_t commandByte);
|
||||
bool Ps2SendKeyboard(uint8_t byte);
|
||||
bool Ps2SendMouse(uint8_t byte);
|
||||
@@ -0,0 +1,64 @@
|
||||
// ps2_i8042.c - 8042 controller and device command helpers.
|
||||
#include "ps2.h"
|
||||
|
||||
void Ps2Pause(void) {
|
||||
_mm_pause();
|
||||
}
|
||||
|
||||
void Ps2WaitInput(void) {
|
||||
int timeout = 100000;
|
||||
while ((__inbyte(PS2_STAT) & PS2_STAT_IN_FULL) && --timeout) {
|
||||
Ps2Pause();
|
||||
}
|
||||
}
|
||||
|
||||
bool Ps2WaitOutput(void) {
|
||||
int timeout = 5000000;
|
||||
while (!(__inbyte(PS2_STAT) & PS2_STAT_OUT_FULL) && --timeout) {
|
||||
Ps2Pause();
|
||||
}
|
||||
return timeout != 0;
|
||||
}
|
||||
|
||||
void Ps2FlushOutput(void) {
|
||||
int timeout = 100000;
|
||||
while ((__inbyte(PS2_STAT) & PS2_STAT_OUT_FULL) && --timeout) {
|
||||
(void)__inbyte(PS2_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Ps2ReadData(void) {
|
||||
if (!Ps2WaitOutput()) return 0xFFu;
|
||||
return __inbyte(PS2_DATA);
|
||||
}
|
||||
|
||||
void Ps2WriteCommand(uint8_t command) {
|
||||
Ps2WaitInput();
|
||||
__outbyte(PS2_CMD, command);
|
||||
}
|
||||
|
||||
void Ps2WriteData(uint8_t data) {
|
||||
Ps2WaitInput();
|
||||
__outbyte(PS2_DATA, data);
|
||||
}
|
||||
|
||||
uint8_t Ps2ReadCommandByte(void) {
|
||||
Ps2WriteCommand(0x20u);
|
||||
return Ps2ReadData();
|
||||
}
|
||||
|
||||
void Ps2WriteCommandByte(uint8_t commandByte) {
|
||||
Ps2WriteCommand(0x60u);
|
||||
Ps2WriteData(commandByte);
|
||||
}
|
||||
|
||||
bool Ps2SendKeyboard(uint8_t byte) {
|
||||
Ps2WriteData(byte);
|
||||
return Ps2ReadData() == 0xFAu;
|
||||
}
|
||||
|
||||
bool Ps2SendMouse(uint8_t byte) {
|
||||
Ps2WriteCommand(0xD4u);
|
||||
Ps2WriteData(byte);
|
||||
return Ps2ReadData() == 0xFAu;
|
||||
}
|
||||
@@ -4,7 +4,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
|
||||
#pragma intrinsic(_disable)
|
||||
#pragma intrinsic(_enable)
|
||||
#include <lwip/opt.h>
|
||||
#include <lwip/sys.h>
|
||||
#include <lwip/err.h>
|
||||
|
||||
@@ -1,150 +1,9 @@
|
||||
// rtl8139.c - Realtek RTL8139 / QEMU rtl8139 PCI fast Ethernet driver.
|
||||
//
|
||||
// Scope for this first version:
|
||||
// * PCI ID 10ec:8139, legacy INTx interrupts.
|
||||
// * MMIO preferred, I/O BAR fallback.
|
||||
// * Classic RTL8139 register interface: one RX ring buffer and four TX slots.
|
||||
// * No checksum offload, C+ mode, VLAN, multicast hash programming, or power mgmt.
|
||||
//
|
||||
// This is intentionally a separate mini-driver from rtl816x.sys. RTL8139 and
|
||||
// RTL8111/8168 share a vendor but not a useful packet-DMA programming model.
|
||||
// rtl8139.c - packet I/O, interrupt, and DriverEntry path.
|
||||
#include "rtl8139.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <ntlite/status.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <ntlite/ddi/net_device.h>
|
||||
|
||||
#define RTL8139_VENDOR_REALTEK 0x10ECu
|
||||
#define RTL8139_DEVICE_ID 0x8139u
|
||||
|
||||
#define PCI_REG_COMMAND 0x04u
|
||||
#define PCI_REG_BAR0 0x10u
|
||||
#define PCI_COMMAND_IO (1u << 0)
|
||||
#define PCI_COMMAND_MEMORY (1u << 1)
|
||||
#define PCI_COMMAND_MASTER (1u << 2)
|
||||
#define PCI_COMMAND_INTX_DISABLE (1u << 10)
|
||||
|
||||
#define RTL8139_REG_IDR0 0x00u
|
||||
#define RTL8139_REG_MAR0 0x08u
|
||||
#define RTL8139_REG_TSD0 0x10u
|
||||
#define RTL8139_REG_TSAD0 0x20u
|
||||
#define RTL8139_REG_RBSTART 0x30u
|
||||
#define RTL8139_REG_CMD 0x37u
|
||||
#define RTL8139_REG_CAPR 0x38u
|
||||
#define RTL8139_REG_CBR 0x3Au
|
||||
#define RTL8139_REG_IMR 0x3Cu
|
||||
#define RTL8139_REG_ISR 0x3Eu
|
||||
#define RTL8139_REG_TCR 0x40u
|
||||
#define RTL8139_REG_RCR 0x44u
|
||||
#define RTL8139_REG_CONFIG1 0x52u
|
||||
|
||||
#define RTL8139_CMD_BUFE 0x01u
|
||||
#define RTL8139_CMD_TE 0x04u
|
||||
#define RTL8139_CMD_RE 0x08u
|
||||
#define RTL8139_CMD_RST 0x10u
|
||||
|
||||
#define RTL8139_ISR_ROK 0x0001u
|
||||
#define RTL8139_ISR_RER 0x0002u
|
||||
#define RTL8139_ISR_TOK 0x0004u
|
||||
#define RTL8139_ISR_TER 0x0008u
|
||||
#define RTL8139_ISR_RXOVW 0x0010u
|
||||
#define RTL8139_ISR_PUN 0x0020u
|
||||
#define RTL8139_ISR_FOVW 0x0040u
|
||||
#define RTL8139_ISR_LENCHG 0x2000u
|
||||
#define RTL8139_ISR_TIMEOUT 0x4000u
|
||||
#define RTL8139_ISR_SYSERR 0x8000u
|
||||
|
||||
#define RTL8139_INTR_ENABLES (RTL8139_ISR_ROK | RTL8139_ISR_RER | RTL8139_ISR_TOK | \
|
||||
RTL8139_ISR_TER | RTL8139_ISR_RXOVW | RTL8139_ISR_PUN | \
|
||||
RTL8139_ISR_FOVW | RTL8139_ISR_LENCHG | RTL8139_ISR_SYSERR)
|
||||
|
||||
#define RTL8139_RCR_AAP (1u << 0)
|
||||
#define RTL8139_RCR_APM (1u << 1)
|
||||
#define RTL8139_RCR_AM (1u << 2)
|
||||
#define RTL8139_RCR_AB (1u << 3)
|
||||
#define RTL8139_RCR_AR (1u << 4)
|
||||
#define RTL8139_RCR_AER (1u << 5)
|
||||
#define RTL8139_RCR_WRAP (1u << 7)
|
||||
#define RTL8139_RCR_MXDMA_UNLIMITED (7u << 8)
|
||||
#define RTL8139_RCR_RBLEN_8K (0u << 11)
|
||||
|
||||
#define RTL8139_TCR_MXDMA_UNLIMITED (7u << 8)
|
||||
#define RTL8139_TCR_IFG_NORMAL (3u << 24)
|
||||
|
||||
#define RTL8139_TSD_SIZE_MASK 0x00001FFFu
|
||||
#define RTL8139_TSD_OWN (1u << 13)
|
||||
#define RTL8139_TSD_TUN (1u << 14)
|
||||
#define RTL8139_TSD_TOK (1u << 15)
|
||||
#define RTL8139_TSD_TABT (1u << 30)
|
||||
|
||||
#define RTL8139_RX_OK 0x0001u
|
||||
#define RTL8139_RX_BAD_BITS 0x003Eu // FAE | CRC | LONG | RUNT | ISE; MAR/PAM/BAR are valid match flags.
|
||||
|
||||
#define RTL8139_TX_SLOTS 4u
|
||||
#define RTL8139_TX_BUF_SIZE 2048u
|
||||
#define RTL8139_RX_RING_SIZE 8192u
|
||||
#define RTL8139_RX_PAD 16u
|
||||
#define RTL8139_RX_GUARD 1500u
|
||||
#define RTL8139_RX_ALLOC_SIZE (RTL8139_RX_RING_SIZE + RTL8139_RX_PAD + RTL8139_RX_GUARD)
|
||||
#define RTL8139_RX_PAGES ((RTL8139_RX_ALLOC_SIZE + 4095u) / 4096u)
|
||||
#define RTL8139_TX_PAGES (((RTL8139_TX_SLOTS * RTL8139_TX_BUF_SIZE) + 4095u) / 4096u)
|
||||
#define RTL8139_MMIO_PAGES 1u
|
||||
#define RTL8139_RX_WIRE_MAX (NET_ETHERNET_MAX_FRAME + 4u)
|
||||
|
||||
#define RTL8139_REG_TSD(slot) (RTL8139_REG_TSD0 + ((slot) * 4u))
|
||||
#define RTL8139_REG_TSAD(slot) (RTL8139_REG_TSAD0 + ((slot) * 4u))
|
||||
|
||||
typedef enum RTL8139_BAR_KIND {
|
||||
Rtl8139BarNone = 0,
|
||||
Rtl8139BarIo,
|
||||
Rtl8139BarMmio,
|
||||
} RTL8139_BAR_KIND;
|
||||
|
||||
typedef struct RTL8139_BAR {
|
||||
RTL8139_BAR_KIND Kind;
|
||||
uint64_t Base;
|
||||
uint8_t Index;
|
||||
} RTL8139_BAR;
|
||||
|
||||
typedef struct RTL8139_DEVICE {
|
||||
uint8_t Bus;
|
||||
uint8_t Dev;
|
||||
uint8_t Fn;
|
||||
uint8_t Irq;
|
||||
|
||||
bool UseMmio;
|
||||
volatile uint8_t* Mmio;
|
||||
uint16_t IoBase;
|
||||
|
||||
PHYSICAL_ADDRESS RxPhys;
|
||||
PHYSICAL_ADDRESS TxPhys;
|
||||
uint8_t* RxBuffer;
|
||||
uint8_t* TxBuffers;
|
||||
uint8_t RxScratch[RTL8139_TX_BUF_SIZE];
|
||||
|
||||
uint32_t RxOffset;
|
||||
uint32_t TxSlot;
|
||||
SPINLOCK TxLock;
|
||||
KDPC Dpc;
|
||||
volatile uint16_t PendingIsr;
|
||||
|
||||
NET_DEVICE NetDevice;
|
||||
} RTL8139_DEVICE;
|
||||
|
||||
static const HAL_API* gHal;
|
||||
static RTL8139_DEVICE gDevice;
|
||||
static bool gDevicePresent = false;
|
||||
const HAL_API* gHal;
|
||||
RTL8139_DEVICE gDevice;
|
||||
bool gDevicePresent = false;
|
||||
|
||||
static __forceinline void Rtl8139MemoryBarrier(void) {
|
||||
_ReadWriteBarrier();
|
||||
@@ -185,112 +44,9 @@ static void Rtl8139Write32(RTL8139_DEVICE* r, uint32_t reg, uint32_t value) {
|
||||
else __outdword((unsigned short)(r->IoBase + reg), value);
|
||||
}
|
||||
|
||||
static bool Rtl8139ReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL8139_BAR* out) {
|
||||
uint8_t reg = (uint8_t)(PCI_REG_BAR0 + index * 4u);
|
||||
uint32_t lo = gHal->Pci.Read32(bus, dev, fn, reg);
|
||||
|
||||
out->Kind = Rtl8139BarNone;
|
||||
out->Base = 0;
|
||||
out->Index = index;
|
||||
|
||||
if (lo == 0 || lo == 0xFFFFFFFFu)
|
||||
return false;
|
||||
|
||||
if (lo & 1u) {
|
||||
uint32_t base = lo & ~0x3u;
|
||||
if (!base) return false;
|
||||
out->Kind = Rtl8139BarIo;
|
||||
out->Base = base;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t base = (uint64_t)(lo & ~0xFu);
|
||||
uint32_t type = (lo >> 1) & 0x3u;
|
||||
if (type == 0x2u && index < 5) {
|
||||
uint32_t hi = gHal->Pci.Read32(bus, dev, fn, (uint8_t)(reg + 4u));
|
||||
base |= ((uint64_t)hi << 32);
|
||||
}
|
||||
|
||||
if (!base) return false;
|
||||
out->Kind = Rtl8139BarMmio;
|
||||
out->Base = base;
|
||||
return true;
|
||||
}
|
||||
|
||||
static KSTATUS Rtl8139MapMmio(uint64_t physBase, volatile uint8_t** outMmio) {
|
||||
uint64_t pageBase = physBase & PAGE_MASK;
|
||||
uint64_t pageOff = physBase - pageBase;
|
||||
|
||||
for (uint32_t i = 0; i < RTL8139_MMIO_PAGES; i++) {
|
||||
PHYSICAL_ADDRESS phys = pageBase + (uint64_t)i * PAGE_SIZE;
|
||||
uintptr_t virt = (uintptr_t)gHal->PhysToVirt(phys);
|
||||
KSTATUS st = MmMapPage(virt, phys, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||
if (st == STATUS_ALREADY_EXISTS)
|
||||
st = MmSetPageProtect(virt, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
}
|
||||
|
||||
*outMmio = (volatile uint8_t*)((uintptr_t)gHal->PhysToVirt(pageBase) + pageOff);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS Rtl8139SelectRegisterBar(RTL8139_DEVICE* r) {
|
||||
RTL8139_BAR best = { Rtl8139BarNone, 0, 0 };
|
||||
RTL8139_BAR fallback = { Rtl8139BarNone, 0, 0 };
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
RTL8139_BAR bar;
|
||||
if (!Rtl8139ReadBar(r->Bus, r->Dev, r->Fn, i, &bar))
|
||||
continue;
|
||||
|
||||
if (bar.Kind == Rtl8139BarMmio) {
|
||||
best = bar;
|
||||
break;
|
||||
}
|
||||
if (bar.Kind == Rtl8139BarIo && fallback.Kind == Rtl8139BarNone)
|
||||
fallback = bar;
|
||||
|
||||
if (bar.Kind == Rtl8139BarMmio) {
|
||||
uint32_t lo = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, (uint8_t)(PCI_REG_BAR0 + i * 4u));
|
||||
if (((lo >> 1) & 0x3u) == 0x2u)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (best.Kind == Rtl8139BarNone)
|
||||
best = fallback;
|
||||
|
||||
if (best.Kind == Rtl8139BarMmio) {
|
||||
KSTATUS st = Rtl8139MapMmio(best.Base, &r->Mmio);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
r->UseMmio = true;
|
||||
DbgPrint("RTL8139: BAR%u MMIO base=0x%llx virt=%p\n",
|
||||
best.Index, (unsigned long long)best.Base, r->Mmio);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (best.Kind == Rtl8139BarIo) {
|
||||
if (best.Base > 0xFFFFu)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
r->IoBase = (uint16_t)best.Base;
|
||||
r->UseMmio = false;
|
||||
DbgPrint("RTL8139: BAR%u I/O base=0x%04x\n", best.Index, r->IoBase);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
static void Rtl8139EnablePci(RTL8139_DEVICE* r) {
|
||||
uint32_t cmd = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND);
|
||||
cmd |= PCI_COMMAND_MASTER;
|
||||
if (r->UseMmio) cmd |= PCI_COMMAND_MEMORY;
|
||||
else cmd |= PCI_COMMAND_IO;
|
||||
cmd &= ~PCI_COMMAND_INTX_DISABLE;
|
||||
gHal->Pci.Write32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND, cmd);
|
||||
}
|
||||
|
||||
static KSTATUS Rtl8139Reset(RTL8139_DEVICE* r) {
|
||||
Rtl8139Write8(r, RTL8139_REG_CMD, RTL8139_CMD_RST);
|
||||
@@ -328,7 +84,7 @@ static KSTATUS Rtl8139AllocateDma(RTL8139_DEVICE* r) {
|
||||
memset(r->RxBuffer, 0, PAGE_SIZE * RTL8139_RX_PAGES);
|
||||
memset(r->TxBuffers, 0, PAGE_SIZE * RTL8139_TX_PAGES);
|
||||
|
||||
DbgPrint("RTL8139: rx=0x%llx tx=0x%llx\n",
|
||||
DbgPrint("RTL8139: rx=0x%016llX tx=0x%016llX\n",
|
||||
(unsigned long long)r->RxPhys,
|
||||
(unsigned long long)r->TxPhys);
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
// rtl8139.h - shared rtl8139 driver internals.
|
||||
#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/status.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <ntlite/ddi/net_device.h>
|
||||
|
||||
#define RTL8139_VENDOR_REALTEK 0x10ECu
|
||||
#define RTL8139_DEVICE_ID 0x8139u
|
||||
|
||||
#define PCI_REG_COMMAND 0x04u
|
||||
#define PCI_REG_BAR0 0x10u
|
||||
#define PCI_COMMAND_IO (1u << 0)
|
||||
#define PCI_COMMAND_MEMORY (1u << 1)
|
||||
#define PCI_COMMAND_MASTER (1u << 2)
|
||||
#define PCI_COMMAND_INTX_DISABLE (1u << 10)
|
||||
|
||||
#define RTL8139_REG_IDR0 0x00u
|
||||
#define RTL8139_REG_MAR0 0x08u
|
||||
#define RTL8139_REG_TSD0 0x10u
|
||||
#define RTL8139_REG_TSAD0 0x20u
|
||||
#define RTL8139_REG_RBSTART 0x30u
|
||||
#define RTL8139_REG_CMD 0x37u
|
||||
#define RTL8139_REG_CAPR 0x38u
|
||||
#define RTL8139_REG_CBR 0x3Au
|
||||
#define RTL8139_REG_IMR 0x3Cu
|
||||
#define RTL8139_REG_ISR 0x3Eu
|
||||
#define RTL8139_REG_TCR 0x40u
|
||||
#define RTL8139_REG_RCR 0x44u
|
||||
#define RTL8139_REG_CONFIG1 0x52u
|
||||
|
||||
#define RTL8139_CMD_BUFE 0x01u
|
||||
#define RTL8139_CMD_TE 0x04u
|
||||
#define RTL8139_CMD_RE 0x08u
|
||||
#define RTL8139_CMD_RST 0x10u
|
||||
|
||||
#define RTL8139_ISR_ROK 0x0001u
|
||||
#define RTL8139_ISR_RER 0x0002u
|
||||
#define RTL8139_ISR_TOK 0x0004u
|
||||
#define RTL8139_ISR_TER 0x0008u
|
||||
#define RTL8139_ISR_RXOVW 0x0010u
|
||||
#define RTL8139_ISR_PUN 0x0020u
|
||||
#define RTL8139_ISR_FOVW 0x0040u
|
||||
#define RTL8139_ISR_LENCHG 0x2000u
|
||||
#define RTL8139_ISR_TIMEOUT 0x4000u
|
||||
#define RTL8139_ISR_SYSERR 0x8000u
|
||||
|
||||
#define RTL8139_INTR_ENABLES (RTL8139_ISR_ROK | RTL8139_ISR_RER | RTL8139_ISR_TOK | \
|
||||
RTL8139_ISR_TER | RTL8139_ISR_RXOVW | RTL8139_ISR_PUN | \
|
||||
RTL8139_ISR_FOVW | RTL8139_ISR_LENCHG | RTL8139_ISR_SYSERR)
|
||||
|
||||
#define RTL8139_RCR_AAP (1u << 0)
|
||||
#define RTL8139_RCR_APM (1u << 1)
|
||||
#define RTL8139_RCR_AM (1u << 2)
|
||||
#define RTL8139_RCR_AB (1u << 3)
|
||||
#define RTL8139_RCR_AR (1u << 4)
|
||||
#define RTL8139_RCR_AER (1u << 5)
|
||||
#define RTL8139_RCR_WRAP (1u << 7)
|
||||
#define RTL8139_RCR_MXDMA_UNLIMITED (7u << 8)
|
||||
#define RTL8139_RCR_RBLEN_8K (0u << 11)
|
||||
|
||||
#define RTL8139_TCR_MXDMA_UNLIMITED (7u << 8)
|
||||
#define RTL8139_TCR_IFG_NORMAL (3u << 24)
|
||||
|
||||
#define RTL8139_TSD_SIZE_MASK 0x00001FFFu
|
||||
#define RTL8139_TSD_OWN (1u << 13)
|
||||
#define RTL8139_TSD_TUN (1u << 14)
|
||||
#define RTL8139_TSD_TOK (1u << 15)
|
||||
#define RTL8139_TSD_TABT (1u << 30)
|
||||
|
||||
#define RTL8139_RX_OK 0x0001u
|
||||
#define RTL8139_RX_BAD_BITS 0x003Eu // FAE | CRC | LONG | RUNT | ISE; MAR/PAM/BAR are valid match flags.
|
||||
|
||||
#define RTL8139_TX_SLOTS 4u
|
||||
#define RTL8139_TX_BUF_SIZE 2048u
|
||||
#define RTL8139_RX_RING_SIZE 8192u
|
||||
#define RTL8139_RX_PAD 16u
|
||||
#define RTL8139_RX_GUARD 1500u
|
||||
#define RTL8139_RX_ALLOC_SIZE (RTL8139_RX_RING_SIZE + RTL8139_RX_PAD + RTL8139_RX_GUARD)
|
||||
#define RTL8139_RX_PAGES ((RTL8139_RX_ALLOC_SIZE + 4095u) / 4096u)
|
||||
#define RTL8139_TX_PAGES (((RTL8139_TX_SLOTS * RTL8139_TX_BUF_SIZE) + 4095u) / 4096u)
|
||||
#define RTL8139_MMIO_PAGES 1u
|
||||
#define RTL8139_RX_WIRE_MAX (NET_ETHERNET_MAX_FRAME + 4u)
|
||||
|
||||
#define RTL8139_REG_TSD(slot) (RTL8139_REG_TSD0 + ((slot) * 4u))
|
||||
#define RTL8139_REG_TSAD(slot) (RTL8139_REG_TSAD0 + ((slot) * 4u))
|
||||
|
||||
typedef enum RTL8139_BAR_KIND {
|
||||
Rtl8139BarNone = 0,
|
||||
Rtl8139BarIo,
|
||||
Rtl8139BarMmio,
|
||||
} RTL8139_BAR_KIND;
|
||||
|
||||
typedef struct RTL8139_BAR {
|
||||
RTL8139_BAR_KIND Kind;
|
||||
uint64_t Base;
|
||||
uint8_t Index;
|
||||
} RTL8139_BAR;
|
||||
|
||||
typedef struct RTL8139_DEVICE {
|
||||
uint8_t Bus;
|
||||
uint8_t Dev;
|
||||
uint8_t Fn;
|
||||
uint8_t Irq;
|
||||
|
||||
bool UseMmio;
|
||||
volatile uint8_t* Mmio;
|
||||
uint16_t IoBase;
|
||||
|
||||
PHYSICAL_ADDRESS RxPhys;
|
||||
PHYSICAL_ADDRESS TxPhys;
|
||||
uint8_t* RxBuffer;
|
||||
uint8_t* TxBuffers;
|
||||
uint8_t RxScratch[RTL8139_TX_BUF_SIZE];
|
||||
|
||||
uint32_t RxOffset;
|
||||
uint32_t TxSlot;
|
||||
SPINLOCK TxLock;
|
||||
KDPC Dpc;
|
||||
volatile uint16_t PendingIsr;
|
||||
|
||||
NET_DEVICE NetDevice;
|
||||
} RTL8139_DEVICE;
|
||||
|
||||
|
||||
extern const HAL_API* gHal;
|
||||
extern RTL8139_DEVICE gDevice;
|
||||
extern bool gDevicePresent;
|
||||
|
||||
bool Rtl8139ReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL8139_BAR* out);
|
||||
KSTATUS Rtl8139MapMmio(uint64_t physBase, volatile uint8_t** outMmio);
|
||||
KSTATUS Rtl8139SelectRegisterBar(RTL8139_DEVICE* r);
|
||||
void Rtl8139EnablePci(RTL8139_DEVICE* r);
|
||||
@@ -0,0 +1,112 @@
|
||||
// rtl8139_pci.c - PCI BAR/MMIO front-end for rtl8139.
|
||||
#include "rtl8139.h"
|
||||
|
||||
bool Rtl8139ReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL8139_BAR* out) {
|
||||
uint8_t reg = (uint8_t)(PCI_REG_BAR0 + index * 4u);
|
||||
uint32_t lo = gHal->Pci.Read32(bus, dev, fn, reg);
|
||||
|
||||
out->Kind = Rtl8139BarNone;
|
||||
out->Base = 0;
|
||||
out->Index = index;
|
||||
|
||||
if (lo == 0 || lo == 0xFFFFFFFFu)
|
||||
return false;
|
||||
|
||||
if (lo & 1u) {
|
||||
uint32_t base = lo & ~0x3u;
|
||||
if (!base) return false;
|
||||
out->Kind = Rtl8139BarIo;
|
||||
out->Base = base;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t base = (uint64_t)(lo & ~0xFu);
|
||||
uint32_t type = (lo >> 1) & 0x3u;
|
||||
if (type == 0x2u && index < 5) {
|
||||
uint32_t hi = gHal->Pci.Read32(bus, dev, fn, (uint8_t)(reg + 4u));
|
||||
base |= ((uint64_t)hi << 32);
|
||||
}
|
||||
|
||||
if (!base) return false;
|
||||
out->Kind = Rtl8139BarMmio;
|
||||
out->Base = base;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS Rtl8139MapMmio(uint64_t physBase, volatile uint8_t** outMmio) {
|
||||
uint64_t pageBase = physBase & PAGE_MASK;
|
||||
uint64_t pageOff = physBase - pageBase;
|
||||
|
||||
for (uint32_t i = 0; i < RTL8139_MMIO_PAGES; i++) {
|
||||
PHYSICAL_ADDRESS phys = pageBase + (uint64_t)i * PAGE_SIZE;
|
||||
uintptr_t virt = (uintptr_t)gHal->PhysToVirt(phys);
|
||||
KSTATUS st = MmMapPage(virt, phys, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||
if (st == STATUS_ALREADY_EXISTS)
|
||||
st = MmSetPageProtect(virt, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
}
|
||||
|
||||
*outMmio = (volatile uint8_t*)((uintptr_t)gHal->PhysToVirt(pageBase) + pageOff);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS Rtl8139SelectRegisterBar(RTL8139_DEVICE* r) {
|
||||
RTL8139_BAR best = { Rtl8139BarNone, 0, 0 };
|
||||
RTL8139_BAR fallback = { Rtl8139BarNone, 0, 0 };
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
RTL8139_BAR bar;
|
||||
if (!Rtl8139ReadBar(r->Bus, r->Dev, r->Fn, i, &bar))
|
||||
continue;
|
||||
|
||||
if (bar.Kind == Rtl8139BarMmio) {
|
||||
best = bar;
|
||||
break;
|
||||
}
|
||||
if (bar.Kind == Rtl8139BarIo && fallback.Kind == Rtl8139BarNone)
|
||||
fallback = bar;
|
||||
|
||||
if (bar.Kind == Rtl8139BarMmio) {
|
||||
uint32_t lo = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, (uint8_t)(PCI_REG_BAR0 + i * 4u));
|
||||
if (((lo >> 1) & 0x3u) == 0x2u)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (best.Kind == Rtl8139BarNone)
|
||||
best = fallback;
|
||||
|
||||
if (best.Kind == Rtl8139BarMmio) {
|
||||
KSTATUS st = Rtl8139MapMmio(best.Base, &r->Mmio);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
r->UseMmio = true;
|
||||
DbgPrint("RTL8139: BAR%u MMIO base=0x%016llX virt=0x%016llX\n",
|
||||
best.Index, (unsigned long long)best.Base, r->Mmio);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (best.Kind == Rtl8139BarIo) {
|
||||
if (best.Base > 0xFFFFu)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
r->IoBase = (uint16_t)best.Base;
|
||||
r->UseMmio = false;
|
||||
DbgPrint("RTL8139: BAR%u I/O base=0x%04x\n", best.Index, r->IoBase);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
void Rtl8139EnablePci(RTL8139_DEVICE* r) {
|
||||
uint32_t cmd = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND);
|
||||
cmd |= PCI_COMMAND_MASTER;
|
||||
if (r->UseMmio) cmd |= PCI_COMMAND_MEMORY;
|
||||
else cmd |= PCI_COMMAND_IO;
|
||||
cmd &= ~PCI_COMMAND_INTX_DISABLE;
|
||||
gHal->Pci.Write32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND, cmd);
|
||||
}
|
||||
@@ -1,168 +1,9 @@
|
||||
// rtl816x.c - first-pass Realtek RTL8111/8168/8411 PCIe Gigabit NIC driver.
|
||||
//
|
||||
// Scope for this version:
|
||||
// * RTL8168/RTL8169-compatible PCI IDs, legacy INTx interrupt delivery.
|
||||
// * MMIO preferred, I/O BAR fallback.
|
||||
// * Descriptor-mode RX/TX with plain Ethernet frames.
|
||||
// * No checksum offload, TSO, VLAN, jumbo frames, MSI/MSI-X, or EEE.
|
||||
//
|
||||
// The driver registers a NET_DEVICE so a later ARP/IPv4/ICMP stack can attach
|
||||
// without knowing Realtek register details.
|
||||
// rtl816x.c - packet I/O, interrupt, and DriverEntry path.
|
||||
#include "rtl816x.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <ntlite/status.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <ntlite/ddi/net_device.h>
|
||||
|
||||
#define RTL_VENDOR_REALTEK 0x10ECu
|
||||
#define RTL_DEVICE_8168 0x8168u
|
||||
#define RTL_DEVICE_8169 0x8169u
|
||||
|
||||
#define PCI_REG_COMMAND 0x04u
|
||||
#define PCI_REG_BAR0 0x10u
|
||||
#define PCI_REG_IRQ 0x3Cu
|
||||
#define PCI_COMMAND_IO (1u << 0)
|
||||
#define PCI_COMMAND_MEMORY (1u << 1)
|
||||
#define PCI_COMMAND_MASTER (1u << 2)
|
||||
#define PCI_COMMAND_INTX_DISABLE (1u << 10)
|
||||
|
||||
#define RTL_REG_MAC0 0x00u
|
||||
#define RTL_REG_MAR0 0x08u
|
||||
#define RTL_REG_TNPDS_LO 0x20u
|
||||
#define RTL_REG_TNPDS_HI 0x24u
|
||||
#define RTL_REG_THPDS_LO 0x28u
|
||||
#define RTL_REG_THPDS_HI 0x2Cu
|
||||
#define RTL_REG_CHIP_CMD 0x37u
|
||||
#define RTL_REG_TX_POLL 0x38u
|
||||
#define RTL_REG_INTR_MASK 0x3Cu
|
||||
#define RTL_REG_INTR_STAT 0x3Eu
|
||||
#define RTL_REG_TX_CONFIG 0x40u
|
||||
#define RTL_REG_RX_CONFIG 0x44u
|
||||
#define RTL_REG_CFG9346 0x50u
|
||||
#define RTL_REG_CONFIG1 0x52u
|
||||
#define RTL_REG_PHY_STATUS 0x6Cu
|
||||
#define RTL_REG_RX_MAXSIZE 0xDAu
|
||||
#define RTL_REG_CPLUS_CMD 0xE0u
|
||||
#define RTL_REG_RDSAR_LO 0xE4u
|
||||
#define RTL_REG_RDSAR_HI 0xE8u
|
||||
|
||||
#define RTL_CMD_RESET 0x10u
|
||||
#define RTL_CMD_RX_ENABLE 0x08u
|
||||
#define RTL_CMD_TX_ENABLE 0x04u
|
||||
|
||||
#define RTL_TXPOLL_NPQ 0x40u
|
||||
|
||||
#define RTL_ISR_ROK 0x0001u
|
||||
#define RTL_ISR_RER 0x0002u
|
||||
#define RTL_ISR_TOK 0x0004u
|
||||
#define RTL_ISR_TER 0x0008u
|
||||
#define RTL_ISR_RXOVW 0x0010u
|
||||
#define RTL_ISR_LINKCHG 0x0020u
|
||||
#define RTL_ISR_FOVW 0x0040u
|
||||
#define RTL_ISR_TDU 0x0080u
|
||||
#define RTL_ISR_SWINT 0x0100u
|
||||
#define RTL_ISR_TIMEOUT 0x4000u
|
||||
#define RTL_ISR_SYSERR 0x8000u
|
||||
|
||||
#define RTL_INTR_ENABLES (RTL_ISR_ROK | RTL_ISR_RER | RTL_ISR_TOK | RTL_ISR_TER | \
|
||||
RTL_ISR_RXOVW | RTL_ISR_LINKCHG | RTL_ISR_FOVW | \
|
||||
RTL_ISR_TDU | RTL_ISR_SYSERR)
|
||||
|
||||
#define RTL_RXCFG_AAP (1u << 0) // accept all physical packets/promiscuous
|
||||
#define RTL_RXCFG_APM (1u << 1) // accept physical match
|
||||
#define RTL_RXCFG_AM (1u << 2) // accept multicast
|
||||
#define RTL_RXCFG_AB (1u << 3) // accept broadcast
|
||||
#define RTL_RXCFG_AR (1u << 4) // accept runt
|
||||
#define RTL_RXCFG_AER (1u << 5) // accept errored
|
||||
#define RTL_RXCFG_DMA_UNLIMITED (7u << 8)
|
||||
|
||||
#define RTL_TXCFG_DMA_UNLIMITED (7u << 8)
|
||||
#define RTL_TXCFG_IFG (3u << 24)
|
||||
|
||||
#define RTL_DESC_OWN (1u << 31)
|
||||
#define RTL_DESC_EOR (1u << 30)
|
||||
#define RTL_DESC_FS (1u << 29)
|
||||
#define RTL_DESC_LS (1u << 28)
|
||||
#define RTL_RXDESC_RES (1u << 21)
|
||||
#define RTL_RXDESC_RWT (1u << 22)
|
||||
#define RTL_RXDESC_CRC (1u << 19)
|
||||
#define RTL_RXDESC_RUNT (1u << 20)
|
||||
#define RTL_DESC_LENGTH_MASK 0x00003FFFu
|
||||
|
||||
#define RTL_RX_COUNT 64u
|
||||
#define RTL_TX_COUNT 64u
|
||||
#define RTL_BUF_SIZE 2048u
|
||||
#define RTL_RX_WIRE_MAX (NET_ETHERNET_MAX_FRAME + 4u)
|
||||
#define RTL_RING_PAGES 1u
|
||||
#define RTL_RXBUF_PAGES ((RTL_RX_COUNT * RTL_BUF_SIZE + 4095u) / 4096u)
|
||||
#define RTL_TXBUF_PAGES ((RTL_TX_COUNT * RTL_BUF_SIZE + 4095u) / 4096u)
|
||||
#define RTL_MMIO_PAGES 1u
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct RTL_DESC {
|
||||
volatile uint32_t opts1;
|
||||
volatile uint32_t opts2;
|
||||
volatile uint32_t addrLo;
|
||||
volatile uint32_t addrHi;
|
||||
} RTL_DESC;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef enum RTL_BAR_KIND {
|
||||
RtlBarNone = 0,
|
||||
RtlBarIo,
|
||||
RtlBarMmio,
|
||||
} RTL_BAR_KIND;
|
||||
|
||||
typedef struct RTL_BAR {
|
||||
RTL_BAR_KIND Kind;
|
||||
uint64_t Base;
|
||||
uint8_t Index;
|
||||
} RTL_BAR;
|
||||
|
||||
typedef struct RTL816X_DEVICE {
|
||||
uint8_t Bus;
|
||||
uint8_t Dev;
|
||||
uint8_t Fn;
|
||||
uint8_t Irq;
|
||||
uint16_t DeviceId;
|
||||
|
||||
bool UseMmio;
|
||||
volatile uint8_t* Mmio;
|
||||
uint16_t IoBase;
|
||||
|
||||
RTL_DESC* RxRing;
|
||||
RTL_DESC* TxRing;
|
||||
PHYSICAL_ADDRESS RxRingPhys;
|
||||
PHYSICAL_ADDRESS TxRingPhys;
|
||||
|
||||
uint8_t* RxBuffers;
|
||||
uint8_t* TxBuffers;
|
||||
PHYSICAL_ADDRESS RxBuffersPhys;
|
||||
PHYSICAL_ADDRESS TxBuffersPhys;
|
||||
|
||||
uint32_t RxIndex;
|
||||
uint32_t TxIndex;
|
||||
SPINLOCK TxLock;
|
||||
KDPC Dpc;
|
||||
volatile uint16_t PendingIsr;
|
||||
|
||||
NET_DEVICE NetDevice;
|
||||
} RTL816X_DEVICE;
|
||||
|
||||
static const HAL_API* gHal;
|
||||
static RTL816X_DEVICE gDevice;
|
||||
static bool gDevicePresent = false;
|
||||
const HAL_API* gHal;
|
||||
RTL816X_DEVICE gDevice;
|
||||
bool gDevicePresent = false;
|
||||
|
||||
static __forceinline void RtlMemoryBarrier(void) {
|
||||
_ReadWriteBarrier();
|
||||
@@ -203,125 +44,10 @@ static void RtlWrite32(RTL816X_DEVICE* r, uint32_t reg, uint32_t value) {
|
||||
else __outdword((unsigned short)(r->IoBase + reg), value);
|
||||
}
|
||||
|
||||
static bool RtlFindDevice(uint8_t* outBus, uint8_t* outDev, uint8_t* outFn, uint16_t* outDeviceId) {
|
||||
if (gHal->Pci.FindDevice(RTL_VENDOR_REALTEK, RTL_DEVICE_8168, outBus, outDev, outFn)) {
|
||||
*outDeviceId = RTL_DEVICE_8168;
|
||||
return true;
|
||||
}
|
||||
if (gHal->Pci.FindDevice(RTL_VENDOR_REALTEK, RTL_DEVICE_8169, outBus, outDev, outFn)) {
|
||||
*outDeviceId = RTL_DEVICE_8169;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool RtlReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL_BAR* out) {
|
||||
uint8_t reg = (uint8_t)(PCI_REG_BAR0 + index * 4u);
|
||||
uint32_t lo = gHal->Pci.Read32(bus, dev, fn, reg);
|
||||
|
||||
out->Kind = RtlBarNone;
|
||||
out->Base = 0;
|
||||
out->Index = index;
|
||||
|
||||
if (lo == 0 || lo == 0xFFFFFFFFu)
|
||||
return false;
|
||||
|
||||
if (lo & 1u) {
|
||||
uint32_t base = lo & ~0x3u;
|
||||
if (!base) return false;
|
||||
out->Kind = RtlBarIo;
|
||||
out->Base = base;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t base = (uint64_t)(lo & ~0xFu);
|
||||
uint32_t type = (lo >> 1) & 0x3u;
|
||||
if (type == 0x2u && index < 5) {
|
||||
uint32_t hi = gHal->Pci.Read32(bus, dev, fn, (uint8_t)(reg + 4u));
|
||||
base |= ((uint64_t)hi << 32);
|
||||
}
|
||||
|
||||
if (!base) return false;
|
||||
out->Kind = RtlBarMmio;
|
||||
out->Base = base;
|
||||
return true;
|
||||
}
|
||||
|
||||
static KSTATUS RtlMapMmio(uint64_t physBase, volatile uint8_t** outMmio) {
|
||||
uint64_t pageBase = physBase & PAGE_MASK;
|
||||
uint64_t pageOff = physBase - pageBase;
|
||||
|
||||
for (uint32_t i = 0; i < RTL_MMIO_PAGES; i++) {
|
||||
PHYSICAL_ADDRESS phys = pageBase + (uint64_t)i * PAGE_SIZE;
|
||||
uintptr_t virt = (uintptr_t)gHal->PhysToVirt(phys);
|
||||
KSTATUS st = MmMapPage(virt, phys, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||
if (st == STATUS_ALREADY_EXISTS)
|
||||
st = MmSetPageProtect(virt, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
}
|
||||
|
||||
*outMmio = (volatile uint8_t*)((uintptr_t)gHal->PhysToVirt(pageBase) + pageOff);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS RtlSelectRegisterBar(RTL816X_DEVICE* r) {
|
||||
RTL_BAR best = { RtlBarNone, 0, 0 };
|
||||
RTL_BAR fallback = { RtlBarNone, 0, 0 };
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
RTL_BAR bar;
|
||||
if (!RtlReadBar(r->Bus, r->Dev, r->Fn, i, &bar))
|
||||
continue;
|
||||
|
||||
if (bar.Kind == RtlBarMmio) {
|
||||
best = bar;
|
||||
break;
|
||||
}
|
||||
if (bar.Kind == RtlBarIo && fallback.Kind == RtlBarNone)
|
||||
fallback = bar;
|
||||
|
||||
// Skip the high dword of 64-bit memory BARs.
|
||||
if (bar.Kind == RtlBarMmio) {
|
||||
uint32_t lo = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, (uint8_t)(PCI_REG_BAR0 + i * 4u));
|
||||
if (((lo >> 1) & 0x3u) == 0x2u)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (best.Kind == RtlBarNone)
|
||||
best = fallback;
|
||||
|
||||
if (best.Kind == RtlBarMmio) {
|
||||
KSTATUS st = RtlMapMmio(best.Base, &r->Mmio);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
r->UseMmio = true;
|
||||
DbgPrint("RTL816X: BAR%u MMIO base=0x%llx virt=%p\n",
|
||||
best.Index, (unsigned long long)best.Base, r->Mmio);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (best.Kind == RtlBarIo) {
|
||||
if (best.Base > 0xFFFFu)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
r->IoBase = (uint16_t)best.Base;
|
||||
r->UseMmio = false;
|
||||
DbgPrint("RTL816X: BAR%u I/O base=0x%04x\n", best.Index, r->IoBase);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
static void RtlEnablePci(RTL816X_DEVICE* r) {
|
||||
uint32_t cmd = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND);
|
||||
cmd |= PCI_COMMAND_MASTER;
|
||||
if (r->UseMmio) cmd |= PCI_COMMAND_MEMORY;
|
||||
else cmd |= PCI_COMMAND_IO;
|
||||
cmd &= ~PCI_COMMAND_INTX_DISABLE;
|
||||
gHal->Pci.Write32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND, cmd);
|
||||
}
|
||||
|
||||
static KSTATUS RtlReset(RTL816X_DEVICE* r) {
|
||||
RtlWrite8(r, RTL_REG_CHIP_CMD, RTL_CMD_RESET);
|
||||
@@ -359,7 +85,7 @@ static KSTATUS RtlAllocateDma(RTL816X_DEVICE* r) {
|
||||
memset(r->RxBuffers, 0, PAGE_SIZE * RTL_RXBUF_PAGES);
|
||||
memset(r->TxBuffers, 0, PAGE_SIZE * RTL_TXBUF_PAGES);
|
||||
|
||||
DbgPrint("RTL816X: rxRing=0x%llx txRing=0x%llx rxBuf=0x%llx txBuf=0x%llx\n",
|
||||
DbgPrint("RTL816X: rxRing=0x%016llX txRing=0x%016llX rxBuf=0x%016llX txBuf=0x%016llX\n",
|
||||
(unsigned long long)r->RxRingPhys,
|
||||
(unsigned long long)r->TxRingPhys,
|
||||
(unsigned long long)r->RxBuffersPhys,
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
// rtl816x.h - shared rtl816x driver internals.
|
||||
#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/status.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <ntlite/ddi/net_device.h>
|
||||
|
||||
#define RTL_VENDOR_REALTEK 0x10ECu
|
||||
#define RTL_DEVICE_8168 0x8168u
|
||||
#define RTL_DEVICE_8169 0x8169u
|
||||
|
||||
#define PCI_REG_COMMAND 0x04u
|
||||
#define PCI_REG_BAR0 0x10u
|
||||
#define PCI_REG_IRQ 0x3Cu
|
||||
#define PCI_COMMAND_IO (1u << 0)
|
||||
#define PCI_COMMAND_MEMORY (1u << 1)
|
||||
#define PCI_COMMAND_MASTER (1u << 2)
|
||||
#define PCI_COMMAND_INTX_DISABLE (1u << 10)
|
||||
|
||||
#define RTL_REG_MAC0 0x00u
|
||||
#define RTL_REG_MAR0 0x08u
|
||||
#define RTL_REG_TNPDS_LO 0x20u
|
||||
#define RTL_REG_TNPDS_HI 0x24u
|
||||
#define RTL_REG_THPDS_LO 0x28u
|
||||
#define RTL_REG_THPDS_HI 0x2Cu
|
||||
#define RTL_REG_CHIP_CMD 0x37u
|
||||
#define RTL_REG_TX_POLL 0x38u
|
||||
#define RTL_REG_INTR_MASK 0x3Cu
|
||||
#define RTL_REG_INTR_STAT 0x3Eu
|
||||
#define RTL_REG_TX_CONFIG 0x40u
|
||||
#define RTL_REG_RX_CONFIG 0x44u
|
||||
#define RTL_REG_CFG9346 0x50u
|
||||
#define RTL_REG_CONFIG1 0x52u
|
||||
#define RTL_REG_PHY_STATUS 0x6Cu
|
||||
#define RTL_REG_RX_MAXSIZE 0xDAu
|
||||
#define RTL_REG_CPLUS_CMD 0xE0u
|
||||
#define RTL_REG_RDSAR_LO 0xE4u
|
||||
#define RTL_REG_RDSAR_HI 0xE8u
|
||||
|
||||
#define RTL_CMD_RESET 0x10u
|
||||
#define RTL_CMD_RX_ENABLE 0x08u
|
||||
#define RTL_CMD_TX_ENABLE 0x04u
|
||||
|
||||
#define RTL_TXPOLL_NPQ 0x40u
|
||||
|
||||
#define RTL_ISR_ROK 0x0001u
|
||||
#define RTL_ISR_RER 0x0002u
|
||||
#define RTL_ISR_TOK 0x0004u
|
||||
#define RTL_ISR_TER 0x0008u
|
||||
#define RTL_ISR_RXOVW 0x0010u
|
||||
#define RTL_ISR_LINKCHG 0x0020u
|
||||
#define RTL_ISR_FOVW 0x0040u
|
||||
#define RTL_ISR_TDU 0x0080u
|
||||
#define RTL_ISR_SWINT 0x0100u
|
||||
#define RTL_ISR_TIMEOUT 0x4000u
|
||||
#define RTL_ISR_SYSERR 0x8000u
|
||||
|
||||
#define RTL_INTR_ENABLES (RTL_ISR_ROK | RTL_ISR_RER | RTL_ISR_TOK | RTL_ISR_TER | \
|
||||
RTL_ISR_RXOVW | RTL_ISR_LINKCHG | RTL_ISR_FOVW | \
|
||||
RTL_ISR_TDU | RTL_ISR_SYSERR)
|
||||
|
||||
#define RTL_RXCFG_AAP (1u << 0) // accept all physical packets/promiscuous
|
||||
#define RTL_RXCFG_APM (1u << 1) // accept physical match
|
||||
#define RTL_RXCFG_AM (1u << 2) // accept multicast
|
||||
#define RTL_RXCFG_AB (1u << 3) // accept broadcast
|
||||
#define RTL_RXCFG_AR (1u << 4) // accept runt
|
||||
#define RTL_RXCFG_AER (1u << 5) // accept errored
|
||||
#define RTL_RXCFG_DMA_UNLIMITED (7u << 8)
|
||||
|
||||
#define RTL_TXCFG_DMA_UNLIMITED (7u << 8)
|
||||
#define RTL_TXCFG_IFG (3u << 24)
|
||||
|
||||
#define RTL_DESC_OWN (1u << 31)
|
||||
#define RTL_DESC_EOR (1u << 30)
|
||||
#define RTL_DESC_FS (1u << 29)
|
||||
#define RTL_DESC_LS (1u << 28)
|
||||
#define RTL_RXDESC_RES (1u << 21)
|
||||
#define RTL_RXDESC_RWT (1u << 22)
|
||||
#define RTL_RXDESC_CRC (1u << 19)
|
||||
#define RTL_RXDESC_RUNT (1u << 20)
|
||||
#define RTL_DESC_LENGTH_MASK 0x00003FFFu
|
||||
|
||||
#define RTL_RX_COUNT 64u
|
||||
#define RTL_TX_COUNT 64u
|
||||
#define RTL_BUF_SIZE 2048u
|
||||
#define RTL_RX_WIRE_MAX (NET_ETHERNET_MAX_FRAME + 4u)
|
||||
#define RTL_RING_PAGES 1u
|
||||
#define RTL_RXBUF_PAGES ((RTL_RX_COUNT * RTL_BUF_SIZE + 4095u) / 4096u)
|
||||
#define RTL_TXBUF_PAGES ((RTL_TX_COUNT * RTL_BUF_SIZE + 4095u) / 4096u)
|
||||
#define RTL_MMIO_PAGES 1u
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct RTL_DESC {
|
||||
volatile uint32_t opts1;
|
||||
volatile uint32_t opts2;
|
||||
volatile uint32_t addrLo;
|
||||
volatile uint32_t addrHi;
|
||||
} RTL_DESC;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef enum RTL_BAR_KIND {
|
||||
RtlBarNone = 0,
|
||||
RtlBarIo,
|
||||
RtlBarMmio,
|
||||
} RTL_BAR_KIND;
|
||||
|
||||
typedef struct RTL_BAR {
|
||||
RTL_BAR_KIND Kind;
|
||||
uint64_t Base;
|
||||
uint8_t Index;
|
||||
} RTL_BAR;
|
||||
|
||||
typedef struct RTL816X_DEVICE {
|
||||
uint8_t Bus;
|
||||
uint8_t Dev;
|
||||
uint8_t Fn;
|
||||
uint8_t Irq;
|
||||
uint16_t DeviceId;
|
||||
|
||||
bool UseMmio;
|
||||
volatile uint8_t* Mmio;
|
||||
uint16_t IoBase;
|
||||
|
||||
RTL_DESC* RxRing;
|
||||
RTL_DESC* TxRing;
|
||||
PHYSICAL_ADDRESS RxRingPhys;
|
||||
PHYSICAL_ADDRESS TxRingPhys;
|
||||
|
||||
uint8_t* RxBuffers;
|
||||
uint8_t* TxBuffers;
|
||||
PHYSICAL_ADDRESS RxBuffersPhys;
|
||||
PHYSICAL_ADDRESS TxBuffersPhys;
|
||||
|
||||
uint32_t RxIndex;
|
||||
uint32_t TxIndex;
|
||||
SPINLOCK TxLock;
|
||||
KDPC Dpc;
|
||||
volatile uint16_t PendingIsr;
|
||||
|
||||
NET_DEVICE NetDevice;
|
||||
} RTL816X_DEVICE;
|
||||
|
||||
|
||||
extern const HAL_API* gHal;
|
||||
extern RTL816X_DEVICE gDevice;
|
||||
extern bool gDevicePresent;
|
||||
|
||||
bool RtlFindDevice(uint8_t* outBus, uint8_t* outDev, uint8_t* outFn, uint16_t* outDeviceId);
|
||||
bool RtlReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL_BAR* out);
|
||||
KSTATUS RtlMapMmio(uint64_t physBase, volatile uint8_t** outMmio);
|
||||
KSTATUS RtlSelectRegisterBar(RTL816X_DEVICE* r);
|
||||
void RtlEnablePci(RTL816X_DEVICE* r);
|
||||
@@ -0,0 +1,126 @@
|
||||
// rtl816x_pci.c - PCI BAR/MMIO front-end for rtl816x.
|
||||
#include "rtl816x.h"
|
||||
|
||||
bool RtlFindDevice(uint8_t* outBus, uint8_t* outDev, uint8_t* outFn, uint16_t* outDeviceId) {
|
||||
if (gHal->Pci.FindDevice(RTL_VENDOR_REALTEK, RTL_DEVICE_8168, outBus, outDev, outFn)) {
|
||||
*outDeviceId = RTL_DEVICE_8168;
|
||||
return true;
|
||||
}
|
||||
if (gHal->Pci.FindDevice(RTL_VENDOR_REALTEK, RTL_DEVICE_8169, outBus, outDev, outFn)) {
|
||||
*outDeviceId = RTL_DEVICE_8169;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool RtlReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL_BAR* out) {
|
||||
uint8_t reg = (uint8_t)(PCI_REG_BAR0 + index * 4u);
|
||||
uint32_t lo = gHal->Pci.Read32(bus, dev, fn, reg);
|
||||
|
||||
out->Kind = RtlBarNone;
|
||||
out->Base = 0;
|
||||
out->Index = index;
|
||||
|
||||
if (lo == 0 || lo == 0xFFFFFFFFu)
|
||||
return false;
|
||||
|
||||
if (lo & 1u) {
|
||||
uint32_t base = lo & ~0x3u;
|
||||
if (!base) return false;
|
||||
out->Kind = RtlBarIo;
|
||||
out->Base = base;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t base = (uint64_t)(lo & ~0xFu);
|
||||
uint32_t type = (lo >> 1) & 0x3u;
|
||||
if (type == 0x2u && index < 5) {
|
||||
uint32_t hi = gHal->Pci.Read32(bus, dev, fn, (uint8_t)(reg + 4u));
|
||||
base |= ((uint64_t)hi << 32);
|
||||
}
|
||||
|
||||
if (!base) return false;
|
||||
out->Kind = RtlBarMmio;
|
||||
out->Base = base;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS RtlMapMmio(uint64_t physBase, volatile uint8_t** outMmio) {
|
||||
uint64_t pageBase = physBase & PAGE_MASK;
|
||||
uint64_t pageOff = physBase - pageBase;
|
||||
|
||||
for (uint32_t i = 0; i < RTL_MMIO_PAGES; i++) {
|
||||
PHYSICAL_ADDRESS phys = pageBase + (uint64_t)i * PAGE_SIZE;
|
||||
uintptr_t virt = (uintptr_t)gHal->PhysToVirt(phys);
|
||||
KSTATUS st = MmMapPage(virt, phys, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||
if (st == STATUS_ALREADY_EXISTS)
|
||||
st = MmSetPageProtect(virt, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
}
|
||||
|
||||
*outMmio = (volatile uint8_t*)((uintptr_t)gHal->PhysToVirt(pageBase) + pageOff);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS RtlSelectRegisterBar(RTL816X_DEVICE* r) {
|
||||
RTL_BAR best = { RtlBarNone, 0, 0 };
|
||||
RTL_BAR fallback = { RtlBarNone, 0, 0 };
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
RTL_BAR bar;
|
||||
if (!RtlReadBar(r->Bus, r->Dev, r->Fn, i, &bar))
|
||||
continue;
|
||||
|
||||
if (bar.Kind == RtlBarMmio) {
|
||||
best = bar;
|
||||
break;
|
||||
}
|
||||
if (bar.Kind == RtlBarIo && fallback.Kind == RtlBarNone)
|
||||
fallback = bar;
|
||||
|
||||
// Skip the high dword of 64-bit memory BARs.
|
||||
if (bar.Kind == RtlBarMmio) {
|
||||
uint32_t lo = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, (uint8_t)(PCI_REG_BAR0 + i * 4u));
|
||||
if (((lo >> 1) & 0x3u) == 0x2u)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (best.Kind == RtlBarNone)
|
||||
best = fallback;
|
||||
|
||||
if (best.Kind == RtlBarMmio) {
|
||||
KSTATUS st = RtlMapMmio(best.Base, &r->Mmio);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
r->UseMmio = true;
|
||||
DbgPrint("RTL816X: BAR%u MMIO base=0x%016llX virt=0x%016llX\n",
|
||||
best.Index, (unsigned long long)best.Base, r->Mmio);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (best.Kind == RtlBarIo) {
|
||||
if (best.Base > 0xFFFFu)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
r->IoBase = (uint16_t)best.Base;
|
||||
r->UseMmio = false;
|
||||
DbgPrint("RTL816X: BAR%u I/O base=0x%04x\n", best.Index, r->IoBase);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
void RtlEnablePci(RTL816X_DEVICE* r) {
|
||||
uint32_t cmd = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND);
|
||||
cmd |= PCI_COMMAND_MASTER;
|
||||
if (r->UseMmio) cmd |= PCI_COMMAND_MEMORY;
|
||||
else cmd |= PCI_COMMAND_IO;
|
||||
cmd &= ~PCI_COMMAND_INTX_DISABLE;
|
||||
gHal->Pci.Write32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND, cmd);
|
||||
}
|
||||
@@ -1,436 +1,18 @@
|
||||
// ahci.c - AHCI driver, interrupt-driven with per-drive async IRP queue.
|
||||
// Supports SATA drives only (no ATAPI). Registers each found drive with the
|
||||
// kernel disk device registry via IoRegisterDiskDevice().
|
||||
//
|
||||
// Memory model:
|
||||
// MmAllocateContiguous(n) returns a physical address for n*4K pages.
|
||||
// hal->PhysToVirt(phys) converts to kernel virtual address.
|
||||
// DMA buffers are bounce-buffered (allocated via MmAllocateContiguous) to
|
||||
// avoid needing a VirtToPhys conversion for arbitrary kernel pointers.
|
||||
//
|
||||
// Async I/O model (IRP path):
|
||||
// Each drive has a dedicated kernel worker thread (AhciWorker) that blocks
|
||||
// on a per-drive counting semaphore. Callers submit IRPs through IoCallDriver,
|
||||
// which dispatches to AHCI and enqueues the IRP. The worker dequeues
|
||||
// one IRP at a time, runs the synchronous hardware transfer (blocking the
|
||||
// worker, NOT the submitting thread), then writes irp->Status and fires
|
||||
// irp->CompletionEvent / irp->CompletionFn.
|
||||
//
|
||||
// This gives true async I/O from the caller's perspective: IoSubmitIrp()
|
||||
// returns STATUS_PENDING immediately. Multiple callers can have outstanding
|
||||
// requests against different drives concurrently without stalling each other.
|
||||
//
|
||||
// NCQ extension (future work - see "NCQ TODO" comments):
|
||||
// To saturate modern SSDs, replace the serialised single-slot model with
|
||||
// Native Command Queuing:
|
||||
// • Allocate per-slot bounce buffers (NUM_NCQ_SLOTS × SLOT_BOUNCE_BYTES).
|
||||
// • Use ATA_CMD_READ_FPDMA_QUEUED / WRITE_FPDMA_QUEUED.
|
||||
// • Encode the slot tag in FIS.countl bits 7:3.
|
||||
// • Issue via port->sact (NCQ active) rather than port->ci.
|
||||
// • In AhciIsr: completed slots = prev_sact ^ port->sact; look up
|
||||
// slot_irp[slot] and fire each completion from within the ISR.
|
||||
// • The worker becomes a pure dispatcher: grab a free slot, build the
|
||||
// command, issue it, return immediately. No per-slot KeWaitForEvent.
|
||||
//
|
||||
// Interrupt model (unchanged):
|
||||
// GHC.IE and per-port PORT_IE are enabled after each port is initialised.
|
||||
// AhciIsr fires on the PCI IRQ line, captures/clears port status, and queues
|
||||
// a per-drive DPC. The DPC signals completionEvent outside hard IRQ context.
|
||||
// port_issue_cmd blocks the worker thread on KeWaitForEvent instead of
|
||||
// spinning on CI. port_issue_cmd_poll is kept for early init (IDENTIFY)
|
||||
// before the scheduler is live.
|
||||
// ahci.c - AHCI disk command path and DriverEntry.
|
||||
#include "ahci.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_irp.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef enum {
|
||||
FIS_TYPE_REG_H2D = 0x27,
|
||||
FIS_TYPE_REG_D2H = 0x34,
|
||||
FIS_TYPE_DMA_ACT = 0x39,
|
||||
FIS_TYPE_DMA_SETUP = 0x41,
|
||||
FIS_TYPE_DATA = 0x46,
|
||||
FIS_TYPE_BIST = 0x58,
|
||||
FIS_TYPE_PIO_SETUP = 0x5F,
|
||||
FIS_TYPE_DEV_BITS = 0xA1,
|
||||
} FIS_TYPE;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// Register FIS - Host to Device (H2D)
|
||||
typedef struct {
|
||||
uint8_t fis_type; // FIS_TYPE_REG_H2D
|
||||
uint8_t pmport : 4;
|
||||
uint8_t rsv0 : 3;
|
||||
uint8_t c : 1; // 1 = Command, 0 = Control
|
||||
uint8_t command;
|
||||
uint8_t featurel;
|
||||
// DW1
|
||||
uint8_t lba0, lba1, lba2;
|
||||
uint8_t device;
|
||||
// DW2
|
||||
uint8_t lba3, lba4, lba5;
|
||||
uint8_t featureh;
|
||||
// DW3
|
||||
uint8_t countl, counth;
|
||||
uint8_t icc;
|
||||
uint8_t control;
|
||||
// DW4
|
||||
uint8_t rsv1[4];
|
||||
} FIS_REG_H2D;
|
||||
|
||||
// Register FIS - Device to Host (D2H)
|
||||
typedef struct {
|
||||
uint8_t fis_type;
|
||||
uint8_t pmport : 4;
|
||||
uint8_t rsv0 : 2;
|
||||
uint8_t i : 1;
|
||||
uint8_t rsv1 : 1;
|
||||
uint8_t status;
|
||||
uint8_t error;
|
||||
uint8_t lba0, lba1, lba2;
|
||||
uint8_t device;
|
||||
uint8_t lba3, lba4, lba5;
|
||||
uint8_t rsv2;
|
||||
uint8_t countl, counth;
|
||||
uint8_t rsv3[2];
|
||||
uint8_t rsv4[4];
|
||||
} FIS_REG_D2H;
|
||||
|
||||
// DMA Setup FIS
|
||||
typedef struct {
|
||||
uint8_t fis_type;
|
||||
uint8_t pmport : 4;
|
||||
uint8_t rsv0 : 1;
|
||||
uint8_t d : 1;
|
||||
uint8_t i : 1;
|
||||
uint8_t a : 1;
|
||||
uint8_t rsved[2];
|
||||
uint64_t DMAbufferID;
|
||||
uint32_t rsvd;
|
||||
uint32_t DMAbufOffset;
|
||||
uint32_t TransferCount;
|
||||
uint32_t resvd;
|
||||
} FIS_DMA_SETUP;
|
||||
|
||||
// PIO Setup FIS
|
||||
typedef struct {
|
||||
uint8_t fis_type;
|
||||
uint8_t pmport : 4;
|
||||
uint8_t rsv0 : 1;
|
||||
uint8_t d : 1;
|
||||
uint8_t i : 1;
|
||||
uint8_t rsv1 : 1;
|
||||
uint8_t status;
|
||||
uint8_t error;
|
||||
uint8_t lba0, lba1, lba2;
|
||||
uint8_t device;
|
||||
uint8_t lba3, lba4, lba5;
|
||||
uint8_t rsv2;
|
||||
uint8_t countl, counth;
|
||||
uint8_t rsv3;
|
||||
uint8_t e_status;
|
||||
uint16_t tc;
|
||||
uint8_t rsv4[2];
|
||||
} FIS_PIO_SETUP;
|
||||
|
||||
// Set Device Bits FIS
|
||||
typedef struct {
|
||||
uint8_t fis_type;
|
||||
uint8_t pmport : 4;
|
||||
uint8_t rsv0 : 2;
|
||||
uint8_t i : 1;
|
||||
uint8_t n : 1;
|
||||
uint8_t statusl : 4;
|
||||
uint8_t rsv1 : 4;
|
||||
uint8_t statush : 4;
|
||||
uint8_t rsv2 : 4;
|
||||
uint8_t error;
|
||||
uint32_t protocol;
|
||||
} FIS_DEV_BITS;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HBA structures (volatile - MMIO)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Received FIS structure in host memory
|
||||
typedef volatile struct {
|
||||
FIS_DMA_SETUP dsfis; // 0x00 DMA Setup FIS
|
||||
uint8_t pad0[4];
|
||||
FIS_PIO_SETUP psfis; // 0x20 PIO Setup FIS
|
||||
uint8_t pad1[12];
|
||||
FIS_REG_D2H rfis; // 0x40 Register D2H FIS
|
||||
uint8_t pad2[4];
|
||||
FIS_DEV_BITS sdbfis; // 0x58 Set Device Bits FIS
|
||||
uint8_t ufis[64]; // 0x60 Unknown FIS
|
||||
uint8_t rsv[0x100-0xA0];
|
||||
} HBA_FIS;
|
||||
|
||||
// Command List header (one entry = one slot)
|
||||
typedef struct {
|
||||
// DW0
|
||||
uint8_t cfl : 5; // Command FIS length in DWORDs (2..16)
|
||||
uint8_t a : 1; // ATAPI
|
||||
uint8_t w : 1; // Write (H2D=1, D2H=0)
|
||||
uint8_t p : 1; // Prefetchable
|
||||
uint8_t r : 1; // Reset
|
||||
uint8_t b : 1; // BIST
|
||||
uint8_t c : 1; // Clear busy on R_OK
|
||||
uint8_t rsv0 : 1;
|
||||
uint8_t pmp : 4; // Port multiplier port
|
||||
uint16_t prdtl; // PRDT length in entries
|
||||
// DW1
|
||||
volatile uint32_t prdbc; // PRDT byte count transferred
|
||||
// DW2,3
|
||||
uint32_t ctba; // Command table base address (128-byte aligned)
|
||||
uint32_t ctbau; // Upper 32 bits
|
||||
// DW4-7
|
||||
uint32_t rsv1[4];
|
||||
} HBA_CMD_HEADER;
|
||||
|
||||
// Physical Region Descriptor Table entry
|
||||
typedef struct {
|
||||
uint32_t dba; // Data base address
|
||||
uint32_t dbau; // Upper 32 bits
|
||||
uint32_t rsv0;
|
||||
uint32_t dbc : 22; // Byte count (0-based, so actual = dbc+1)
|
||||
uint32_t rsv1 : 9;
|
||||
uint32_t i : 1; // Interrupt on completion
|
||||
} HBA_PRDT_ENTRY;
|
||||
|
||||
// Command Table
|
||||
typedef struct {
|
||||
uint8_t cfis[64]; // Command FIS (up to 64 bytes)
|
||||
uint8_t acmd[16]; // ATAPI command (12 or 16 bytes)
|
||||
uint8_t rsv[48]; // Reserved
|
||||
HBA_PRDT_ENTRY prdt_entry[1]; // PRDT entries (variable length)
|
||||
} HBA_CMD_TBL;
|
||||
#pragma pack(pop)
|
||||
|
||||
// Port registers
|
||||
typedef volatile struct {
|
||||
uint32_t clb; // 0x00 Command list base (1K-aligned)
|
||||
uint32_t clbu; // 0x04 Command list base upper
|
||||
uint32_t fb; // 0x08 FIS base (256-byte aligned)
|
||||
uint32_t fbu; // 0x0C FIS base upper
|
||||
uint32_t is; // 0x10 Interrupt status
|
||||
uint32_t ie; // 0x14 Interrupt enable
|
||||
uint32_t cmd; // 0x18 Command and status
|
||||
uint32_t rsv0; // 0x1C Reserved
|
||||
uint32_t tfd; // 0x20 Task file data
|
||||
uint32_t sig; // 0x24 Signature
|
||||
uint32_t ssts; // 0x28 SATA status
|
||||
uint32_t sctl; // 0x2C SATA control
|
||||
uint32_t serr; // 0x30 SATA error
|
||||
uint32_t sact; // 0x34 SATA active
|
||||
uint32_t ci; // 0x38 Command issue
|
||||
uint32_t sntf; // 0x3C SATA notification
|
||||
uint32_t fbs; // 0x40 FIS-based switch control
|
||||
uint32_t rsv1[11];
|
||||
uint32_t vendor[4];
|
||||
} HBA_PORT;
|
||||
|
||||
// Generic Host Control registers (starts at ABAR)
|
||||
typedef volatile struct {
|
||||
uint32_t cap; // 0x00 Host capability
|
||||
uint32_t ghc; // 0x04 Global host control
|
||||
uint32_t is; // 0x08 Interrupt status
|
||||
uint32_t pi; // 0x0C Ports implemented
|
||||
uint32_t vs; // 0x10 Version
|
||||
uint32_t ccc_ctl; // 0x14 CCC control
|
||||
uint32_t ccc_pts; // 0x18 CCC ports
|
||||
uint32_t em_loc; // 0x1C Enclosure mgmt location
|
||||
uint32_t em_ctl; // 0x20 Enclosure mgmt control
|
||||
uint32_t cap2; // 0x24 Extended capabilities
|
||||
uint32_t bohc; // 0x28 BIOS/OS handoff control
|
||||
uint8_t rsv[0xA0-0x2C];
|
||||
uint8_t vendor[0x100-0xA0];
|
||||
HBA_PORT ports[32]; // 0x100 Port control registers
|
||||
} HBA_MEM;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// PCI
|
||||
#define PCI_AHCI_CLASS 0x01
|
||||
#define PCI_AHCI_SUBCLASS 0x06
|
||||
#define PCI_CMD_REG 0x04
|
||||
#define PCI_BAR5_REG 0x24
|
||||
#define PCI_CMD_BUSMASTER (1 << 2)
|
||||
#define PCI_CMD_MEMSPACE (1 << 1)
|
||||
|
||||
// GHC register bits
|
||||
#define GHC_AE (1u << 31) // AHCI Enable
|
||||
#define GHC_IE (1u << 1) // Interrupt Enable
|
||||
#define GHC_HR (1u << 0) // HBA Reset
|
||||
|
||||
// BOHC register bits
|
||||
#define BOHC_BOS (1u << 0) // BIOS-owned semaphore
|
||||
#define BOHC_OOS (1u << 1) // OS-owned semaphore
|
||||
#define BOHC_BB (1u << 4) // BIOS Busy
|
||||
|
||||
// CAP2 bits
|
||||
#define CAP2_BOH (1u << 0) // BIOS/OS handoff capable
|
||||
|
||||
// Port CMD register bits
|
||||
#define PORT_CMD_ST (1u << 0) // Start
|
||||
#define PORT_CMD_FRE (1u << 4) // FIS Receive Enable
|
||||
#define PORT_CMD_FR (1u << 14) // FIS Receive Running
|
||||
#define PORT_CMD_CR (1u << 15) // Command List Running
|
||||
#define PORT_CMD_POD (1u << 2) // Power On Device
|
||||
#define PORT_CMD_SUD (1u << 1) // Spin-Up Device
|
||||
#define PORT_CMD_ICC_ACTIVE (0x1u << 28)
|
||||
|
||||
// Port IS register bits (interrupt status)
|
||||
#define PORT_IS_TFES (1u << 30) // Task File Error Status
|
||||
#define PORT_IS_HBFS (1u << 29) // Host Bus Fatal Error
|
||||
#define PORT_IS_HBDS (1u << 28) // Host Bus Data Error
|
||||
#define PORT_IS_IFS (1u << 27) // Interface Fatal Error
|
||||
|
||||
// Port SSTS register
|
||||
#define PORT_DET_PRESENT 0x3
|
||||
#define PORT_IPM_ACTIVE 0x1
|
||||
|
||||
// Port signatures
|
||||
#define SATA_SIG_ATA 0x00000101
|
||||
#define SATA_SIG_ATAPI 0xEB140101
|
||||
#define SATA_SIG_SEMB 0xC33C0101
|
||||
#define SATA_SIG_PM 0x96690101
|
||||
|
||||
// ATA commands
|
||||
#define ATA_CMD_READ_DMA_EX 0x25
|
||||
#define ATA_CMD_WRITE_DMA_EX 0x35
|
||||
#define ATA_CMD_IDENTIFY 0xEC
|
||||
#define ATA_CMD_FLUSH_EXT 0xEA
|
||||
|
||||
// ATA status bits
|
||||
#define ATA_SR_BUSY 0x80
|
||||
#define ATA_SR_DRQ 0x08
|
||||
#define ATA_SR_ERR 0x01
|
||||
|
||||
// IDENTIFY word offsets
|
||||
#define ATA_IDENT_W83 83 // bit 10 = 48-bit LBA supported
|
||||
#define ATA_IDENT_W100 100 // 48-bit LBA sector count (4 words)
|
||||
#define ATA_IDENT_W60 60 // 28-bit LBA sector count (2 words)
|
||||
|
||||
// Layout constants (per port, single contiguous physical allocation)
|
||||
// Offset 0: Command List 1024 bytes (32 × 32-byte headers)
|
||||
// Offset 1024: Received FIS 256 bytes
|
||||
// Offset 2048: Command Tables 32 × 256 bytes = 8192 bytes
|
||||
// Total: 10240 bytes → 3 pages (12288 bytes)
|
||||
#define PORT_PAGES_STRUCT 3
|
||||
#define PORT_OFF_CMDLIST 0
|
||||
#define PORT_OFF_FIS 1024
|
||||
#define PORT_OFF_CMDTBLS 2048
|
||||
#define PORT_CMDTBL_SIZE 256 // 64 CFIS + 16 ACMD + 48 rsv + 8×16 PRDT
|
||||
#define PORT_PRDT_COUNT 8 // PRDT entries per command table
|
||||
|
||||
// Bounce buffer: 128 sectors × 512 bytes = 64 KB = 16 pages
|
||||
#define BOUNCE_SECTORS 128
|
||||
#define BOUNCE_BYTES (BOUNCE_SECTORS * 512)
|
||||
#define BOUNCE_PAGES (BOUNCE_BYTES / 4096)
|
||||
|
||||
// Polling timeout (spin iterations)
|
||||
#define POLL_TIMEOUT 1000000
|
||||
|
||||
#define MAX_AHCI_DRIVES 8
|
||||
#define NUM_CMD_SLOTS 32
|
||||
|
||||
// Map MMIO physical range into the physical window as uncacheable.
|
||||
// Must be called before any pointer dereference via PhysToVirt().
|
||||
// AHCI MMIO layout: 0x100 GHC + 32*0x80 ports = 0x1100 bytes → 2 pages minimum.
|
||||
// We map 8 pages (32KB) without probing the BAR size.
|
||||
#define AHCI_MMIO_PAGES 8
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Driver-global state
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static const HAL_API *g_hal;
|
||||
static HBA_MEM *g_hba; // HBA MMIO base (set during init)
|
||||
static uint8_t g_irq; // PCI IRQ line for this HBA
|
||||
static DRIVER_OBJECT g_driver; // AHCI disk driver object
|
||||
|
||||
// Per-drive context
|
||||
typedef struct {
|
||||
HBA_PORT *port; // MMIO port registers (virtual)
|
||||
uint64_t sectorCount; // Total LBA sectors on device
|
||||
uint8_t portNo; // Physical port index (for ISR dispatch)
|
||||
|
||||
// Structures block (command list + FIS + cmd tables)
|
||||
uint64_t structPhys; // Physical base
|
||||
void *structVirt; // Virtual base
|
||||
|
||||
// Bounce buffer for DMA data
|
||||
uint64_t bouncePhys;
|
||||
void *bounceVirt;
|
||||
|
||||
// Signaled by the completion DPC when the port raises an interrupt.
|
||||
// Auto-reset: one Wait consumes one completion.
|
||||
EVENT *completionEvent;
|
||||
|
||||
// Last IS snapshot captured by the ISR (for error checking post-wait).
|
||||
volatile uint32_t lastIs;
|
||||
|
||||
// Bottom-half used to wake the worker outside hard IRQ/DIRQL context.
|
||||
KDPC completionDpc;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// IRP queue - async dispatch
|
||||
// -----------------------------------------------------------------------
|
||||
// AhciWorker blocks here waiting for work. AHCI dispatch enqueues an IRP
|
||||
// and signals the semaphore to wake the worker.
|
||||
IRP_QUEUE irpQueue;
|
||||
THREAD *workerThread;
|
||||
DEVICE_OBJECT *deviceObject;
|
||||
|
||||
// NCQ TODO: for multi-slot NCQ, add:
|
||||
// uint32_t slotMask; // bitmask of in-flight slots
|
||||
// IRP *slotIrp[NUM_CMD_SLOTS]; // slot → pending IRP
|
||||
// uint64_t slotBouncePhys[NUM_CMD_SLOTS];
|
||||
// void *slotBounceVirt[NUM_CMD_SLOTS];
|
||||
// volatile uint32_t prevSact; // previous port->sact snapshot
|
||||
} AHCI_DRIVE;
|
||||
|
||||
static AHCI_DRIVE g_drives[MAX_AHCI_DRIVES];
|
||||
static int g_driveCount;
|
||||
const HAL_API *g_hal;
|
||||
HBA_MEM *g_hba;
|
||||
uint8_t g_irq;
|
||||
DRIVER_OBJECT g_driver;
|
||||
AHCI_DRIVE g_drives[MAX_AHCI_DRIVES];
|
||||
int g_driveCount;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
static KSTATUS ahci_map_mmio(uint64_t physBase) {
|
||||
for (int i = 0; i < AHCI_MMIO_PAGES; i++) {
|
||||
uint64_t phys = physBase + (uint64_t)i * 4096;
|
||||
uintptr_t virt = (uintptr_t)g_hal->PhysToVirt(phys);
|
||||
uint64_t flags = PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE;
|
||||
KSTATUS st = MmMapPage(virt, phys, flags);
|
||||
// Ignore STATUS_ALREADY_EXISTS - a prior mapping with different flags
|
||||
// is possible for the first page if the bootloader was overly generous.
|
||||
// Use MmSetPageProtect to force NOCACHE in that case.
|
||||
if (st == STATUS_ALREADY_EXISTS) {
|
||||
st = MmSetPageProtect(virt, 4096, flags);
|
||||
}
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("AHCI: MmMapPage failed for page %d (phys=0x%llx, st=0x%x)\n",
|
||||
i, (unsigned long long)phys, st);
|
||||
return st;
|
||||
}
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Accessors for port structure memory at known offsets from structVirt/Phys
|
||||
static inline HBA_CMD_HEADER *cmd_list(AHCI_DRIVE *drv) {
|
||||
@@ -460,29 +42,7 @@ static inline uint64_t cmd_tbl_phys(AHCI_DRIVE *drv, int slot) {
|
||||
// Port engine start / stop (polling safe)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void port_stop(HBA_PORT *port) {
|
||||
// Clear ST (bit 0)
|
||||
port->cmd &= ~PORT_CMD_ST;
|
||||
// Wait for CR (bit 15) to clear (command list DMA stops)
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++) {
|
||||
if (!(port->cmd & PORT_CMD_CR)) break;
|
||||
}
|
||||
// Clear FRE (bit 4)
|
||||
port->cmd &= ~PORT_CMD_FRE;
|
||||
// Wait for FR (bit 14) to clear
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++) {
|
||||
if (!(port->cmd & PORT_CMD_FR)) break;
|
||||
}
|
||||
}
|
||||
|
||||
static void port_start(HBA_PORT *port) {
|
||||
// Wait until CR is clear before setting ST
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++) {
|
||||
if (!(port->cmd & PORT_CMD_CR)) break;
|
||||
}
|
||||
port->cmd |= PORT_CMD_FRE;
|
||||
port->cmd |= PORT_CMD_ST;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Port memory initialisation
|
||||
@@ -878,100 +438,16 @@ static void AhciWorker(uintptr_t arg) {
|
||||
// AHCI controller initialisation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static KSTATUS hba_reset(HBA_MEM *hba) {
|
||||
// BIOS/OS handoff (spec §10.6.3)
|
||||
if (hba->cap2 & CAP2_BOH) {
|
||||
hba->bohc |= BOHC_OOS;
|
||||
// Wait for BIOS to clear BOS
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++) {
|
||||
if (!(hba->bohc & BOHC_BOS)) break;
|
||||
}
|
||||
// Give the BIOS another moment to clear Busy
|
||||
for (int i = 0; i < 800000; i++) {
|
||||
if (!(hba->bohc & BOHC_BB)) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable AHCI mode before reset so the reset actually takes effect
|
||||
hba->ghc |= GHC_AE;
|
||||
|
||||
// HBA reset
|
||||
hba->ghc |= GHC_HR;
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++) {
|
||||
if (!(hba->ghc & GHC_HR)) break;
|
||||
}
|
||||
if (hba->ghc & GHC_HR) {
|
||||
DbgPrint("AHCI: HBA reset timed out\n");
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
// Re-enable AHCI mode (reset cleared it), then enable global interrupts.
|
||||
// GHC.IE lets the HBA assert its PCI interrupt line when a port fires.
|
||||
hba->ghc |= GHC_AE | GHC_IE;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Before memory init: only check physical link. Signature is not yet valid.
|
||||
static bool port_link_up(HBA_PORT *port) {
|
||||
uint32_t det = port->ssts & 0x0F;
|
||||
uint32_t ipm = (port->ssts >> 8) & 0x0F;
|
||||
return (det == PORT_DET_PRESENT && ipm == PORT_IPM_ACTIVE);
|
||||
}
|
||||
|
||||
// After COMRESET + FIS receive: check signature for SATA disk.
|
||||
static bool port_is_sata_disk(HBA_PORT *port) {
|
||||
return port->sig == SATA_SIG_ATA;
|
||||
}
|
||||
|
||||
static void ahci_delay_ms(uint32_t ms) {
|
||||
for (uint32_t i = 0; i < ms * 1000; i++)
|
||||
__outbyte(0x80, 0);
|
||||
}
|
||||
|
||||
// Issue a COMRESET on the port, wait for link re-establishment and D2H FIS.
|
||||
// FRE must already be set (port_init_memory does this) so the HBA can
|
||||
// capture the signature FIS the device sends after the reset.
|
||||
static KSTATUS port_comreset(AHCI_DRIVE *drv) {
|
||||
HBA_PORT *port = drv->port;
|
||||
|
||||
port->cmd &= ~PORT_CMD_ST;
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++)
|
||||
if (!(port->cmd & PORT_CMD_CR)) break;
|
||||
|
||||
// Assert COMRESET: SControl.DET = 1, hold for ≥ 1ms
|
||||
port->sctl = (port->sctl & ~0x0Fu) | 0x1u;
|
||||
ahci_delay_ms(1);
|
||||
|
||||
// Deassert: DET = 0
|
||||
port->sctl = port->sctl & ~0x0Fu;
|
||||
|
||||
// Poll for DET = 3, up to 1 second
|
||||
bool link_ok = false;
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
if ((port->ssts & 0x0Fu) == PORT_DET_PRESENT) { link_ok = true; break; }
|
||||
ahci_delay_ms(1);
|
||||
}
|
||||
if (!link_ok) {
|
||||
DbgPrint("AHCI: COMRESET: link did not come back (ssts=0x%x)\n", port->ssts);
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
port->serr = (uint32_t)-1;
|
||||
port->is = (uint32_t)-1;
|
||||
port->cmd |= PORT_CMD_ST;
|
||||
|
||||
// Wait up to 500ms for a valid signature (D2H FIS from device)
|
||||
for (int i = 0; i < 500; i++) {
|
||||
uint32_t sig = port->sig;
|
||||
if (sig != 0xFFFFFFFFu && sig != 0x00000000u) break;
|
||||
ahci_delay_ms(1);
|
||||
}
|
||||
|
||||
DbgPrint("AHCI: port COMRESET done: sig=0x%08x ssts=0x%08x\n",
|
||||
port->sig, port->ssts);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// PORT_IE bits we care about:
|
||||
// DHRS (bit 0) - D2H Register FIS received - normal completion
|
||||
@@ -1032,7 +508,7 @@ KSTATUS AhciInitialize(const HAL_API *hal) {
|
||||
if (!KSUCCESS(st)) return st;
|
||||
|
||||
g_hba = (HBA_MEM *)hal->PhysToVirt(abarPhys);
|
||||
DbgPrint("AHCI: ABAR phys=0x%llx version=0x%08x\n",
|
||||
DbgPrint("AHCI: ABAR phys=0x%016llX version=0x%08x\n",
|
||||
(unsigned long long)abarPhys, g_hba->vs);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
// ahci.h - shared AHCI driver internals.
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_irp.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef enum {
|
||||
FIS_TYPE_REG_H2D = 0x27,
|
||||
FIS_TYPE_REG_D2H = 0x34,
|
||||
FIS_TYPE_DMA_ACT = 0x39,
|
||||
FIS_TYPE_DMA_SETUP = 0x41,
|
||||
FIS_TYPE_DATA = 0x46,
|
||||
FIS_TYPE_BIST = 0x58,
|
||||
FIS_TYPE_PIO_SETUP = 0x5F,
|
||||
FIS_TYPE_DEV_BITS = 0xA1,
|
||||
} FIS_TYPE;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
// Register FIS - Host to Device (H2D)
|
||||
typedef struct {
|
||||
uint8_t fis_type; // FIS_TYPE_REG_H2D
|
||||
uint8_t pmport : 4;
|
||||
uint8_t rsv0 : 3;
|
||||
uint8_t c : 1; // 1 = Command, 0 = Control
|
||||
uint8_t command;
|
||||
uint8_t featurel;
|
||||
// DW1
|
||||
uint8_t lba0, lba1, lba2;
|
||||
uint8_t device;
|
||||
// DW2
|
||||
uint8_t lba3, lba4, lba5;
|
||||
uint8_t featureh;
|
||||
// DW3
|
||||
uint8_t countl, counth;
|
||||
uint8_t icc;
|
||||
uint8_t control;
|
||||
// DW4
|
||||
uint8_t rsv1[4];
|
||||
} FIS_REG_H2D;
|
||||
|
||||
// Register FIS - Device to Host (D2H)
|
||||
typedef struct {
|
||||
uint8_t fis_type;
|
||||
uint8_t pmport : 4;
|
||||
uint8_t rsv0 : 2;
|
||||
uint8_t i : 1;
|
||||
uint8_t rsv1 : 1;
|
||||
uint8_t status;
|
||||
uint8_t error;
|
||||
uint8_t lba0, lba1, lba2;
|
||||
uint8_t device;
|
||||
uint8_t lba3, lba4, lba5;
|
||||
uint8_t rsv2;
|
||||
uint8_t countl, counth;
|
||||
uint8_t rsv3[2];
|
||||
uint8_t rsv4[4];
|
||||
} FIS_REG_D2H;
|
||||
|
||||
// DMA Setup FIS
|
||||
typedef struct {
|
||||
uint8_t fis_type;
|
||||
uint8_t pmport : 4;
|
||||
uint8_t rsv0 : 1;
|
||||
uint8_t d : 1;
|
||||
uint8_t i : 1;
|
||||
uint8_t a : 1;
|
||||
uint8_t rsved[2];
|
||||
uint64_t DMAbufferID;
|
||||
uint32_t rsvd;
|
||||
uint32_t DMAbufOffset;
|
||||
uint32_t TransferCount;
|
||||
uint32_t resvd;
|
||||
} FIS_DMA_SETUP;
|
||||
|
||||
// PIO Setup FIS
|
||||
typedef struct {
|
||||
uint8_t fis_type;
|
||||
uint8_t pmport : 4;
|
||||
uint8_t rsv0 : 1;
|
||||
uint8_t d : 1;
|
||||
uint8_t i : 1;
|
||||
uint8_t rsv1 : 1;
|
||||
uint8_t status;
|
||||
uint8_t error;
|
||||
uint8_t lba0, lba1, lba2;
|
||||
uint8_t device;
|
||||
uint8_t lba3, lba4, lba5;
|
||||
uint8_t rsv2;
|
||||
uint8_t countl, counth;
|
||||
uint8_t rsv3;
|
||||
uint8_t e_status;
|
||||
uint16_t tc;
|
||||
uint8_t rsv4[2];
|
||||
} FIS_PIO_SETUP;
|
||||
|
||||
// Set Device Bits FIS
|
||||
typedef struct {
|
||||
uint8_t fis_type;
|
||||
uint8_t pmport : 4;
|
||||
uint8_t rsv0 : 2;
|
||||
uint8_t i : 1;
|
||||
uint8_t n : 1;
|
||||
uint8_t statusl : 4;
|
||||
uint8_t rsv1 : 4;
|
||||
uint8_t statush : 4;
|
||||
uint8_t rsv2 : 4;
|
||||
uint8_t error;
|
||||
uint32_t protocol;
|
||||
} FIS_DEV_BITS;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HBA structures (volatile - MMIO)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Received FIS structure in host memory
|
||||
typedef volatile struct {
|
||||
FIS_DMA_SETUP dsfis; // 0x00 DMA Setup FIS
|
||||
uint8_t pad0[4];
|
||||
FIS_PIO_SETUP psfis; // 0x20 PIO Setup FIS
|
||||
uint8_t pad1[12];
|
||||
FIS_REG_D2H rfis; // 0x40 Register D2H FIS
|
||||
uint8_t pad2[4];
|
||||
FIS_DEV_BITS sdbfis; // 0x58 Set Device Bits FIS
|
||||
uint8_t ufis[64]; // 0x60 Unknown FIS
|
||||
uint8_t rsv[0x100-0xA0];
|
||||
} HBA_FIS;
|
||||
|
||||
// Command List header (one entry = one slot)
|
||||
typedef struct {
|
||||
// DW0
|
||||
uint8_t cfl : 5; // Command FIS length in DWORDs (2..16)
|
||||
uint8_t a : 1; // ATAPI
|
||||
uint8_t w : 1; // Write (H2D=1, D2H=0)
|
||||
uint8_t p : 1; // Prefetchable
|
||||
uint8_t r : 1; // Reset
|
||||
uint8_t b : 1; // BIST
|
||||
uint8_t c : 1; // Clear busy on R_OK
|
||||
uint8_t rsv0 : 1;
|
||||
uint8_t pmp : 4; // Port multiplier port
|
||||
uint16_t prdtl; // PRDT length in entries
|
||||
// DW1
|
||||
volatile uint32_t prdbc; // PRDT byte count transferred
|
||||
// DW2,3
|
||||
uint32_t ctba; // Command table base address (128-byte aligned)
|
||||
uint32_t ctbau; // Upper 32 bits
|
||||
// DW4-7
|
||||
uint32_t rsv1[4];
|
||||
} HBA_CMD_HEADER;
|
||||
|
||||
// Physical Region Descriptor Table entry
|
||||
typedef struct {
|
||||
uint32_t dba; // Data base address
|
||||
uint32_t dbau; // Upper 32 bits
|
||||
uint32_t rsv0;
|
||||
uint32_t dbc : 22; // Byte count (0-based, so actual = dbc+1)
|
||||
uint32_t rsv1 : 9;
|
||||
uint32_t i : 1; // Interrupt on completion
|
||||
} HBA_PRDT_ENTRY;
|
||||
|
||||
// Command Table
|
||||
typedef struct {
|
||||
uint8_t cfis[64]; // Command FIS (up to 64 bytes)
|
||||
uint8_t acmd[16]; // ATAPI command (12 or 16 bytes)
|
||||
uint8_t rsv[48]; // Reserved
|
||||
HBA_PRDT_ENTRY prdt_entry[1]; // PRDT entries (variable length)
|
||||
} HBA_CMD_TBL;
|
||||
#pragma pack(pop)
|
||||
|
||||
// Port registers
|
||||
typedef volatile struct {
|
||||
uint32_t clb; // 0x00 Command list base (1K-aligned)
|
||||
uint32_t clbu; // 0x04 Command list base upper
|
||||
uint32_t fb; // 0x08 FIS base (256-byte aligned)
|
||||
uint32_t fbu; // 0x0C FIS base upper
|
||||
uint32_t is; // 0x10 Interrupt status
|
||||
uint32_t ie; // 0x14 Interrupt enable
|
||||
uint32_t cmd; // 0x18 Command and status
|
||||
uint32_t rsv0; // 0x1C Reserved
|
||||
uint32_t tfd; // 0x20 Task file data
|
||||
uint32_t sig; // 0x24 Signature
|
||||
uint32_t ssts; // 0x28 SATA status
|
||||
uint32_t sctl; // 0x2C SATA control
|
||||
uint32_t serr; // 0x30 SATA error
|
||||
uint32_t sact; // 0x34 SATA active
|
||||
uint32_t ci; // 0x38 Command issue
|
||||
uint32_t sntf; // 0x3C SATA notification
|
||||
uint32_t fbs; // 0x40 FIS-based switch control
|
||||
uint32_t rsv1[11];
|
||||
uint32_t vendor[4];
|
||||
} HBA_PORT;
|
||||
|
||||
// Generic Host Control registers (starts at ABAR)
|
||||
typedef volatile struct {
|
||||
uint32_t cap; // 0x00 Host capability
|
||||
uint32_t ghc; // 0x04 Global host control
|
||||
uint32_t is; // 0x08 Interrupt status
|
||||
uint32_t pi; // 0x0C Ports implemented
|
||||
uint32_t vs; // 0x10 Version
|
||||
uint32_t ccc_ctl; // 0x14 CCC control
|
||||
uint32_t ccc_pts; // 0x18 CCC ports
|
||||
uint32_t em_loc; // 0x1C Enclosure mgmt location
|
||||
uint32_t em_ctl; // 0x20 Enclosure mgmt control
|
||||
uint32_t cap2; // 0x24 Extended capabilities
|
||||
uint32_t bohc; // 0x28 BIOS/OS handoff control
|
||||
uint8_t rsv[0xA0-0x2C];
|
||||
uint8_t vendor[0x100-0xA0];
|
||||
HBA_PORT ports[32]; // 0x100 Port control registers
|
||||
} HBA_MEM;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// PCI
|
||||
#define PCI_AHCI_CLASS 0x01
|
||||
#define PCI_AHCI_SUBCLASS 0x06
|
||||
#define PCI_CMD_REG 0x04
|
||||
#define PCI_BAR5_REG 0x24
|
||||
#define PCI_CMD_BUSMASTER (1 << 2)
|
||||
#define PCI_CMD_MEMSPACE (1 << 1)
|
||||
|
||||
// GHC register bits
|
||||
#define GHC_AE (1u << 31) // AHCI Enable
|
||||
#define GHC_IE (1u << 1) // Interrupt Enable
|
||||
#define GHC_HR (1u << 0) // HBA Reset
|
||||
|
||||
// BOHC register bits
|
||||
#define BOHC_BOS (1u << 0) // BIOS-owned semaphore
|
||||
#define BOHC_OOS (1u << 1) // OS-owned semaphore
|
||||
#define BOHC_BB (1u << 4) // BIOS Busy
|
||||
|
||||
// CAP2 bits
|
||||
#define CAP2_BOH (1u << 0) // BIOS/OS handoff capable
|
||||
|
||||
// Port CMD register bits
|
||||
#define PORT_CMD_ST (1u << 0) // Start
|
||||
#define PORT_CMD_FRE (1u << 4) // FIS Receive Enable
|
||||
#define PORT_CMD_FR (1u << 14) // FIS Receive Running
|
||||
#define PORT_CMD_CR (1u << 15) // Command List Running
|
||||
#define PORT_CMD_POD (1u << 2) // Power On Device
|
||||
#define PORT_CMD_SUD (1u << 1) // Spin-Up Device
|
||||
#define PORT_CMD_ICC_ACTIVE (0x1u << 28)
|
||||
|
||||
// Port IS register bits (interrupt status)
|
||||
#define PORT_IS_TFES (1u << 30) // Task File Error Status
|
||||
#define PORT_IS_HBFS (1u << 29) // Host Bus Fatal Error
|
||||
#define PORT_IS_HBDS (1u << 28) // Host Bus Data Error
|
||||
#define PORT_IS_IFS (1u << 27) // Interface Fatal Error
|
||||
|
||||
// Port SSTS register
|
||||
#define PORT_DET_PRESENT 0x3
|
||||
#define PORT_IPM_ACTIVE 0x1
|
||||
|
||||
// Port signatures
|
||||
#define SATA_SIG_ATA 0x00000101
|
||||
#define SATA_SIG_ATAPI 0xEB140101
|
||||
#define SATA_SIG_SEMB 0xC33C0101
|
||||
#define SATA_SIG_PM 0x96690101
|
||||
|
||||
// ATA commands
|
||||
#define ATA_CMD_READ_DMA_EX 0x25
|
||||
#define ATA_CMD_WRITE_DMA_EX 0x35
|
||||
#define ATA_CMD_IDENTIFY 0xEC
|
||||
#define ATA_CMD_FLUSH_EXT 0xEA
|
||||
|
||||
// ATA status bits
|
||||
#define ATA_SR_BUSY 0x80
|
||||
#define ATA_SR_DRQ 0x08
|
||||
#define ATA_SR_ERR 0x01
|
||||
|
||||
// IDENTIFY word offsets
|
||||
#define ATA_IDENT_W83 83 // bit 10 = 48-bit LBA supported
|
||||
#define ATA_IDENT_W100 100 // 48-bit LBA sector count (4 words)
|
||||
#define ATA_IDENT_W60 60 // 28-bit LBA sector count (2 words)
|
||||
|
||||
// Layout constants (per port, single contiguous physical allocation)
|
||||
// Offset 0: Command List 1024 bytes (32 × 32-byte headers)
|
||||
// Offset 1024: Received FIS 256 bytes
|
||||
// Offset 2048: Command Tables 32 × 256 bytes = 8192 bytes
|
||||
// Total: 10240 bytes → 3 pages (12288 bytes)
|
||||
#define PORT_PAGES_STRUCT 3
|
||||
#define PORT_OFF_CMDLIST 0
|
||||
#define PORT_OFF_FIS 1024
|
||||
#define PORT_OFF_CMDTBLS 2048
|
||||
#define PORT_CMDTBL_SIZE 256 // 64 CFIS + 16 ACMD + 48 rsv + 8×16 PRDT
|
||||
#define PORT_PRDT_COUNT 8 // PRDT entries per command table
|
||||
|
||||
// Bounce buffer: 128 sectors × 512 bytes = 64 KB = 16 pages
|
||||
#define BOUNCE_SECTORS 128
|
||||
#define BOUNCE_BYTES (BOUNCE_SECTORS * 512)
|
||||
#define BOUNCE_PAGES (BOUNCE_BYTES / 4096)
|
||||
|
||||
// Polling timeout (spin iterations)
|
||||
#define POLL_TIMEOUT 1000000
|
||||
|
||||
#define MAX_AHCI_DRIVES 8
|
||||
#define NUM_CMD_SLOTS 32
|
||||
|
||||
// Map MMIO physical range into the physical window as uncacheable.
|
||||
// Must be called before any pointer dereference via PhysToVirt().
|
||||
// AHCI MMIO layout: 0x100 GHC + 32*0x80 ports = 0x1100 bytes → 2 pages minimum.
|
||||
// We map 8 pages (32KB) without probing the BAR size.
|
||||
#define AHCI_MMIO_PAGES 8
|
||||
|
||||
// Per-drive context
|
||||
typedef struct {
|
||||
HBA_PORT *port; // MMIO port registers (virtual)
|
||||
uint64_t sectorCount; // Total LBA sectors on device
|
||||
uint8_t portNo; // Physical port index (for ISR dispatch)
|
||||
|
||||
// Structures block (command list + FIS + cmd tables)
|
||||
uint64_t structPhys; // Physical base
|
||||
void* structVirt; // Virtual base
|
||||
|
||||
// Bounce buffer for DMA data
|
||||
uint64_t bouncePhys;
|
||||
void* bounceVirt;
|
||||
|
||||
// Signaled by the completion DPC when the port raises an interrupt.
|
||||
// Auto-reset: one Wait consumes one completion.
|
||||
EVENT *completionEvent;
|
||||
|
||||
// Last IS snapshot captured by the ISR (for error checking post-wait).
|
||||
volatile uint32_t lastIs;
|
||||
|
||||
// Bottom-half used to wake the worker outside hard IRQ/DIRQL context.
|
||||
KDPC completionDpc;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// IRP queue - async dispatch
|
||||
// -----------------------------------------------------------------------
|
||||
// AhciWorker blocks here waiting for work. AHCI dispatch enqueues an IRP
|
||||
// and signals the semaphore to wake the worker.
|
||||
IRP_QUEUE irpQueue;
|
||||
THREAD* workerThread;
|
||||
DEVICE_OBJECT* deviceObject;
|
||||
|
||||
// NCQ TODO: for multi-slot NCQ, add:
|
||||
// uint32_t slotMask; // bitmask of in-flight slots
|
||||
// IRP* slotIrp[NUM_CMD_SLOTS]; // slot → pending IRP
|
||||
// uint64_t slotBouncePhys[NUM_CMD_SLOTS];
|
||||
// void* slotBounceVirt[NUM_CMD_SLOTS];
|
||||
// volatile uint32_t prevSact; // previous port->sact snapshot
|
||||
} AHCI_DRIVE;
|
||||
|
||||
|
||||
extern const HAL_API *g_hal;
|
||||
extern HBA_MEM *g_hba;
|
||||
extern uint8_t g_irq;
|
||||
extern DRIVER_OBJECT g_driver;
|
||||
extern AHCI_DRIVE g_drives[MAX_AHCI_DRIVES];
|
||||
extern int g_driveCount;
|
||||
|
||||
KSTATUS ahci_map_mmio(uint64_t physBase);
|
||||
void port_stop(HBA_PORT *port);
|
||||
void port_start(HBA_PORT *port);
|
||||
KSTATUS hba_reset(HBA_MEM *hba);
|
||||
bool port_link_up(HBA_PORT *port);
|
||||
bool port_is_sata_disk(HBA_PORT *port);
|
||||
void ahci_delay_ms(uint32_t ms);
|
||||
KSTATUS port_comreset(AHCI_DRIVE *drv);
|
||||
@@ -0,0 +1,143 @@
|
||||
// ahci_port.c - AHCI MMIO mapping, HBA reset, and port link bring-up helpers.
|
||||
#include "ahci.h"
|
||||
|
||||
KSTATUS ahci_map_mmio(uint64_t physBase) {
|
||||
for (int i = 0; i < AHCI_MMIO_PAGES; i++) {
|
||||
uint64_t phys = physBase + (uint64_t)i * 4096;
|
||||
uintptr_t virt = (uintptr_t)g_hal->PhysToVirt(phys);
|
||||
uint64_t flags = PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE;
|
||||
KSTATUS st = MmMapPage(virt, phys, flags);
|
||||
// Ignore STATUS_ALREADY_EXISTS - a prior mapping with different flags
|
||||
// is possible for the first page if the bootloader was overly generous.
|
||||
// Use MmSetPageProtect to force NOCACHE in that case.
|
||||
if (st == STATUS_ALREADY_EXISTS) {
|
||||
st = MmSetPageProtect(virt, 4096, flags);
|
||||
}
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("AHCI: MmMapPage failed for page %d (phys=0x%016llX, st=0x%x)\n",
|
||||
i, (unsigned long long)phys, st);
|
||||
return st;
|
||||
}
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void port_stop(HBA_PORT *port) {
|
||||
// Clear ST (bit 0)
|
||||
port->cmd &= ~PORT_CMD_ST;
|
||||
// Wait for CR (bit 15) to clear (command list DMA stops)
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++) {
|
||||
if (!(port->cmd & PORT_CMD_CR)) break;
|
||||
}
|
||||
// Clear FRE (bit 4)
|
||||
port->cmd &= ~PORT_CMD_FRE;
|
||||
// Wait for FR (bit 14) to clear
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++) {
|
||||
if (!(port->cmd & PORT_CMD_FR)) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void port_start(HBA_PORT *port) {
|
||||
// Wait until CR is clear before setting ST
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++) {
|
||||
if (!(port->cmd & PORT_CMD_CR)) break;
|
||||
}
|
||||
port->cmd |= PORT_CMD_FRE;
|
||||
port->cmd |= PORT_CMD_ST;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS hba_reset(HBA_MEM *hba) {
|
||||
// BIOS/OS handoff (spec §10.6.3)
|
||||
if (hba->cap2 & CAP2_BOH) {
|
||||
hba->bohc |= BOHC_OOS;
|
||||
// Wait for BIOS to clear BOS
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++) {
|
||||
if (!(hba->bohc & BOHC_BOS)) break;
|
||||
}
|
||||
// Give the BIOS another moment to clear Busy
|
||||
for (int i = 0; i < 800000; i++) {
|
||||
if (!(hba->bohc & BOHC_BB)) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable AHCI mode before reset so the reset actually takes effect
|
||||
hba->ghc |= GHC_AE;
|
||||
|
||||
// HBA reset
|
||||
hba->ghc |= GHC_HR;
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++) {
|
||||
if (!(hba->ghc & GHC_HR)) break;
|
||||
}
|
||||
if (hba->ghc & GHC_HR) {
|
||||
DbgPrint("AHCI: HBA reset timed out\n");
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
// Re-enable AHCI mode (reset cleared it), then enable global interrupts.
|
||||
// GHC.IE lets the HBA assert its PCI interrupt line when a port fires.
|
||||
hba->ghc |= GHC_AE | GHC_IE;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
bool port_link_up(HBA_PORT *port) {
|
||||
uint32_t det = port->ssts & 0x0F;
|
||||
uint32_t ipm = (port->ssts >> 8) & 0x0F;
|
||||
return (det == PORT_DET_PRESENT && ipm == PORT_IPM_ACTIVE);
|
||||
}
|
||||
|
||||
|
||||
bool port_is_sata_disk(HBA_PORT *port) {
|
||||
return port->sig == SATA_SIG_ATA;
|
||||
}
|
||||
|
||||
|
||||
void ahci_delay_ms(uint32_t ms) {
|
||||
for (uint32_t i = 0; i < ms * 1000; i++)
|
||||
__outbyte(0x80, 0);
|
||||
}
|
||||
|
||||
|
||||
KSTATUS port_comreset(AHCI_DRIVE *drv) {
|
||||
HBA_PORT *port = drv->port;
|
||||
|
||||
port->cmd &= ~PORT_CMD_ST;
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++)
|
||||
if (!(port->cmd & PORT_CMD_CR)) break;
|
||||
|
||||
// Assert COMRESET: SControl.DET = 1, hold for ≥ 1ms
|
||||
port->sctl = (port->sctl & ~0x0Fu) | 0x1u;
|
||||
ahci_delay_ms(1);
|
||||
|
||||
// Deassert: DET = 0
|
||||
port->sctl = port->sctl & ~0x0Fu;
|
||||
|
||||
// Poll for DET = 3, up to 1 second
|
||||
bool link_ok = false;
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
if ((port->ssts & 0x0Fu) == PORT_DET_PRESENT) { link_ok = true; break; }
|
||||
ahci_delay_ms(1);
|
||||
}
|
||||
if (!link_ok) {
|
||||
DbgPrint("AHCI: COMRESET: link did not come back (ssts=0x%x)\n", port->ssts);
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
port->serr = (uint32_t)-1;
|
||||
port->is = (uint32_t)-1;
|
||||
port->cmd |= PORT_CMD_ST;
|
||||
|
||||
// Wait up to 500ms for a valid signature (D2H FIS from device)
|
||||
for (int i = 0; i < 500; i++) {
|
||||
uint32_t sig = port->sig;
|
||||
if (sig != 0xFFFFFFFFu && sig != 0x00000000u) break;
|
||||
ahci_delay_ms(1);
|
||||
}
|
||||
|
||||
DbgPrint("AHCI: port COMRESET done: sig=0x%08x ssts=0x%08x\n",
|
||||
port->sig, port->ssts);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
+19
-207
@@ -1,130 +1,26 @@
|
||||
// ide.c — IDE/ATA driver: PIO (sync fallback) + Bus Master DMA (async)
|
||||
//
|
||||
// Architecture
|
||||
// ------------
|
||||
// The dispatch path mirrors AHCI:
|
||||
// IoCallDriver → IdeDispatchReadWrite → IRP queue or inline PIO
|
||||
// IdeWorker → dequeues IRP, calls IdeDmaRw(), completes the IRP
|
||||
// IdeIsr → clears BM status, queues a DPC
|
||||
// IdeDpc → signals gDmaEvent to unblock the worker
|
||||
//
|
||||
// Graceful degradation: if Bus Master init fails (no PCI IDE controller,
|
||||
// BAR4 not assigned, PRDT allocation above 4 GB, etc.) the same device
|
||||
// object remains registered and dispatch completes IRPs inline through PIO.
|
||||
//
|
||||
// PRDT layout
|
||||
// -----------
|
||||
// One page allocated via MmAllocateContiguous. We build up to PRDT_MAX_ENTRIES
|
||||
// entries by walking the transfer buffer in page-sized chunks and looking up
|
||||
// each page's physical address via MmGetPhysicalAddress. Adjacent physical
|
||||
// pages are coalesced into a single PRD entry where possible.
|
||||
//
|
||||
// LBA48 vs LBA28
|
||||
// --------------
|
||||
// DMA commands exist in both modes: CMD_READ/WRITE_DMA_EXT (0x25/0x35) for
|
||||
// LBA48 and CMD_READ/WRITE_DMA (0xC8/0xCA) for LBA28. The existing
|
||||
// gSupportsLba48 flag picks the right variant.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_irp.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IDE register map (primary channel, base 0x1F0)
|
||||
// ---------------------------------------------------------------------------
|
||||
#define IDE_DATA 0x1F0
|
||||
#define IDE_ERR 0x1F1
|
||||
#define IDE_SECCNT 0x1F2
|
||||
#define IDE_LBA0 0x1F3
|
||||
#define IDE_LBA1 0x1F4
|
||||
#define IDE_LBA2 0x1F5
|
||||
#define IDE_DRIVE 0x1F6
|
||||
#define IDE_STATUS 0x1F7
|
||||
#define IDE_CMD 0x1F7
|
||||
#define IDE_ALT 0x3F6
|
||||
|
||||
#define IDE_BUSY 0x80
|
||||
#define IDE_READY 0x40
|
||||
#define IDE_ERR_F 0x01
|
||||
#define IDE_DRQ 0x08
|
||||
|
||||
// ATA commands
|
||||
#define CMD_READ_EXT 0x24 // PIO 48-bit read
|
||||
#define CMD_WRITE_EXT 0x34 // PIO 48-bit write
|
||||
#define CMD_READ_DMA_EXT 0x25 // DMA 48-bit read
|
||||
#define CMD_WRITE_DMA_EXT 0x35 // DMA 48-bit write
|
||||
#define CMD_READ_DMA 0xC8 // DMA 28-bit read
|
||||
#define CMD_WRITE_DMA 0xCA // DMA 28-bit write
|
||||
#define CMD_IDENT 0xEC
|
||||
|
||||
// IDE primary IRQ
|
||||
#define IDE_IRQ_PRIMARY 14
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Bus Master IDE register offsets (relative to gBmBase, primary channel)
|
||||
// ---------------------------------------------------------------------------
|
||||
#define BM_CMD_REG 0x00 // command
|
||||
#define BM_STATUS_REG 0x02 // status
|
||||
#define BM_PRDT_REG 0x04 // PRDT physical address (32-bit)
|
||||
|
||||
// BM_CMD_REG bits
|
||||
#define BM_CMD_START 0x01 // start DMA transfer
|
||||
#define BM_CMD_READ 0x08 // 1 = disk→memory (read), 0 = memory→disk (write)
|
||||
|
||||
// BM_STATUS_REG bits
|
||||
#define BM_STATUS_ACTIVE 0x01 // DMA active
|
||||
#define BM_STATUS_ERROR 0x02 // DMA error (write 1 to clear)
|
||||
#define BM_STATUS_IRQ 0x04 // interrupt pending (write 1 to clear)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Physical Region Descriptor (PRDT entry, 8 bytes)
|
||||
// The PRDT itself must not cross a 64 KB boundary and must be 4-byte aligned.
|
||||
// We allocate a full page so both constraints are trivially satisfied.
|
||||
// ---------------------------------------------------------------------------
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint32_t physAddr; // physical base of this memory region
|
||||
uint16_t byteCount; // byte count; 0 means 64 KB
|
||||
uint16_t flags; // bit 15: End Of Table
|
||||
} PRD;
|
||||
#pragma pack(pop)
|
||||
|
||||
#define PRD_EOT 0x8000u
|
||||
// 512 entries × 8 bytes = 4096 bytes = exactly one page; supports 512×64 KB = 32 MB
|
||||
#define PRDT_MAX_ENTRIES 512
|
||||
// ide.c - IDE/ATA disk I/O path and DriverEntry.
|
||||
#include "ide.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Module state
|
||||
// ---------------------------------------------------------------------------
|
||||
static const HAL_API *gHal;
|
||||
static DRIVER_OBJECT gDriver;
|
||||
static DEVICE_OBJECT *gDevice = NULL;
|
||||
static bool gDmaEnabled = false;
|
||||
static uint64_t gSectorCount = 0;
|
||||
static bool gSupportsLba48 = false;
|
||||
const HAL_API *gHal;
|
||||
DRIVER_OBJECT gDriver;
|
||||
DEVICE_OBJECT *gDevice = NULL;
|
||||
bool gDmaEnabled = false;
|
||||
uint64_t gSectorCount = 0;
|
||||
bool gSupportsLba48 = false;
|
||||
|
||||
// DMA hardware state
|
||||
static uint16_t gBmBase = 0; // Bus Master I/O base port (BAR4 & ~3)
|
||||
static PRD *gPrdtVirt = NULL; // kernel VA of PRDT page
|
||||
static uint32_t gPrdtPhys = 0; // 32-bit physical address of PRDT
|
||||
uint16_t gBmBase = 0; // Bus Master I/O base port (BAR4 & ~3)
|
||||
PRD *gPrdtVirt = NULL; // kernel VA of PRDT page
|
||||
uint32_t gPrdtPhys = 0; // 32-bit physical address of PRDT
|
||||
|
||||
// Async worker synchronisation
|
||||
static IRP_QUEUE gIrpQueue;
|
||||
static SEMAPHORE *gIrpSemaphore = NULL;
|
||||
static EVENT *gDmaEvent = NULL; // auto-reset; DPC signals after each xfer
|
||||
static KDPC gDmaDpc; // bottom half for DMA completion
|
||||
IRP_QUEUE gIrpQueue;
|
||||
SEMAPHORE *gIrpSemaphore = NULL;
|
||||
EVENT *gDmaEvent = NULL; // auto-reset; DPC signals after each xfer
|
||||
KDPC gDmaDpc; // bottom half for DMA completion
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Bus Master I/O helpers
|
||||
@@ -255,7 +151,7 @@ static uint32_t BuildPrdt(void *virtBuf, uint32_t byteLen) {
|
||||
PHYSICAL_ADDRESS phys = MmGetPhysicalAddress(va);
|
||||
if (phys >> 32) {
|
||||
// Physical address above 4 GB — BM IDE's 32-bit PRDT can't reach it.
|
||||
DbgPrint("IDE DMA: buffer page at VA 0x%llx has phys 0x%llx > 4 GB\n",
|
||||
DbgPrint("IDE DMA: buffer page at VA 0x%016llX has phys 0x%016llX > 4 GB\n",
|
||||
(uint64_t)va, (uint64_t)phys);
|
||||
return 0;
|
||||
}
|
||||
@@ -294,7 +190,7 @@ static uint32_t BuildPrdt(void *virtBuf, uint32_t byteLen) {
|
||||
// IDE DMA completion DPC. The ISR owns hardware acknowledgement; this bottom
|
||||
// half wakes the worker outside hard IRQ/DIRQL context.
|
||||
// ---------------------------------------------------------------------------
|
||||
static void IdeDmaCompletionDpc(KDPC *dpc, void *context,
|
||||
void IdeDmaCompletionDpc(KDPC *dpc, void *context,
|
||||
void *systemArgument1, void *systemArgument2) {
|
||||
(void)dpc;
|
||||
(void)context;
|
||||
@@ -312,7 +208,7 @@ static void IdeDmaCompletionDpc(KDPC *dpc, void *context,
|
||||
// interrupt bits here and queue a DPC to wake the worker thread.
|
||||
// The worker reads the final status and fires IRP completion hooks.
|
||||
// ---------------------------------------------------------------------------
|
||||
static void IdeIsr(void) {
|
||||
void IdeIsr(void) {
|
||||
uint8_t bmSt = BmRead8(BM_STATUS_REG);
|
||||
if (!(bmSt & BM_STATUS_IRQ))
|
||||
return; // not our interrupt (spurious or secondary channel)
|
||||
@@ -417,7 +313,7 @@ static KSTATUS IdeDmaRw(uint64_t lba, uint32_t count, void *buf, bool write) {
|
||||
// Blocks on gIrpSemaphore. Each IrpQueueEnqueue increments it by one, so
|
||||
// the worker wakes once per submitted IRP. Mirrors AhciWorker exactly.
|
||||
// ---------------------------------------------------------------------------
|
||||
static void IdeWorker(uintptr_t arg) {
|
||||
void IdeWorker(uintptr_t arg) {
|
||||
(void)arg;
|
||||
while (true) {
|
||||
KeWaitSemaphore(gIrpSemaphore);
|
||||
@@ -473,90 +369,6 @@ static KSTATUS IdeDispatchReadWrite(DEVICE_OBJECT *device, IRP *irp) {
|
||||
// Returns true on success. On any failure, logs a message and returns false;
|
||||
// dispatch will complete IRPs inline through PIO.
|
||||
// ---------------------------------------------------------------------------
|
||||
static bool IdeDmaInit(void) {
|
||||
// ---- Find PCI IDE controller (class 0x01, subclass 0x01) --------------
|
||||
uint8_t bus, dev, fn;
|
||||
if (!gHal->Pci.FindClass(0x01, 0x01, &bus, &dev, &fn)) {
|
||||
DbgPrint("IDE DMA: No PCI IDE controller found — using PIO\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---- Read BAR4 (Bus Master base, I/O space) ---------------------------
|
||||
// PCI config offset 0x20. Bit 0 must be 1 for I/O BAR.
|
||||
uint32_t bar4 = gHal->Pci.Read32(bus, dev, fn, 0x20);
|
||||
if (!(bar4 & 0x1)) {
|
||||
DbgPrint("IDE DMA: BAR4 is a memory BAR (0x%08x) — unexpected, using PIO\n", bar4);
|
||||
return false;
|
||||
}
|
||||
uint16_t bmBase = (uint16_t)(bar4 & 0xFFFC);
|
||||
if (bmBase == 0) {
|
||||
DbgPrint("IDE DMA: BAR4 is zero (firmware didn't assign it) — using PIO\n");
|
||||
return false;
|
||||
}
|
||||
DbgPrint("IDE DMA: Bus Master base = 0x%04X (bus %u, dev %u, fn %u)\n",
|
||||
bmBase, bus, dev, fn);
|
||||
|
||||
// ---- Enable Bus Mastering in PCI Command register (bit 2) ------------
|
||||
uint32_t pciCmd = gHal->Pci.Read32(bus, dev, fn, 0x04);
|
||||
gHal->Pci.Write32(bus, dev, fn, 0x04, pciCmd | (1u << 2));
|
||||
|
||||
// ---- Allocate PRDT page (must be 32-bit physical, 4-byte aligned) ----
|
||||
PHYSICAL_ADDRESS prdtPhys = MmAllocateContiguous(1);
|
||||
if (!prdtPhys) {
|
||||
DbgPrint("IDE DMA: Failed to allocate PRDT page\n");
|
||||
return false;
|
||||
}
|
||||
if (prdtPhys >> 32) {
|
||||
// MmAllocateContiguous returned memory above 4 GB — BM IDE can't
|
||||
// address it. This shouldn't happen on normal x86 systems with early
|
||||
// boot allocations but we check defensively.
|
||||
DbgPrint("IDE DMA: PRDT page is above 4 GB (0x%llx) — using PIO\n",
|
||||
(uint64_t)prdtPhys);
|
||||
return false;
|
||||
}
|
||||
void *prdtVirt = gHal->PhysToVirt(prdtPhys);
|
||||
DbgPrint("IDE DMA: PRDT at phys 0x%08x, virt %p\n", (uint32_t)prdtPhys, prdtVirt);
|
||||
|
||||
// ---- Create synchronisation objects ------------------------------------
|
||||
SEMAPHORE *sem = NULL;
|
||||
if (!KSUCCESS(KeSemaphoreCreate(0, 0x7FFFFFFF, &sem))) {
|
||||
DbgPrint("IDE DMA: Failed to create IRP semaphore\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
EVENT *dmaEv = NULL;
|
||||
if (!KSUCCESS(KeEventCreate(EventAutoReset, 0, &dmaEv))) {
|
||||
DbgPrint("IDE DMA: Failed to create DMA completion event\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---- Commit module state -----------------------------------------------
|
||||
gBmBase = bmBase;
|
||||
gPrdtPhys = (uint32_t)prdtPhys;
|
||||
gPrdtVirt = (PRD *)prdtVirt;
|
||||
gIrpSemaphore = sem;
|
||||
gDmaEvent = dmaEv;
|
||||
KeInitializeDpc(&gDmaDpc, IdeDmaCompletionDpc, (void *)0);
|
||||
|
||||
IrpQueueInit(&gIrpQueue, sem);
|
||||
|
||||
// ---- Register IRQ 14 --------------------------------------------------
|
||||
KeIrqRegister(IDE_IRQ_PRIMARY, IdeIsr);
|
||||
DbgPrint("IDE DMA: IRQ %u registered\n", IDE_IRQ_PRIMARY);
|
||||
|
||||
// ---- Spawn worker thread -----------------------------------------------
|
||||
KSTATUS st = KeCreateKernelThread((uintptr_t)IdeWorker, 0,
|
||||
PriorityHigh, (THREAD **)NULL);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("IDE DMA: Worker thread creation failed (0x%x)\n", st);
|
||||
// Not fatal for init — the PRDT/IRQ are live but async callers will
|
||||
// never get completions. Better to surface the failure clearly.
|
||||
return false;
|
||||
}
|
||||
|
||||
DbgPrint("IDE DMA: Init complete\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DriverEntry
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// ide.h - shared IDE/ATA driver internals.
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_irp.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IDE register map (primary channel, base 0x1F0)
|
||||
// ---------------------------------------------------------------------------
|
||||
#define IDE_DATA 0x1F0
|
||||
#define IDE_ERR 0x1F1
|
||||
#define IDE_SECCNT 0x1F2
|
||||
#define IDE_LBA0 0x1F3
|
||||
#define IDE_LBA1 0x1F4
|
||||
#define IDE_LBA2 0x1F5
|
||||
#define IDE_DRIVE 0x1F6
|
||||
#define IDE_STATUS 0x1F7
|
||||
#define IDE_CMD 0x1F7
|
||||
#define IDE_ALT 0x3F6
|
||||
|
||||
#define IDE_BUSY 0x80
|
||||
#define IDE_READY 0x40
|
||||
#define IDE_ERR_F 0x01
|
||||
#define IDE_DRQ 0x08
|
||||
|
||||
// ATA commands
|
||||
#define CMD_READ_EXT 0x24 // PIO 48-bit read
|
||||
#define CMD_WRITE_EXT 0x34 // PIO 48-bit write
|
||||
#define CMD_READ_DMA_EXT 0x25 // DMA 48-bit read
|
||||
#define CMD_WRITE_DMA_EXT 0x35 // DMA 48-bit write
|
||||
#define CMD_READ_DMA 0xC8 // DMA 28-bit read
|
||||
#define CMD_WRITE_DMA 0xCA // DMA 28-bit write
|
||||
#define CMD_IDENT 0xEC
|
||||
|
||||
// IDE primary IRQ
|
||||
#define IDE_IRQ_PRIMARY 14
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Bus Master IDE register offsets (relative to gBmBase, primary channel)
|
||||
// ---------------------------------------------------------------------------
|
||||
#define BM_CMD_REG 0x00 // command
|
||||
#define BM_STATUS_REG 0x02 // status
|
||||
#define BM_PRDT_REG 0x04 // PRDT physical address (32-bit)
|
||||
|
||||
// BM_CMD_REG bits
|
||||
#define BM_CMD_START 0x01 // start DMA transfer
|
||||
#define BM_CMD_READ 0x08 // 1 = disk→memory (read), 0 = memory→disk (write)
|
||||
|
||||
// BM_STATUS_REG bits
|
||||
#define BM_STATUS_ACTIVE 0x01 // DMA active
|
||||
#define BM_STATUS_ERROR 0x02 // DMA error (write 1 to clear)
|
||||
#define BM_STATUS_IRQ 0x04 // interrupt pending (write 1 to clear)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Physical Region Descriptor (PRDT entry, 8 bytes)
|
||||
// The PRDT itself must not cross a 64 KB boundary and must be 4-byte aligned.
|
||||
// We allocate a full page so both constraints are trivially satisfied.
|
||||
// ---------------------------------------------------------------------------
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint32_t physAddr; // physical base of this memory region
|
||||
uint16_t byteCount; // byte count; 0 means 64 KB
|
||||
uint16_t flags; // bit 15: End Of Table
|
||||
} PRD;
|
||||
#pragma pack(pop)
|
||||
|
||||
#define PRD_EOT 0x8000u
|
||||
// 512 entries × 8 bytes = 4096 bytes = exactly one page; supports 512×64 KB = 32 MB
|
||||
#define PRDT_MAX_ENTRIES 512
|
||||
|
||||
|
||||
extern const HAL_API *gHal;
|
||||
extern DRIVER_OBJECT gDriver;
|
||||
extern DEVICE_OBJECT *gDevice;
|
||||
extern bool gDmaEnabled;
|
||||
extern uint64_t gSectorCount;
|
||||
extern bool gSupportsLba48;
|
||||
extern uint16_t gBmBase;
|
||||
extern PRD *gPrdtVirt;
|
||||
extern uint32_t gPrdtPhys;
|
||||
extern IRP_QUEUE gIrpQueue;
|
||||
extern SEMAPHORE *gIrpSemaphore;
|
||||
extern EVENT *gDmaEvent;
|
||||
extern KDPC gDmaDpc;
|
||||
|
||||
void IdeDmaCompletionDpc(KDPC *dpc, void *context, void *systemArgument1, void *systemArgument2);
|
||||
void IdeIsr(void);
|
||||
void IdeWorker(uintptr_t arg);
|
||||
bool IdeDmaInit(void);
|
||||
@@ -0,0 +1,87 @@
|
||||
// ide_bus.c - Bus-master IDE PCI/DMA initialisation.
|
||||
#include "ide.h"
|
||||
|
||||
bool IdeDmaInit(void) {
|
||||
// ---- Find PCI IDE controller (class 0x01, subclass 0x01) --------------
|
||||
uint8_t bus, dev, fn;
|
||||
if (!gHal->Pci.FindClass(0x01, 0x01, &bus, &dev, &fn)) {
|
||||
DbgPrint("IDE DMA: No PCI IDE controller found — using PIO\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---- Read BAR4 (Bus Master base, I/O space) ---------------------------
|
||||
// PCI config offset 0x20. Bit 0 must be 1 for I/O BAR.
|
||||
uint32_t bar4 = gHal->Pci.Read32(bus, dev, fn, 0x20);
|
||||
if (!(bar4 & 0x1)) {
|
||||
DbgPrint("IDE DMA: BAR4 is a memory BAR (0x%08x) — unexpected, using PIO\n", bar4);
|
||||
return false;
|
||||
}
|
||||
uint16_t bmBase = (uint16_t)(bar4 & 0xFFFC);
|
||||
if (bmBase == 0) {
|
||||
DbgPrint("IDE DMA: BAR4 is zero (firmware didn't assign it) — using PIO\n");
|
||||
return false;
|
||||
}
|
||||
DbgPrint("IDE DMA: Bus Master base = 0x%04X (bus %u, dev %u, fn %u)\n",
|
||||
bmBase, bus, dev, fn);
|
||||
|
||||
// ---- Enable Bus Mastering in PCI Command register (bit 2) ------------
|
||||
uint32_t pciCmd = gHal->Pci.Read32(bus, dev, fn, 0x04);
|
||||
gHal->Pci.Write32(bus, dev, fn, 0x04, pciCmd | (1u << 2));
|
||||
|
||||
// ---- Allocate PRDT page (must be 32-bit physical, 4-byte aligned) ----
|
||||
PHYSICAL_ADDRESS prdtPhys = MmAllocateContiguous(1);
|
||||
if (!prdtPhys) {
|
||||
DbgPrint("IDE DMA: Failed to allocate PRDT page\n");
|
||||
return false;
|
||||
}
|
||||
if (prdtPhys >> 32) {
|
||||
// MmAllocateContiguous returned memory above 4 GB — BM IDE can't
|
||||
// address it. This shouldn't happen on normal x86 systems with early
|
||||
// boot allocations but we check defensively.
|
||||
DbgPrint("IDE DMA: PRDT page is above 4 GB (0x%016llX) — using PIO\n",
|
||||
(uint64_t)prdtPhys);
|
||||
return false;
|
||||
}
|
||||
void *prdtVirt = gHal->PhysToVirt(prdtPhys);
|
||||
DbgPrint("IDE DMA: PRDT at phys 0x%08x, virt 0x%016llX\n", (uint32_t)prdtPhys, prdtVirt);
|
||||
|
||||
// ---- Create synchronisation objects ------------------------------------
|
||||
SEMAPHORE *sem = NULL;
|
||||
if (!KSUCCESS(KeSemaphoreCreate(0, 0x7FFFFFFF, &sem))) {
|
||||
DbgPrint("IDE DMA: Failed to create IRP semaphore\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
EVENT *dmaEv = NULL;
|
||||
if (!KSUCCESS(KeEventCreate(EventAutoReset, 0, &dmaEv))) {
|
||||
DbgPrint("IDE DMA: Failed to create DMA completion event\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---- Commit module state -----------------------------------------------
|
||||
gBmBase = bmBase;
|
||||
gPrdtPhys = (uint32_t)prdtPhys;
|
||||
gPrdtVirt = (PRD *)prdtVirt;
|
||||
gIrpSemaphore = sem;
|
||||
gDmaEvent = dmaEv;
|
||||
KeInitializeDpc(&gDmaDpc, IdeDmaCompletionDpc, (void *)0);
|
||||
|
||||
IrpQueueInit(&gIrpQueue, sem);
|
||||
|
||||
// ---- Register IRQ 14 --------------------------------------------------
|
||||
KeIrqRegister(IDE_IRQ_PRIMARY, IdeIsr);
|
||||
DbgPrint("IDE DMA: IRQ %u registered\n", IDE_IRQ_PRIMARY);
|
||||
|
||||
// ---- Spawn worker thread -----------------------------------------------
|
||||
KSTATUS st = KeCreateKernelThread((uintptr_t)IdeWorker, 0,
|
||||
PriorityHigh, (THREAD **)NULL);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("IDE DMA: Worker thread creation failed (0x%x)\n", st);
|
||||
// Not fatal for init — the PRDT/IRQ are live but async callers will
|
||||
// never get completions. Better to surface the failure clearly.
|
||||
return false;
|
||||
}
|
||||
|
||||
DbgPrint("IDE DMA: Init complete\n");
|
||||
return true;
|
||||
}
|
||||
Reference in new issue
Block a user