add ntfs.sys standalone driver target, change default usb vhd layout formatting to ntfs, implement Fat32CreateDir for directory creation support in the fat32 file system driver, add a dedicated HAL serial output endpoint for COM1 writes in hal_serial.c and subscribe serial to HAL text streams, introduce waitable kernel timers with KeTimerCreate, KeTimerSet, and KeTimerCancel alongside spinlock-protected global timer list initialization and scheduler tick tracking, implement SyscallWriteFileAsync system call for asynchronous non-blocking file writes, refactor NetSocketConnect and NetSocketSendTo to use generic SYS_SOCKET_ADDRESS_K structures instead of being hardcoded to IPv4, update gethostbyname and DNS name resolution to support IPv6 parsing via ResolveNameFamily, update tcpserve.exe with -4 and -6 command-line flags to accept and display IPv6 addresses, append size and version headers to all kernel telemetry and statistics structures including SYS_PMM_STATS, SYS_CPU_STATS, and SYS_SCHEDULER_STATS, update winmm timeGetTime to look up high-resolution hardware frequencies via SysGetTickFrequency, integrate 14 native smoke test applications.
This commit is contained in:
1 parent
2c4a68a75f
commit
ed3d065abd
156 files changed
+10509
-2643
No files matched your search
@@ -23,7 +23,7 @@ static DRIVER_OBJECT gPciDriver;
|
||||
static uint32_t gPciDeviceCount;
|
||||
|
||||
// PCI MMIO BARs use a private virtual aperture instead of the physical
|
||||
// direct map. This avoids cacheability aliasing with large direct-map pages
|
||||
// direct map. This avoids cacheability aliasing with large direct-map pages
|
||||
// and keeps BAR mappings page-granular.
|
||||
|
||||
#define PCI_MMIO_VA_BASE 0xFFFF900000000000ULL
|
||||
@@ -139,7 +139,7 @@ static void PciDecodeBars(PCI_PDO_EXTENSION* ext) {
|
||||
}
|
||||
|
||||
// Size BARs with I/O and memory decoding disabled so all-ones probes do
|
||||
// not accidentally alias a live resource. Restore policy immediately;
|
||||
// not accidentally alias a live resource. Restore policy immediately;
|
||||
// PciEnableDevice() is the only exported path that changes command bits.
|
||||
uint16_t savedCommand = PciRawRead16(ext->Info.Address.Bus,
|
||||
ext->Info.Address.Device,
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; }
|
||||
#endif
|
||||
|
||||
|
||||
#define PCI_CLASS_SERIAL_BUS 0x0Cu
|
||||
#define PCI_SUBCLASS_USB 0x03u
|
||||
#define PCI_PROGIF_XHCI 0x30u
|
||||
@@ -116,7 +115,6 @@ static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; }
|
||||
#define XHCI_TRB_TYPE_ADDRESS_DEVICE 11u
|
||||
#define XHCI_TRB_TYPE_CONFIGURE_ENDPOINT 12u
|
||||
|
||||
|
||||
#define XHCI_CC_SUCCESS 1u
|
||||
#define XHCI_CC_SHORT_PACKET 13u
|
||||
|
||||
@@ -205,7 +203,6 @@ static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; }
|
||||
#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
|
||||
@@ -487,8 +484,6 @@ typedef struct USB_PDO_EXTENSION {
|
||||
USB_ENDPOINT_DESCRIPTOR Endpoints[USB_MAX_TRACKED_ENDPOINTS];
|
||||
} USB_PDO_EXTENSION;
|
||||
|
||||
|
||||
|
||||
typedef enum USB_HUB_KIND {
|
||||
UsbHubKindRoot = 1,
|
||||
UsbHubKindExternal = 2,
|
||||
@@ -591,8 +586,6 @@ typedef struct USB_HID_EXTENSION {
|
||||
uint32_t InputDeviceId;
|
||||
} USB_HID_EXTENSION;
|
||||
|
||||
|
||||
|
||||
typedef enum USB_STORAGE_EXTENSION_KIND {
|
||||
UsbStorageExtensionInterface = 0x55534946u,
|
||||
UsbStorageExtensionDisk = 0x5553444Bu,
|
||||
@@ -838,7 +831,6 @@ typedef struct XHCI_CONTROLLER {
|
||||
USB_HCD Hcd;
|
||||
} XHCI_CONTROLLER;
|
||||
|
||||
|
||||
extern DRIVER_OBJECT gUsbStorageDriver;
|
||||
extern DRIVER_OBJECT gUsbBusDriver;
|
||||
extern DRIVER_OBJECT gUsbCompositeDriver;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// USB bus manager. This file owns device addressing, descriptor reads, and child PDO creation.
|
||||
|
||||
// This is the structural USB bus layer used by the upcoming composite, hub,
|
||||
// and hotplug stages. The legacy direct xHCI class path is still present for
|
||||
// and hotplug stages. The legacy direct xHCI class path is still present for
|
||||
// boot HID/storage during migration, but newly enumerated xHCI devices now also
|
||||
// get first-class USB PDOs and can accept internal URB IRPs.
|
||||
#include "usb.h"
|
||||
@@ -187,7 +187,6 @@ static void UsbFormatClass(char* buf, size_t cap, uint8_t cls) {
|
||||
UsbIdAppendHex(buf, cap, &pos, cls, 2);
|
||||
}
|
||||
|
||||
|
||||
// Implements the format Composite operation.
|
||||
static void UsbFormatComposite(char* buf, size_t cap) {
|
||||
size_t pos = 0;
|
||||
@@ -442,7 +441,6 @@ KSTATUS UsbCreateInterfacePdos(USB_HCD* hcd, XHCI_DEVICE* xdev, DEVICE_OBJECT* p
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the reference First Child operation.
|
||||
static DEVICE_OBJECT* UsbReferenceFirstChild(DEVICE_OBJECT* parent) {
|
||||
if (!parent) return (DEVICE_OBJECT*)0;
|
||||
@@ -487,7 +485,7 @@ KSTATUS UsbRemovePdoSubtree(DEVICE_OBJECT* pdo, bool surprise) {
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
// Child PDOs may be owned either directly by the PDO or by an attached FDO
|
||||
// such as usb-composite or usb-hub. The attached driver's PnP handler is
|
||||
// such as usb-composite or usb-hub. The attached driver's PnP handler is
|
||||
// therefore responsible for removing its own child PDOs before forwarding
|
||||
// REMOVE_DEVICE to the lower PDO.
|
||||
(void)UsbRemoveDeviceObjectChildren(pdo, surprise);
|
||||
|
||||
@@ -46,7 +46,7 @@ KSTATUS UsbCompositeDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
// * an FDO attached above a USB device PDO, and
|
||||
// * interface PDOs that it creates for class drivers.
|
||||
// Forwarded PnP IRPs from HID/storage land on the interface PDO, so the
|
||||
// PDO must use the generic USB PDO PnP handler. Treating its
|
||||
// PDO must use the generic USB PDO PnP handler. Treating its
|
||||
// USB_PDO_EXTENSION as USB_COMPOSITE_EXTENSION corrupts the dispatch path.
|
||||
if (device->Type == DeviceObjectPhysical)
|
||||
return UsbDispatchPdoPnp(device, irp);
|
||||
|
||||
@@ -25,7 +25,6 @@ KSTATUS XhciSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Returns tracked Interface By Number.
|
||||
static USB_TRACKED_INTERFACE* UsbFindTrackedInterfaceByNumber(XHCI_DEVICE* dev, uint8_t interfaceNumber) {
|
||||
if (!dev) return (USB_TRACKED_INTERFACE*)0;
|
||||
@@ -111,11 +110,11 @@ void XhciLogConfigurationTree(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
}
|
||||
} else {
|
||||
|
||||
// Alternate settings are not independent USB interfaces. Real
|
||||
// Alternate settings are not independent USB interfaces. Real
|
||||
// UVC cameras often expose one VideoStreaming interface with
|
||||
// many alternate settings, and publishing each alternate setting
|
||||
// as a separate PDO causes duplicate interface devices and can
|
||||
// exhaust USB_MAX_TRACKED_INTERFACES. Track only alternate
|
||||
// exhaust USB_MAX_TRACKED_INTERFACES. Track only alternate
|
||||
// setting zero until a function driver explicitly selects a
|
||||
// different setting.
|
||||
|
||||
@@ -211,8 +210,6 @@ void XhciLogConfigurationTree(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
const USB_DEVICE_DESCRIPTOR* devDesc) {
|
||||
if (devDesc->bNumConfigurations == 0) {
|
||||
@@ -254,7 +251,6 @@ KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the rollback Enumerated Slot operation.
|
||||
static void XhciRollbackEnumeratedSlot(XHCI_CONTROLLER* xhci, uint8_t slotId) {
|
||||
if (!xhci || slotId == 0 || slotId > XHCI_MAX_TRACKED_SLOTS)
|
||||
@@ -279,7 +275,6 @@ static void XhciRollbackEnumeratedSlot(XHCI_CONTROLLER* xhci, uint8_t slotId) {
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
}
|
||||
|
||||
|
||||
static KSTATUS XhciEnumeratePathEx(XHCI_CONTROLLER* xhci,
|
||||
uint8_t logicalPort,
|
||||
uint8_t speed,
|
||||
@@ -435,7 +430,6 @@ KSTATUS XhciEnumeratePort(XHCI_CONTROLLER* xhci, uint8_t port) {
|
||||
return XhciEnumeratePortEx(xhci, port, (uint8_t*)0);
|
||||
}
|
||||
|
||||
|
||||
// Implements the enumerate Root Ports operation.
|
||||
void XhciEnumerateRootPorts(XHCI_CONTROLLER* xhci) {
|
||||
uint32_t attempted = 0;
|
||||
|
||||
@@ -11,7 +11,6 @@ bool XhciHidKeyInReport(const uint8_t* keys, uint8_t key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
INPUT_KEY XhciHidModifierKey(uint8_t bit) {
|
||||
switch (bit) {
|
||||
case 0: return INPUT_KEY_LEFT_CTRL;
|
||||
@@ -26,7 +25,6 @@ INPUT_KEY XhciHidModifierKey(uint8_t bit) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Submits hid Key.
|
||||
void XhciSubmitHidKey(INPUT_KEY key, bool pressed) {
|
||||
if (key == INPUT_KEY_NONE) return;
|
||||
@@ -36,7 +34,6 @@ void XhciSubmitHidKey(INPUT_KEY key, bool pressed) {
|
||||
KeInputSubmitKey(&ev);
|
||||
}
|
||||
|
||||
|
||||
// Processes boot Keyboard Report.
|
||||
void XhciProcessBootKeyboardReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_t len) {
|
||||
if (len < 8) return;
|
||||
@@ -66,7 +63,6 @@ void XhciProcessBootKeyboardReport(XHCI_DEVICE* dev, const uint8_t* report, uint
|
||||
memcpy(dev->HidLastKeys, keys, sizeof(dev->HidLastKeys));
|
||||
}
|
||||
|
||||
|
||||
// Processes boot Mouse Report.
|
||||
void XhciProcessBootMouseReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_t len) {
|
||||
if (len < 3) return;
|
||||
@@ -88,7 +84,6 @@ void XhciProcessBootMouseReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_
|
||||
dev->HidLastButtons = buttons;
|
||||
}
|
||||
|
||||
|
||||
// Implements the arm Hid Interrupt In operation.
|
||||
KSTATUS XhciArmHidInterruptIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
if (!dev->HidActive || dev->HidArmed || !dev->HidRing || !dev->HidBuffer)
|
||||
@@ -117,7 +112,6 @@ KSTATUS XhciArmHidInterruptIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the arm All Hid operation.
|
||||
void XhciArmAllHid(XHCI_CONTROLLER* xhci) {
|
||||
for (uint8_t slot = 1; slot <= XHCI_MAX_TRACKED_SLOTS; slot++) {
|
||||
@@ -131,7 +125,6 @@ void XhciArmAllHid(XHCI_CONTROLLER* xhci) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void XhciHandleLegacyHidTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbPhys,
|
||||
uint32_t completionCode, uint8_t slotId,
|
||||
uint8_t endpointId, uint32_t residue) {
|
||||
@@ -163,7 +156,6 @@ static void XhciHandleLegacyHidTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trb
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Starts hid Interrupts.
|
||||
KSTATUS XhciStartHidInterrupts(XHCI_CONTROLLER* xhci) {
|
||||
if (!xhci || xhci->HidDeviceCount == 0)
|
||||
@@ -173,7 +165,6 @@ KSTATUS XhciStartHidInterrupts(XHCI_CONTROLLER* xhci) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Updates hid Boot Protocol.
|
||||
KSTATUS XhciSetHidBootProtocol(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
USB_SETUP_PACKET setup;
|
||||
@@ -205,7 +196,6 @@ KSTATUS XhciSetHidBootProtocol(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Creates hid Memory.
|
||||
KSTATUS XhciAllocateHidMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
if (dev->HidRing && dev->HidBuffer) return STATUS_SUCCESS;
|
||||
@@ -232,7 +222,6 @@ KSTATUS XhciAllocateHidMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the configure Hid Endpoint operation.
|
||||
KSTATUS XhciConfigureHidEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
if (!dev->HidCandidate)
|
||||
@@ -308,7 +297,6 @@ KSTATUS XhciConfigureHidEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// PDO/FDO boot HID function driver path.
|
||||
|
||||
DRIVER_OBJECT gUsbHidDriver;
|
||||
@@ -324,7 +312,6 @@ static const IO_DRIVER_ID_TABLE gUsbHidIdTable = {
|
||||
sizeof(gUsbHidIds) / sizeof(gUsbHidIds[0]),
|
||||
};
|
||||
|
||||
|
||||
// Implements the hid Is Qemu Absolute Tablet Pdo operation.
|
||||
static bool UsbHidIsQemuAbsoluteTabletPdo(const USB_PDO_EXTENSION* pdoExt, const USB_ENDPOINT_DESCRIPTOR* ep) {
|
||||
if (!pdoExt || !pdoExt->Device || !ep)
|
||||
@@ -358,7 +345,6 @@ static const USB_ENDPOINT_DESCRIPTOR* UsbHidFindInterruptInPdo(const USB_PDO_EXT
|
||||
return (const USB_ENDPOINT_DESCRIPTOR*)0;
|
||||
}
|
||||
|
||||
|
||||
// Implements the hid Process Boot Keyboard Report operation.
|
||||
static void UsbHidProcessBootKeyboardReport(USB_HID_EXTENSION* ext, const uint8_t* report, uint16_t len) {
|
||||
if (!ext || len < 8) return;
|
||||
@@ -630,7 +616,6 @@ static KSTATUS UsbHidStartDevice(DEVICE_OBJECT* device, USB_HID_EXTENSION* ext)
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the hid Dispatch Pnp operation.
|
||||
KSTATUS UsbHidDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
if (!device || !irp)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// USB hub driver. This file owns hub probing, port status handling, and child-device enumeration.
|
||||
|
||||
// Root hubs and external hubs are represented by the same FDO abstraction. The
|
||||
// Root hubs and external hubs are represented by the same FDO abstraction. The
|
||||
// xHCI root hub feeds port-change events directly into this driver, while an
|
||||
// external hub feeds the same port state machine from its interrupt-IN change
|
||||
// bitmap.
|
||||
@@ -77,9 +77,9 @@ static KSTATUS UsbHubEnumerateRootPortWithRetries(USB_HUB_EXTENSION* hub,
|
||||
|
||||
// On real Intel laptop controllers some internal full-speed devices
|
||||
// respond with xHCI USB Transaction Error during Address Device while
|
||||
// the port is still settling. Retry that boundedly while the port is
|
||||
// the port is still settling. Retry that boundedly while the port is
|
||||
// held in ENUMERATING state so queued PSC events cannot start parallel
|
||||
// enumerations. Descriptor/control timeouts are not retried here; they
|
||||
// enumerations. Descriptor/control timeouts are not retried here; they
|
||||
// are usually dead/firmware-owned ports and retrying them stalls boot.
|
||||
|
||||
if (last != STATUS_DEVICE_ERROR || attempt == maxAttempts)
|
||||
@@ -898,7 +898,7 @@ KSTATUS UsbHubDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
if (hub->WorkerSemaphore)
|
||||
KeSignalSemaphore(hub->WorkerSemaphore);
|
||||
|
||||
// Remove downstream PDOs owned by this hub FDO. The explicit
|
||||
// Remove downstream PDOs owned by this hub FDO. The explicit
|
||||
// slot arrays are still used as the hardware lookup table; the
|
||||
// child PDO tree is the authoritative software lifetime tree.
|
||||
(void)UsbRemoveDeviceObjectChildren(device, true);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// USB mass-storage driver. This file owns bulk-only transport and storage-device exposure.
|
||||
|
||||
// The storage driver now binds as a USB function driver to an interface PDO
|
||||
// (USB\CLASS_08&SUBCLASS_06&PROT_50). It creates a disk FDO above that
|
||||
// interface stack and issues BOT transfers through USB URBs. The xHCI HCD
|
||||
// (USB\CLASS_08&SUBCLASS_06&PROT_50). It creates a disk FDO above that
|
||||
// interface stack and issues BOT transfers through USB URBs. The xHCI HCD
|
||||
// still backs the bulk URBs with the existing per-device rings; later stages
|
||||
// will make endpoint rings fully pipe-owned and asynchronous.
|
||||
#include "usb.h"
|
||||
@@ -597,7 +597,7 @@ static KSTATUS UsbStorageStartDevice(DEVICE_OBJECT* device, USB_STORAGE_EXTENSIO
|
||||
}
|
||||
|
||||
// The disk namespace publishes an arrival notification synchronously from
|
||||
// IoRegisterDiskDevice(). Mark the storage FDO usable before publishing the
|
||||
// IoRegisterDiskDevice(). Mark the storage FDO usable before publishing the
|
||||
// disk so filesystem hotplug callbacks can read sector 0 immediately.
|
||||
xdev->MassActive = true;
|
||||
storage->Started = true;
|
||||
@@ -785,7 +785,6 @@ KSTATUS UsbStorageDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
|
||||
// Implements the storage Add Device operation.
|
||||
KSTATUS UsbStorageAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) {
|
||||
if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical)
|
||||
|
||||
@@ -7,7 +7,6 @@ uint32_t UsbAlignUp32(uint32_t value, uint32_t align) {
|
||||
return (value + align - 1u) & ~(align - 1u);
|
||||
}
|
||||
|
||||
|
||||
// Implements the barrier operation.
|
||||
void XhciBarrier(void) {
|
||||
_ReadWriteBarrier();
|
||||
@@ -15,31 +14,26 @@ void XhciBarrier(void) {
|
||||
_ReadWriteBarrier();
|
||||
}
|
||||
|
||||
|
||||
// Implements the read8 operation.
|
||||
uint8_t XhciRead8(volatile uint8_t* base, uint32_t reg) {
|
||||
return *(volatile uint8_t*)(base + reg);
|
||||
}
|
||||
|
||||
|
||||
// Implements the read16 operation.
|
||||
uint16_t XhciRead16(volatile uint8_t* base, uint32_t reg) {
|
||||
return *(volatile uint16_t*)(base + reg);
|
||||
}
|
||||
|
||||
|
||||
// Implements the read32 operation.
|
||||
uint32_t XhciRead32(volatile uint8_t* base, uint32_t reg) {
|
||||
return *(volatile uint32_t*)(base + reg);
|
||||
}
|
||||
|
||||
|
||||
// Implements the write32 operation.
|
||||
void XhciWrite32(volatile uint8_t* base, uint32_t reg, uint32_t value) {
|
||||
*(volatile uint32_t*)(base + reg) = value;
|
||||
}
|
||||
|
||||
|
||||
// Implements the read64 operation.
|
||||
uint64_t XhciRead64(volatile uint8_t* base, uint32_t reg) {
|
||||
uint32_t lo = XhciRead32(base, reg);
|
||||
@@ -47,14 +41,12 @@ uint64_t XhciRead64(volatile uint8_t* base, uint32_t reg) {
|
||||
return ((uint64_t)hi << 32) | lo;
|
||||
}
|
||||
|
||||
|
||||
// Implements the write64 operation.
|
||||
void XhciWrite64(volatile uint8_t* base, uint32_t reg, uint64_t value) {
|
||||
XhciWrite32(base, reg, (uint32_t)value);
|
||||
XhciWrite32(base, reg + 4u, (uint32_t)(value >> 32));
|
||||
}
|
||||
|
||||
|
||||
// Implements the delay Us operation.
|
||||
void UsbDelayUs(const HAL_API* hal, uint32_t us) {
|
||||
if (hal && hal->Timer.Stall) hal->Timer.Stall(us);
|
||||
@@ -63,7 +55,6 @@ 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) {
|
||||
for (uint32_t i = 0; i < timeoutUs; i++) {
|
||||
@@ -73,7 +64,6 @@ bool UsbWaitSet32(const HAL_API* hal, volatile uint8_t* base, uint32_t reg,
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool UsbWaitClear32(const HAL_API* hal, volatile uint8_t* base, uint32_t reg,
|
||||
uint32_t mask, uint32_t timeoutUs) {
|
||||
for (uint32_t i = 0; i < timeoutUs; i++) {
|
||||
@@ -83,15 +73,14 @@ bool UsbWaitClear32(const HAL_API* hal, volatile uint8_t* base, uint32_t reg,
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci, size_t pages,
|
||||
PHYSICAL_ADDRESS* outPhys) {
|
||||
if (!outPhys || pages == 0)
|
||||
return NULL;
|
||||
|
||||
// Most xHCI runtime objects are one page. Do not route those through the
|
||||
// Most xHCI runtime objects are one page. Do not route those through the
|
||||
// physically-contiguous allocator: on real machines that allocator is a
|
||||
// scarce DMA primitive, while a single frame is already contiguous. QEMU
|
||||
// scarce DMA primitive, while a single frame is already contiguous. QEMU
|
||||
// hid this because it had simple low memory; laptop firmware does not.
|
||||
PHYSICAL_ADDRESS phys = 0;
|
||||
if (pages == 1) {
|
||||
@@ -112,62 +101,52 @@ void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci, size_t pages,
|
||||
return virt;
|
||||
}
|
||||
|
||||
|
||||
// Implements the scratchpad Count operation.
|
||||
uint16_t XhciScratchpadCount(uint32_t hcs2) {
|
||||
return (uint16_t)((((hcs2 >> 16) & 0x3E0u) | ((hcs2 >> 27) & 0x1Fu)) & 0x3FFu);
|
||||
}
|
||||
|
||||
|
||||
// Implements the trb Type operation.
|
||||
uint32_t XhciTrbType(const XHCI_TRB* trb) {
|
||||
return (trb->Control >> XHCI_TRB_TYPE_SHIFT) & XHCI_TRB_TYPE_MASK;
|
||||
}
|
||||
|
||||
|
||||
// Implements the completion Code operation.
|
||||
uint32_t XhciCompletionCode(const XHCI_TRB* trb) {
|
||||
return (trb->Status >> 24) & 0xFFu;
|
||||
}
|
||||
|
||||
|
||||
// Implements the event Slot Id operation.
|
||||
uint8_t XhciEventSlotId(const XHCI_TRB* trb) {
|
||||
return (uint8_t)(trb->Control >> 24);
|
||||
}
|
||||
|
||||
|
||||
// Implements the event Endpoint Id operation.
|
||||
uint8_t XhciEventEndpointId(const XHCI_TRB* trb) {
|
||||
return (uint8_t)((trb->Control >> 16) & 0x1Fu);
|
||||
}
|
||||
|
||||
|
||||
// Implements the transfer Residue operation.
|
||||
uint32_t XhciTransferResidue(const XHCI_TRB* trb) {
|
||||
return trb->Status & 0xFFFFFFu;
|
||||
}
|
||||
|
||||
|
||||
// Implements the input Control Context operation.
|
||||
uint32_t* XhciInputControlContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
(void)xhci;
|
||||
return (uint32_t*)dev->InputContext;
|
||||
}
|
||||
|
||||
|
||||
// Implements the input Slot Context operation.
|
||||
uint32_t* XhciInputSlotContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
return (uint32_t*)(dev->InputContext + xhci->ContextBytes);
|
||||
}
|
||||
|
||||
|
||||
uint32_t* XhciInputEndpointContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
uint8_t endpointId) {
|
||||
return (uint32_t*)(dev->InputContext + (uint32_t)(endpointId + 1u) * xhci->ContextBytes);
|
||||
}
|
||||
|
||||
|
||||
// Implements the ep Max Packet operation.
|
||||
uint32_t XhciEpMaxPacket(uint8_t speed) {
|
||||
switch (speed) {
|
||||
@@ -180,7 +159,6 @@ uint32_t XhciEpMaxPacket(uint8_t speed) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Implements the setup Packet To U64 operation.
|
||||
uint64_t UsbSetupPacketToU64(const USB_SETUP_PACKET* setup) {
|
||||
uint64_t v = 0;
|
||||
@@ -192,7 +170,6 @@ uint64_t UsbSetupPacketToU64(const USB_SETUP_PACKET* setup) {
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
const char* XhciPortSpeedName(uint32_t speed) {
|
||||
switch (speed) {
|
||||
case 1: return "full";
|
||||
@@ -204,8 +181,6 @@ const char* XhciPortSpeedName(uint32_t speed) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Parses supported Protocols.
|
||||
void XhciParseSupportedProtocols(XHCI_CONTROLLER* xhci) {
|
||||
memset(xhci->PortProtocolMajor, 0, sizeof(xhci->PortProtocolMajor));
|
||||
@@ -253,7 +228,6 @@ void XhciParseSupportedProtocols(XHCI_CONTROLLER* xhci) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Implements the port Protocol Major operation.
|
||||
uint8_t XhciPortProtocolMajor(XHCI_CONTROLLER* xhci, uint8_t port, uint32_t portsc) {
|
||||
if (port <= XHCI_MAX_ROOT_PORTS && xhci->PortProtocolMajor[port])
|
||||
@@ -277,12 +251,10 @@ const char* UsbEndpointTypeName(uint8_t type) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char* UsbDirectionName(uint8_t endpointAddress) {
|
||||
return (endpointAddress & USB_ENDPOINT_DIR_IN) ? "in" : "out";
|
||||
}
|
||||
|
||||
|
||||
const char* UsbInterfaceClassName(uint8_t klass, uint8_t subclass, uint8_t proto) {
|
||||
if (klass == USB_CLASS_HID) {
|
||||
if (subclass == 1 && proto == 1) return "hid-boot-keyboard";
|
||||
@@ -297,7 +269,6 @@ const char* UsbInterfaceClassName(uint8_t klass, uint8_t subclass, uint8_t proto
|
||||
return "class";
|
||||
}
|
||||
|
||||
|
||||
// Implements the log Port Status operation.
|
||||
void XhciLogPortStatus(XHCI_CONTROLLER* xhci) {
|
||||
for (uint8_t port = 1; port <= xhci->MaxPorts; port++) {
|
||||
@@ -346,7 +317,6 @@ void XhciPowerRootPorts(XHCI_CONTROLLER* xhci) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Implements the ack Interrupter0 operation.
|
||||
void XhciAckInterrupter0(XHCI_CONTROLLER* xhci) {
|
||||
if (!xhci->Runtime) return;
|
||||
@@ -355,7 +325,6 @@ void XhciAckInterrupter0(XHCI_CONTROLLER* xhci) {
|
||||
XhciWrite32(ir0, 0x00u, (iman & XHCI_IMAN_IE) | XHCI_IMAN_IP);
|
||||
}
|
||||
|
||||
|
||||
// Updates erdp.
|
||||
void XhciUpdateErdp(XHCI_CONTROLLER* xhci) {
|
||||
volatile uint8_t* ir0 = xhci->Runtime + 0x20u;
|
||||
@@ -363,7 +332,6 @@ void XhciUpdateErdp(XHCI_CONTROLLER* xhci) {
|
||||
XhciWrite64(ir0, 0x18u, erdp | (1ull << 3)); // EHB clear
|
||||
}
|
||||
|
||||
|
||||
// Returns command Waiter Locked.
|
||||
static XHCI_WAIT_TARGET* XhciFindCommandWaiterLocked(XHCI_CONTROLLER* xhci, uint64_t commandPhys) {
|
||||
for (XHCI_WAIT_TARGET* w = xhci->WaiterList; w; w = w->Next) {
|
||||
@@ -373,7 +341,6 @@ static XHCI_WAIT_TARGET* XhciFindCommandWaiterLocked(XHCI_CONTROLLER* xhci, uint
|
||||
return (XHCI_WAIT_TARGET*)0;
|
||||
}
|
||||
|
||||
|
||||
static XHCI_WAIT_TARGET* XhciFindTransferWaiterLocked(XHCI_CONTROLLER* xhci,
|
||||
uint64_t transferPhys,
|
||||
uint8_t slotId,
|
||||
@@ -389,7 +356,6 @@ static XHCI_WAIT_TARGET* XhciFindTransferWaiterLocked(XHCI_CONTROLLER* xhci,
|
||||
return (XHCI_WAIT_TARGET*)0;
|
||||
}
|
||||
|
||||
|
||||
static XHCI_WAIT_TARGET* XhciClaimCommandWaiter(XHCI_CONTROLLER* xhci,
|
||||
uint64_t commandPhys,
|
||||
uint32_t completionCode,
|
||||
@@ -406,7 +372,6 @@ static XHCI_WAIT_TARGET* XhciClaimCommandWaiter(XHCI_CONTROLLER* xhci,
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
static XHCI_WAIT_TARGET* XhciClaimTransferWaiter(XHCI_CONTROLLER* xhci,
|
||||
uint64_t transferPhys,
|
||||
uint8_t slotId,
|
||||
@@ -427,7 +392,6 @@ static XHCI_WAIT_TARGET* XhciClaimTransferWaiter(XHCI_CONTROLLER* xhci,
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
// Implements the drain Event Ring operation.
|
||||
bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
(void)target;
|
||||
@@ -484,7 +448,6 @@ bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
return processed;
|
||||
}
|
||||
|
||||
|
||||
// Implements the drain Event Ring Locked operation.
|
||||
bool XhciDrainEventRingLocked(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
if (!xhci) return false;
|
||||
@@ -494,7 +457,6 @@ bool XhciDrainEventRingLocked(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
return processed;
|
||||
}
|
||||
|
||||
|
||||
// Waits for target Complete.
|
||||
bool XhciWaitTargetComplete(const XHCI_WAIT_TARGET* target) {
|
||||
if (!target) return false;
|
||||
@@ -503,7 +465,6 @@ bool XhciWaitTargetComplete(const XHCI_WAIT_TARGET* target) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Registers wait Target.
|
||||
void XhciRegisterWaitTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
if (!xhci || !target || target->Registered)
|
||||
@@ -516,7 +477,6 @@ void XhciRegisterWaitTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
KeReleaseSpinlock(&xhci->WaitLock, flags);
|
||||
}
|
||||
|
||||
|
||||
// Implements the unregister Wait Target operation.
|
||||
void XhciUnregisterWaitTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
if (!xhci || !target || !target->Registered)
|
||||
@@ -536,7 +496,6 @@ void XhciUnregisterWaitTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
KeReleaseSpinlock(&xhci->WaitLock, flags);
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciWaitForArmedTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target,
|
||||
uint32_t timeoutUs) {
|
||||
if (!xhci || !target)
|
||||
@@ -566,7 +525,6 @@ KSTATUS XhciWaitForArmedTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target,
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciWaitForTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target,
|
||||
uint32_t timeoutUs) {
|
||||
if (!xhci || !target)
|
||||
@@ -575,7 +533,6 @@ KSTATUS XhciWaitForTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target,
|
||||
return XhciWaitForArmedTarget(xhci, target, timeoutUs);
|
||||
}
|
||||
|
||||
|
||||
void XhciEventDpcRoutine(KDPC* dpc, void* context,
|
||||
void* systemArgument1, void* systemArgument2) {
|
||||
(void)dpc;
|
||||
@@ -592,7 +549,6 @@ void XhciEventDpcRoutine(KDPC* dpc, void* context,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define XHCI_CONTROLLER_MAGIC 0x58484349u // XHCI
|
||||
#define XHCI_MAX_CONTROLLERS 8u
|
||||
|
||||
@@ -630,7 +586,6 @@ void XhciIsr(void) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Implements the bios Handoff operation.
|
||||
KSTATUS XhciBiosHandoff(XHCI_CONTROLLER* xhci) {
|
||||
uint32_t xecp = (xhci->Hcc1 >> 16) & 0xFFFFu;
|
||||
@@ -664,7 +619,7 @@ KSTATUS XhciBiosHandoff(XHCI_CONTROLLER* xhci) {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear legacy SMI enables/status. RW1C status bits tolerate this.
|
||||
// Clear legacy SMI enables/status. RW1C status bits tolerate this.
|
||||
XhciWrite32(xhci->Mmio, off + 4u, 0xFFFFFFFFu);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -675,7 +630,6 @@ KSTATUS XhciBiosHandoff(XHCI_CONTROLLER* xhci) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the stop And Reset operation.
|
||||
KSTATUS XhciStopAndReset(XHCI_CONTROLLER* xhci) {
|
||||
uint32_t cmd = XhciRead32(xhci->Op, XHCI_OP_USBCMD);
|
||||
@@ -709,7 +663,6 @@ KSTATUS XhciStopAndReset(XHCI_CONTROLLER* xhci) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Creates scratchpads.
|
||||
KSTATUS XhciAllocateScratchpads(XHCI_CONTROLLER* xhci) {
|
||||
if (xhci->ScratchpadCount == 0) return STATUS_SUCCESS;
|
||||
@@ -744,7 +697,6 @@ KSTATUS XhciAllocateScratchpads(XHCI_CONTROLLER* xhci) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Creates runtime Memory.
|
||||
KSTATUS XhciAllocateRuntimeMemory(XHCI_CONTROLLER* xhci) {
|
||||
xhci->Dcbaa = (uint64_t*)UsbDmaAllocPages(xhci, 1, &xhci->DcbaaPhys);
|
||||
@@ -786,7 +738,6 @@ KSTATUS XhciAllocateRuntimeMemory(XHCI_CONTROLLER* xhci) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the program Runtime operation.
|
||||
KSTATUS XhciProgramRuntime(XHCI_CONTROLLER* xhci) {
|
||||
uint8_t slots = xhci->MaxSlots;
|
||||
@@ -808,13 +759,11 @@ KSTATUS XhciProgramRuntime(XHCI_CONTROLLER* xhci) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the command Trb Phys operation.
|
||||
uint64_t XhciCommandTrbPhys(XHCI_CONTROLLER* xhci, uint32_t index) {
|
||||
return xhci->CommandRingPhys + (uint64_t)index * sizeof(XHCI_TRB);
|
||||
}
|
||||
|
||||
|
||||
static KSTATUS XhciCommandRingAppend(XHCI_CONTROLLER* xhci,
|
||||
uint64_t parameter,
|
||||
uint32_t status,
|
||||
@@ -850,7 +799,6 @@ static KSTATUS XhciCommandRingAppend(XHCI_CONTROLLER* xhci,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciSubmitCommand(XHCI_CONTROLLER* xhci,
|
||||
uint64_t parameter,
|
||||
uint32_t status,
|
||||
@@ -892,7 +840,6 @@ KSTATUS XhciSubmitCommand(XHCI_CONTROLLER* xhci,
|
||||
return waitStatus;
|
||||
}
|
||||
|
||||
|
||||
// Submits noop Command.
|
||||
KSTATUS XhciSubmitNoopCommand(XHCI_CONTROLLER* xhci) {
|
||||
XHCI_COMMAND_RESULT result;
|
||||
@@ -913,7 +860,6 @@ KSTATUS XhciSubmitNoopCommand(XHCI_CONTROLLER* xhci) {
|
||||
return st;
|
||||
}
|
||||
|
||||
|
||||
// Enables slot.
|
||||
KSTATUS XhciEnableSlot(XHCI_CONTROLLER* xhci, uint8_t* outSlotId) {
|
||||
XHCI_COMMAND_RESULT result;
|
||||
@@ -938,8 +884,6 @@ KSTATUS XhciEnableSlot(XHCI_CONTROLLER* xhci, uint8_t* outSlotId) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Disables slot.
|
||||
KSTATUS XhciDisableSlot(XHCI_CONTROLLER* xhci, uint8_t slotId) {
|
||||
if (!xhci || slotId == 0)
|
||||
@@ -982,7 +926,6 @@ KSTATUS XhciAddressDevice(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Starts the requested operation.
|
||||
KSTATUS XhciStart(XHCI_CONTROLLER* xhci) {
|
||||
uint32_t cmd = XhciRead32(xhci->Op, XHCI_OP_USBCMD);
|
||||
@@ -1000,7 +943,6 @@ KSTATUS XhciStart(XHCI_CONTROLLER* xhci) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Enables interrupts.
|
||||
void XhciEnableInterrupts(XHCI_CONTROLLER* xhci) {
|
||||
volatile uint8_t* ir0 = xhci->Runtime + 0x20u;
|
||||
@@ -1011,13 +953,11 @@ void XhciEnableInterrupts(XHCI_CONTROLLER* xhci) {
|
||||
XhciWrite32(xhci->Op, XHCI_OP_USBCMD, cmd | XHCI_USBCMD_INTE | XHCI_USBCMD_HSEE);
|
||||
}
|
||||
|
||||
|
||||
// Implements the port Reg operation.
|
||||
uint32_t XhciPortReg(uint8_t port) {
|
||||
return XHCI_OP_PORT_BASE + ((uint32_t)port - 1u) * XHCI_OP_PORT_STRIDE;
|
||||
}
|
||||
|
||||
|
||||
// Clears port Changes.
|
||||
void XhciClearPortChanges(XHCI_CONTROLLER* xhci, uint8_t port) {
|
||||
uint32_t off = XhciPortReg(port);
|
||||
@@ -1025,16 +965,15 @@ void XhciClearPortChanges(XHCI_CONTROLLER* xhci, uint8_t port) {
|
||||
uint32_t changes = portsc & XHCI_PORTSC_CHANGE_MASK;
|
||||
if (changes) {
|
||||
|
||||
// PORTSC is a mixed register. The change bits are RW1C, but many
|
||||
// PORTSC is a mixed register. The change bits are RW1C, but many
|
||||
// other bits are either RO or have side effects when written as one
|
||||
// (PED disables the port; PR starts a reset). Preserve only Port
|
||||
// (PED disables the port; PR starts a reset). Preserve only Port
|
||||
// Power and write one to the RW1C change bits.
|
||||
|
||||
XhciWrite32(xhci->Op, off, (portsc & XHCI_PORTSC_PP) | changes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Implements the reset Port operation.
|
||||
KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
uint32_t off = XhciPortReg(port);
|
||||
@@ -1050,9 +989,9 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
|
||||
if (protocol >= 3u || speed >= 4u) {
|
||||
|
||||
// SuperSpeed ports are enabled by link training. Do not issue a
|
||||
// SuperSpeed ports are enabled by link training. Do not issue a
|
||||
// USB2-style PORTSC.PR reset; on real Intel controllers that can make
|
||||
// the link drop during early enumeration. If firmware/controller
|
||||
// the link drop during early enumeration. If firmware/controller
|
||||
// already has PR set, just wait for it to settle.
|
||||
|
||||
bool enabled = false;
|
||||
@@ -1118,7 +1057,6 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Creates device Memory.
|
||||
KSTATUS XhciAllocateDeviceMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
dev->InputContext = (uint8_t*)UsbDmaAllocPages(xhci, 1, &dev->InputContextPhys);
|
||||
@@ -1143,7 +1081,6 @@ KSTATUS XhciAllocateDeviceMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Builds default Control Context.
|
||||
void XhciBuildDefaultControlContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
memset(dev->InputContext, 0, PAGE_SIZE);
|
||||
@@ -1187,13 +1124,11 @@ void XhciBuildDefaultControlContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
XhciBarrier();
|
||||
}
|
||||
|
||||
|
||||
// Implements the ep0 Trb Phys operation.
|
||||
uint64_t XhciEp0TrbPhys(XHCI_DEVICE* dev, uint32_t index) {
|
||||
return dev->Ep0RingPhys + (uint64_t)index * sizeof(XHCI_TRB);
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciEp0AppendTrb(XHCI_DEVICE* dev,
|
||||
uint64_t parameter,
|
||||
uint32_t status,
|
||||
@@ -1229,7 +1164,6 @@ KSTATUS XhciEp0AppendTrb(XHCI_DEVICE* dev,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciControlInTimeout(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
const USB_SETUP_PACKET* setup,
|
||||
void* buffer,
|
||||
@@ -1298,7 +1232,6 @@ KSTATUS XhciControlInTimeout(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
return STATUS_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciControlIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
const USB_SETUP_PACKET* setup,
|
||||
void* buffer,
|
||||
@@ -1308,7 +1241,6 @@ KSTATUS XhciControlIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
USB_XHCI_CONTROL_TIMEOUT_US);
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciControlNoData(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
const USB_SETUP_PACKET* setup) {
|
||||
uint64_t setupValue = UsbSetupPacketToU64(setup);
|
||||
@@ -1359,7 +1291,6 @@ KSTATUS XhciControlNoData(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
return STATUS_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciGetDeviceDescriptorTimeout(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
USB_DEVICE_DESCRIPTOR* outDesc,
|
||||
uint32_t timeoutUs) {
|
||||
@@ -1389,14 +1320,12 @@ KSTATUS XhciGetDeviceDescriptorTimeout(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciGetDeviceDescriptor(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
USB_DEVICE_DESCRIPTOR* outDesc) {
|
||||
return XhciGetDeviceDescriptorTimeout(xhci, dev, outDesc,
|
||||
USB_XHCI_CONTROL_TIMEOUT_US);
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciGetConfigurationDescriptor(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
uint8_t configurationIndex,
|
||||
USB_CONFIGURATION_DESCRIPTOR* outConfig,
|
||||
@@ -1456,7 +1385,6 @@ KSTATUS XhciGetConfigurationDescriptor(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the endpoint Id From Address operation.
|
||||
uint8_t XhciEndpointIdFromAddress(uint8_t endpointAddress) {
|
||||
uint8_t number = endpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
@@ -1464,14 +1392,13 @@ uint8_t XhciEndpointIdFromAddress(uint8_t endpointAddress) {
|
||||
return (uint8_t)(number * 2u + ((endpointAddress & USB_ENDPOINT_DIR_IN) ? 1u : 0u));
|
||||
}
|
||||
|
||||
|
||||
// Implements the interrupt Interval operation.
|
||||
uint8_t XhciInterruptInterval(uint8_t speed, uint8_t usbInterval) {
|
||||
if (usbInterval == 0) usbInterval = 1;
|
||||
|
||||
if (speed == 3 || speed == 4 || speed == 5) {
|
||||
// High-speed and newer interrupt endpoints already encode bInterval as
|
||||
// an exponent in USB descriptors. xHCI stores the zero-based exponent.
|
||||
// an exponent in USB descriptors. xHCI stores the zero-based exponent.
|
||||
if (usbInterval > 16) usbInterval = 16;
|
||||
return (uint8_t)(usbInterval - 1u);
|
||||
}
|
||||
@@ -1488,7 +1415,6 @@ uint8_t XhciInterruptInterval(uint8_t speed, uint8_t usbInterval) {
|
||||
return interval;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciRingAppend(XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys,
|
||||
uint32_t trbCount, uint32_t* enqueue, uint8_t* cycle,
|
||||
uint64_t parameter, uint32_t status,
|
||||
@@ -1523,7 +1449,6 @@ KSTATUS XhciRingAppend(XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void XhciInitialiseTransferRing(XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys,
|
||||
uint32_t trbCount, uint8_t cycle) {
|
||||
memset(ring, 0, trbCount * sizeof(XHCI_TRB));
|
||||
@@ -1536,7 +1461,6 @@ void XhciInitialiseTransferRing(XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys,
|
||||
cycle;
|
||||
}
|
||||
|
||||
|
||||
KSTATUS XhciBulkTransfer(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
uint8_t endpointId,
|
||||
XHCI_TRB* ring,
|
||||
@@ -1595,9 +1519,6 @@ KSTATUS XhciBulkTransfer(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Implements the configure Pipe Endpoint operation.
|
||||
KSTATUS XhciConfigurePipeEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, USB_PIPE* pipe) {
|
||||
if (!xhci || !dev || !pipe || !pipe->Open)
|
||||
|
||||
@@ -254,10 +254,10 @@ static void VmwSvgaCursorCommit(VMWSVGA_DEVICE* v) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Legacy QEMU SVGA cursors are frontend/host cursors. Re-defining the
|
||||
// Legacy QEMU SVGA cursors are frontend/host cursors. Re-defining the
|
||||
// cursor image from the input path can stall the FIFO/frontend after a
|
||||
// GTK focus/grab transition, so cursor activity must only update the
|
||||
// legacy position/visibility registers. The image is defined once when
|
||||
// legacy position/visibility registers. The image is defined once when
|
||||
// SetCursorImage runs.
|
||||
|
||||
VmwSvgaWriteReg(v, SVGA_REG_CURSOR_ID, 0u);
|
||||
@@ -315,7 +315,7 @@ static uint32_t VmwSvgaPackMonoCursorDword(const uint8_t* bits,
|
||||
int y,
|
||||
uint32_t dwordIndex) {
|
||||
|
||||
// SVGA classic cursor scanlines are padded to 32 bits. QEMU stores
|
||||
// SVGA classic cursor scanlines are padded to 32 bits. QEMU stores
|
||||
// FIFO dwords in guest-endian order and then interprets the result as
|
||||
// bytes via cursor_set_mono(), whose first pixel is bit 0x80 of byte 0.
|
||||
// Therefore byte 0 of the numeric dword must contain pixels 0..7,
|
||||
@@ -351,7 +351,7 @@ static int VmwSvgaClassicCursorWidth(int width) {
|
||||
// For widths that are not a multiple of 32 pixels, those two row
|
||||
// strides differ and every other row is decoded from padding bytes.
|
||||
// Define the classic cursor with a 32-pixel-aligned visible width and
|
||||
// leave the extra pixels transparent. The real cursor image remains
|
||||
// leave the extra pixels transparent. The real cursor image remains
|
||||
// at the left edge and the hotspot is unchanged.
|
||||
|
||||
return (width + 31) & ~31;
|
||||
@@ -364,11 +364,11 @@ static void VmwSvgaBuildMonoCursorBits(const uint32_t* argbPixels,
|
||||
uint8_t* andBits,
|
||||
uint8_t* xorBits) {
|
||||
|
||||
// Classic SVGA cursors are AND/XOR masks. The most compatible path is
|
||||
// Classic SVGA cursors are AND/XOR masks. The most compatible path is
|
||||
// a 1bpp AND mask plus a 1bpp XOR mask:
|
||||
// transparent: AND=1 XOR=0
|
||||
// black: AND=0 XOR=0
|
||||
// white: AND=0 XOR=1
|
||||
// black: AND=0 XOR=0
|
||||
// white: AND=0 XOR=1
|
||||
// This intentionally discards antialiasing, but avoids QEMU/GTK's
|
||||
// fragile 32bpp classic host-cursor path.
|
||||
|
||||
@@ -509,7 +509,6 @@ static KSTATUS VmwSvgaDefineCursorImage(VMWSVGA_DEVICE* v, int width, int height
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the align Up32 operation.
|
||||
static uint32_t VmwSvgaAlignUp32(uint32_t value, uint32_t alignment) {
|
||||
return (value + alignment - 1u) & ~(alignment - 1u);
|
||||
@@ -607,9 +606,9 @@ static KSTATUS VmwSvgaInitializeScreenObject(VMWSVGA_DEVICE* v) {
|
||||
((v->Capabilities & SVGA_CAP_SCREEN_OBJECT_2) != 0);
|
||||
|
||||
// Real VMware can advertise SCREEN_OBJECT_2 without also setting the
|
||||
// original FIFO_CAP_SCREEN_OBJECT bit. The command set is still the
|
||||
// original FIFO_CAP_SCREEN_OBJECT bit. The command set is still the
|
||||
// Screen Object command set: DEFINE_SCREEN, DEFINE_GMRFB, and
|
||||
// BLIT_GMRFB_TO_SCREEN. Treat either capability generation as usable.
|
||||
// BLIT_GMRFB_TO_SCREEN. Treat either capability generation as usable.
|
||||
|
||||
if (!hasScreenObject1 && !hasScreenObject2) {
|
||||
DbgPrint("VMWSVGA: screen object unavailable: screenObject1=%u screenObject2=%u caps=0x%x fifoCaps=0x%x\n",
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
#define SVGA_SCREEN_OBJECT_ID_PRIMARY 0u
|
||||
#define SVGA_GMRFB_FORMAT_32BPP_24DEPTH ((32u) | (24u << 8))
|
||||
|
||||
// The production path is legacy GFB + UPDATE. Screen objects are kept behind
|
||||
// The production path is legacy GFB + UPDATE. Screen objects are kept behind
|
||||
// an opt-in switch until the SO/GMRFB path is independently validated on real
|
||||
// VMware hosts.
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ ahci.sys
|
||||
ata.sys
|
||||
usb.sys
|
||||
fat32.sys
|
||||
ntfs.sys
|
||||
|
||||
[runtime]
|
||||
lwip.sys
|
||||
|
||||
+529
-174
@@ -97,6 +97,7 @@ typedef struct {
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define ATTR_VOLUME_ID 0x08
|
||||
#define ATTR_DIRECTORY 0x10
|
||||
#define ATTR_ARCHIVE 0x20
|
||||
#define ATTR_LFN 0x0F
|
||||
@@ -109,10 +110,10 @@ typedef struct {
|
||||
// FAT32_VOL forward declaration (needed by cache helpers).
|
||||
typedef struct FAT32_VOL_S FAT32_VOL;
|
||||
|
||||
// FAT sector cache (fixes #1 and #5)
|
||||
// FAT sector cache (fixes #1 and #5)
|
||||
// 32 direct-mapped slots, indexed by (lba % FAT_CACHE_SLOTS).
|
||||
// Write-back: WriteFatEntry modifies the in-cache bytes and marks the slot
|
||||
// dirty. FlushFatCache() propagates dirty slots to every FAT copy at once,
|
||||
// dirty. FlushFatCache() propagates dirty slots to every FAT copy at once,
|
||||
// replacing the original per-entry 2x(read+write) pattern with a single
|
||||
// write per dirty sector per FAT copy.
|
||||
|
||||
@@ -155,12 +156,12 @@ typedef struct FAT32_CHAIN_CACHE_ENTRY {
|
||||
uint32_t NextCluster;
|
||||
} FAT32_CHAIN_CACHE_ENTRY;
|
||||
|
||||
// The FAT sector cache is global and direct-mapped. mmap-backed video can fault
|
||||
// The FAT sector cache is global and direct-mapped. mmap-backed video can fault
|
||||
// file pages from multiple Bink worker threads at the same time; without
|
||||
// serialization one thread can evict/reload a cache slot while another thread is
|
||||
// still reading a FAT entry from that slot, corrupting cluster-chain traversal.
|
||||
// Use a sleepable mutex rather than a spinlock because cache misses perform disk
|
||||
// I/O. The static mutex avoids allocating an object from the paging path.
|
||||
// I/O. The static mutex avoids allocating an object from the paging path.
|
||||
static MUTEX gFatCacheMutex;
|
||||
static SPINLOCK gFatCacheMutexInitLock = SPINLOCK_INIT;
|
||||
static int gFatCacheMutexReady;
|
||||
@@ -224,7 +225,7 @@ static KSTATUS FatCacheRead(FAT32_VOL *v, uint64_t lba, uint8_t **out) {
|
||||
e->lba = lba;
|
||||
e->valid = true;
|
||||
e->dirty = false;
|
||||
*out = e->data;
|
||||
*out = e->data;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -327,7 +328,7 @@ static void FlushFatCache(FAT32_VOL *v) {
|
||||
FatCacheRelease(held);
|
||||
}
|
||||
|
||||
// FAT read / write (through cache).
|
||||
// FAT read / write (through cache).
|
||||
|
||||
static uint32_t FatEntry(FAT32_VOL *v, uint32_t cluster) {
|
||||
uint32_t fatOffset = cluster * 4;
|
||||
@@ -368,7 +369,7 @@ static KSTATUS WriteFatEntry(FAT32_VOL *v, uint32_t cluster, uint32_t value) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Cluster allocation (fix #3: next-free hint + two-phase wrap-around scan).
|
||||
// Cluster allocation (fix #3: next-free hint + two-phase wrap-around scan).
|
||||
static uint32_t AllocClusterEx(FAT32_VOL *v, int zeroCluster) {
|
||||
if (v->TotalClusters == 0) return 0;
|
||||
|
||||
@@ -385,7 +386,7 @@ static uint32_t AllocClusterEx(FAT32_VOL *v, int zeroCluster) {
|
||||
v->NextFreeHint = c + 1;
|
||||
|
||||
// Normal file growth zeroes newly allocated clusters so old
|
||||
// disk contents are never exposed. Swap/pagefile creation is
|
||||
// disk contents are never exposed. Swap/pagefile creation is
|
||||
// the one exception: every allocated slot is overwritten before
|
||||
// it can ever be read back, so skipping these writes avoids a
|
||||
// slow boot-time full-pagefile zero pass.
|
||||
@@ -404,7 +405,6 @@ static uint32_t AllocClusterEx(FAT32_VOL *v, int zeroCluster) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static uint32_t AllocCluster(FAT32_VOL *v) {
|
||||
return AllocClusterEx(v, 1);
|
||||
}
|
||||
@@ -703,9 +703,9 @@ static int FatIsValidShortChar(char c) {
|
||||
}
|
||||
}
|
||||
|
||||
// True when the user spelling can refer to a normal FAT 8.3 entry. This is
|
||||
// True when the user spelling can refer to a normal FAT 8.3 entry. This is
|
||||
// intentionally structural and case-insensitive: "doom1.wad" must still open
|
||||
// an existing short entry named DOOM1.WAD. Leading-dot names are not 8.3.
|
||||
// an existing short entry named DOOM1.WAD. Leading-dot names are not 8.3.
|
||||
static int FatNameIsStructurallyShort83V(SYS_STRING_VIEW name) {
|
||||
if (!SysSvIsValid(name) || name.Length == 0) return 0;
|
||||
if (name.Data[0] == '.') return 0;
|
||||
@@ -757,7 +757,7 @@ static void FatSplitShortNameV(SYS_STRING_VIEW name, size_t* baseEnd,
|
||||
}
|
||||
|
||||
// Creation uses LFN only when the spelling cannot be represented by a short
|
||||
// entry, or when mixed-case 8.3 spelling would be lost. Plain lowercase
|
||||
// entry, or when mixed-case 8.3 spelling would be lost. Plain lowercase
|
||||
// 8.3 names are represented with FAT's NT case bits, not VFAT LFN entries.
|
||||
static int FatNameNeedsLfnForCreateV(SYS_STRING_VIEW name) {
|
||||
if (!FatNameIsStructurallyShort83V(name)) return 1;
|
||||
@@ -991,7 +991,7 @@ static KSTATUS DeleteLegacyBrokenDotAliasV(FAT32_VOL *v, uint32_t dirCluster,
|
||||
for (int j = 0; j < 8; j++) if (dir[i].Name[j] != ' ') emptyBase = 0;
|
||||
if (!emptyBase) continue;
|
||||
|
||||
// Do not free the cluster chain here. The broken entry may have
|
||||
// Do not free the cluster chain here. The broken entry may have
|
||||
// aliased existing data; deleting the directory entry is enough to
|
||||
// hide it from hosts and avoids freeing clusters still owned by a
|
||||
// valid file.
|
||||
@@ -1024,7 +1024,6 @@ static void LfnAppend(char *lfn, const LFN_ENTRY *e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Directory search and view-native path traversal.
|
||||
static int Fat32IsPathSep(char c) {
|
||||
return c == '\\' || c == '/';
|
||||
@@ -1109,7 +1108,7 @@ static void Fat32PathCacheAddDirectoryStats(FAT32_VOL* v, uint64_t clusters, uin
|
||||
if (!v) return;
|
||||
uintptr_t flags = KeAcquireSpinlock(&v->PathCacheLock);
|
||||
v->Stats.DirectorySearches++;
|
||||
v->Stats.DirectoryClustersRead += clusters;
|
||||
v->Stats.DirectoryBlocksRead += clusters;
|
||||
v->Stats.DirectoryEntriesScanned += entries;
|
||||
KeReleaseSpinlock(&v->PathCacheLock, flags);
|
||||
}
|
||||
@@ -1199,15 +1198,6 @@ void Fat32QueryStats(IO_FILESYSTEM* fs, IO_FILESYSTEM_STATS* out) {
|
||||
KeReleaseSpinlock(&v->PathCacheLock, flags);
|
||||
}
|
||||
|
||||
|
||||
// Adds FAT-chain traversal counters shared by the read path helpers.
|
||||
static void Fat32StatsAddChainWalk(FAT32_VOL* v, uint64_t steps) {
|
||||
if (!v || !steps) return;
|
||||
uintptr_t flags = KeAcquireSpinlock(&v->PathCacheLock);
|
||||
v->Stats.FatChainWalkSteps += steps;
|
||||
KeReleaseSpinlock(&v->PathCacheLock, flags);
|
||||
}
|
||||
|
||||
// Records one data read span and its physically contiguous cluster merges.
|
||||
static void Fat32StatsAddReadRun(FAT32_VOL* v, uint32_t sectors, uint32_t merges) {
|
||||
if (!v || !sectors) return;
|
||||
@@ -1235,12 +1225,6 @@ static void Fat32FileInvalidateChainCache(FAT32_FILE* f) {
|
||||
memset(f->ChainCache, 0, sizeof(f->ChainCache));
|
||||
f->ChainCacheClock = 0;
|
||||
KeReleaseSpinlock(&f->ChainCacheLock, flags);
|
||||
|
||||
FAT32_VOL* v = f->Vol;
|
||||
if (!v) return;
|
||||
flags = KeAcquireSpinlock(&v->PathCacheLock);
|
||||
v->Stats.FatChainCacheInvalidations++;
|
||||
KeReleaseSpinlock(&v->PathCacheLock, flags);
|
||||
}
|
||||
|
||||
// Locates the best cached starting point for a logical cluster index.
|
||||
@@ -1281,15 +1265,6 @@ static bool Fat32ChainCacheLookupStart(FAT32_FILE* f, uint32_t logicalIndex,
|
||||
*outCluster = bestCluster;
|
||||
}
|
||||
KeReleaseSpinlock(&f->ChainCacheLock, flags);
|
||||
|
||||
FAT32_VOL* v = f->Vol;
|
||||
if (v) {
|
||||
flags = KeAcquireSpinlock(&v->PathCacheLock);
|
||||
v->Stats.FatChainCacheLookups++;
|
||||
if (hit) v->Stats.FatChainCacheHits++;
|
||||
else v->Stats.FatChainCacheMisses++;
|
||||
KeReleaseSpinlock(&v->PathCacheLock, flags);
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
@@ -1309,12 +1284,6 @@ static void Fat32ChainCacheStore(FAT32_FILE* f, uint32_t logicalStart,
|
||||
e->Length = length;
|
||||
e->NextCluster = nextCluster;
|
||||
KeReleaseSpinlock(&f->ChainCacheLock, flags);
|
||||
|
||||
FAT32_VOL* v = f->Vol;
|
||||
if (!v) return;
|
||||
flags = KeAcquireSpinlock(&v->PathCacheLock);
|
||||
v->Stats.FatChainCacheStores++;
|
||||
KeReleaseSpinlock(&v->PathCacheLock, flags);
|
||||
}
|
||||
|
||||
// Resolves a logical file cluster through the cached FAT-chain extent map.
|
||||
@@ -1331,13 +1300,10 @@ static uint32_t Fat32ResolveFileCluster(FAT32_FILE* f, uint32_t logicalIndex) {
|
||||
cluster = f->StartCluster;
|
||||
}
|
||||
|
||||
uint64_t steps = 0;
|
||||
while (currentLogical < logicalIndex && cluster >= 2 && cluster < FAT32_EOC) {
|
||||
cluster = FatEntry(f->Vol, cluster);
|
||||
currentLogical++;
|
||||
steps++;
|
||||
}
|
||||
Fat32StatsAddChainWalk(f->Vol, steps);
|
||||
return cluster;
|
||||
}
|
||||
|
||||
@@ -1355,7 +1321,6 @@ static bool Fat32BuildReadRun(FAT32_FILE* f, uint32_t logicalIndex,
|
||||
uint32_t runClusters = 1;
|
||||
uint32_t last = startCluster;
|
||||
uint32_t next = FatEntry(v, last);
|
||||
uint64_t steps = 1;
|
||||
|
||||
while (next == last + 1 && next < FAT32_EOC &&
|
||||
runSectors + v->SectorsPerCluster <= maxSectors) {
|
||||
@@ -1363,10 +1328,8 @@ static bool Fat32BuildReadRun(FAT32_FILE* f, uint32_t logicalIndex,
|
||||
runClusters++;
|
||||
runSectors += v->SectorsPerCluster;
|
||||
next = FatEntry(v, last);
|
||||
steps++;
|
||||
}
|
||||
|
||||
Fat32StatsAddChainWalk(v, steps);
|
||||
Fat32ChainCacheStore(f, logicalIndex, startCluster, runClusters, next);
|
||||
*outRunSectors = runSectors;
|
||||
*outNextCluster = next;
|
||||
@@ -1414,8 +1377,8 @@ static int SearchDirV(FAT32_VOL *v, uint32_t dirCluster, SYS_STRING_VIEW name,
|
||||
|
||||
if (match) {
|
||||
*outCluster = ((uint32_t)dir[i].FirstClusterHi << 16) | dir[i].FirstClusterLo;
|
||||
*outSize = dir[i].FileSize;
|
||||
*outAttr = dir[i].Attr;
|
||||
*outSize = dir[i].FileSize;
|
||||
*outAttr = dir[i].Attr;
|
||||
if (outDirentLba || outDirentOffset) {
|
||||
uint64_t eb = (uint64_t)i * sizeof(DIRENT);
|
||||
if (outDirentLba) *outDirentLba = clusterLba + eb / v->BytesPerSector;
|
||||
@@ -1453,6 +1416,7 @@ static int Fat32PathNextComponent(SYS_STRING_VIEW path, size_t* cursor,
|
||||
|
||||
static KSTATUS Fat32LookupPathV(FAT32_VOL *v, SYS_STRING_VIEW path,
|
||||
uint32_t *outCluster, uint32_t *outSize,
|
||||
uint8_t *outAttr,
|
||||
uint64_t *outDirentLba, uint32_t *outDirentOffset) {
|
||||
if (!v || !outCluster || !outSize || !SysSvIsValid(path))
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
@@ -1461,12 +1425,16 @@ static KSTATUS Fat32LookupPathV(FAT32_VOL *v, SYS_STRING_VIEW path,
|
||||
|
||||
uint32_t cluster = v->RootCluster, size = 0;
|
||||
uint8_t attr = ATTR_DIRECTORY;
|
||||
uint64_t finalDirentLba = 0;
|
||||
uint32_t finalDirentOffset = 0;
|
||||
if (outDirentLba) *outDirentLba = 0;
|
||||
if (outDirentOffset) *outDirentOffset = 0;
|
||||
|
||||
if (Fat32PathCacheLookup(v, path, outCluster, outSize, &attr,
|
||||
outDirentLba, outDirentOffset))
|
||||
outDirentLba, outDirentOffset)) {
|
||||
if (outAttr) *outAttr = attr;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
size_t cursor = 0;
|
||||
SYS_STRING_VIEW component;
|
||||
@@ -1482,21 +1450,23 @@ static KSTATUS Fat32LookupPathV(FAT32_VOL *v, SYS_STRING_VIEW path,
|
||||
return STATUS_NOT_FOUND;
|
||||
|
||||
cluster = childCluster; size = childSize; attr = childAttr;
|
||||
finalDirentLba = childLba;
|
||||
finalDirentOffset = childOffset;
|
||||
if (outDirentLba) *outDirentLba = childLba;
|
||||
if (outDirentOffset) *outDirentOffset = childOffset;
|
||||
}
|
||||
|
||||
*outCluster = cluster;
|
||||
*outSize = size;
|
||||
if (outAttr) *outAttr = attr;
|
||||
Fat32PathCacheStore(v, path, cluster, size, attr,
|
||||
outDirentLba ? *outDirentLba : 0,
|
||||
outDirentOffset ? *outDirentOffset : 0);
|
||||
finalDirentLba, finalDirentOffset);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS TraverseV(FAT32_VOL *v, SYS_STRING_VIEW path,
|
||||
uint32_t *outCluster, uint32_t *outSize) {
|
||||
return Fat32LookupPathV(v, path, outCluster, outSize, NULL, NULL);
|
||||
return Fat32LookupPathV(v, path, outCluster, outSize, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static KSTATUS SplitPathV(SYS_STRING_VIEW path, SYS_STRING_VIEW* parent,
|
||||
@@ -1546,7 +1516,7 @@ static int FindFreeDirent(FAT32_VOL *v, uint32_t dirCluster,
|
||||
uint8_t first = (uint8_t)dir[i].Name[0];
|
||||
if (first == 0xE5 || first == 0x00) {
|
||||
uint64_t eb = (uint64_t)i * sizeof(DIRENT);
|
||||
*outLba = clusterLba + eb / v->BytesPerSector;
|
||||
*outLba = clusterLba + eb / v->BytesPerSector;
|
||||
*outOffset = (uint32_t)(eb % v->BytesPerSector);
|
||||
MmFreePool(clusterBuf); return 1;
|
||||
}
|
||||
@@ -1564,7 +1534,6 @@ static int FindFreeDirent(FAT32_VOL *v, uint32_t dirCluster,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Find a contiguous run of free directory entries within one directory cluster.
|
||||
// This is required for VFAT LFN creation because N long-name entries must
|
||||
// immediately precede the final short entry.
|
||||
@@ -1619,6 +1588,284 @@ static int FindFreeDirentRun(FAT32_VOL *v, uint32_t dirCluster, uint32_t needed,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Finds the parent directory and final component for a create or delete path.
|
||||
static KSTATUS Fat32ResolveParentForMutation(FAT32_VOL *v, SYS_STRING_VIEW path,
|
||||
uint32_t *parentClusterOut,
|
||||
SYS_STRING_VIEW *filenameOut) {
|
||||
if (!v || !parentClusterOut || !filenameOut)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
SYS_STRING_VIEW parentPath;
|
||||
SYS_STRING_VIEW filenameView;
|
||||
KSTATUS s = SplitPathV(path, &parentPath, &filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
uint32_t parentCluster = 0;
|
||||
uint32_t parentSize = 0;
|
||||
uint8_t parentAttr = 0;
|
||||
s = Fat32LookupPathV(v, parentPath, &parentCluster, &parentSize,
|
||||
&parentAttr, NULL, NULL);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!(parentAttr & ATTR_DIRECTORY)) return STATUS_NOT_FOUND;
|
||||
|
||||
*parentClusterOut = parentCluster;
|
||||
*filenameOut = filenameView;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Creates a FAT directory entry run for a new file or directory.
|
||||
static KSTATUS Fat32CreateDirent(FAT32_VOL *v, SYS_STRING_VIEW path,
|
||||
uint8_t attr, uint32_t firstCluster,
|
||||
uint32_t fileSize,
|
||||
uint64_t *direntLbaOut,
|
||||
uint32_t *direntOffsetOut) {
|
||||
if (!v || !SysSvIsValid(path) || path.Length == 0)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
uint32_t parentCluster = 0;
|
||||
SYS_STRING_VIEW filenameView;
|
||||
KSTATUS s = Fat32ResolveParentForMutation(v, path, &parentCluster, &filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
{
|
||||
uint32_t ec, es;
|
||||
uint8_t ea;
|
||||
uint64_t existingLba = 0;
|
||||
uint32_t existingOffset = 0;
|
||||
if (SearchDirV(v, parentCluster, filenameView, &ec, &es, &ea,
|
||||
&existingLba, &existingOffset)) {
|
||||
return STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
}
|
||||
|
||||
int useLfn = FatNameNeedsLfnForCreateV(filenameView);
|
||||
char shortName[8], shortExt[3];
|
||||
if (useLfn) {
|
||||
s = DeleteLegacyBrokenDotAliasV(v, parentCluster, filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!MakeUniqueShortAliasV(v, parentCluster, filenameView, shortName, shortExt))
|
||||
return STATUS_OBJECT_NAME_COLLISION;
|
||||
} else {
|
||||
Make83NameV(filenameView, shortName, shortExt);
|
||||
}
|
||||
|
||||
size_t nameLen = filenameView.Length;
|
||||
if (nameLen == 0 || nameLen >= MAX_LFN) return STATUS_INVALID_PARAMETER;
|
||||
uint32_t lfnCount = useLfn ? (uint32_t)((nameLen + 12u) / 13u) : 0;
|
||||
uint32_t neededEntries = lfnCount + 1u;
|
||||
|
||||
uint32_t entryCluster, firstIndex;
|
||||
if (!FindFreeDirentRun(v, parentCluster, neededEntries, &entryCluster, &firstIndex))
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
|
||||
if (!clusterBuf) return STATUS_NO_MEMORY;
|
||||
|
||||
uint64_t clusterLba = ClusterToLba(v, entryCluster);
|
||||
s = ReadSectors(v, clusterLba, v->SectorsPerCluster, clusterBuf);
|
||||
if (!KSUCCESS(s)) { MmFreePool(clusterBuf); return s; }
|
||||
|
||||
DIRENT *dir = (DIRENT *)clusterBuf;
|
||||
uint32_t shortIndex = firstIndex + lfnCount;
|
||||
|
||||
if (useLfn) {
|
||||
uint8_t checksum = LfnChecksum(shortName, shortExt);
|
||||
for (uint32_t slot = 0; slot < lfnCount; slot++) {
|
||||
uint32_t ordinal = lfnCount - slot;
|
||||
BuildLfnEntry((LFN_ENTRY *)&dir[firstIndex + slot], filenameView.Data,
|
||||
nameLen, ordinal, lfnCount, checksum);
|
||||
}
|
||||
}
|
||||
|
||||
DIRENT *d = &dir[shortIndex];
|
||||
memset(d, 0, sizeof(DIRENT));
|
||||
memcpy(d->Name, shortName, 8);
|
||||
memcpy(d->Ext, shortExt, 3);
|
||||
d->Attr = attr;
|
||||
d->Reserved = FatShortNameCaseFlagsV(filenameView);
|
||||
d->FirstClusterHi = (uint16_t)(firstCluster >> 16);
|
||||
d->FirstClusterLo = (uint16_t)(firstCluster & 0xFFFFu);
|
||||
d->FileSize = fileSize;
|
||||
{
|
||||
FAT32_ENCODED_TIME now;
|
||||
FatQueryEncodedTime(&now);
|
||||
d->CrtTimeTenth = now.CreateTimeTenth;
|
||||
d->CrtTime = now.Time;
|
||||
d->CrtDate = now.Date;
|
||||
d->LastAccDate = now.Date;
|
||||
d->WrtTime = now.Time;
|
||||
d->WrtDate = now.Date;
|
||||
}
|
||||
|
||||
s = WriteSectors(v, clusterLba, v->SectorsPerCluster, clusterBuf);
|
||||
MmFreePool(clusterBuf);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
uint64_t shortEntryByte = (uint64_t)shortIndex * sizeof(DIRENT);
|
||||
uint64_t direntLba = clusterLba + shortEntryByte / v->BytesPerSector;
|
||||
uint32_t direntOffset = (uint32_t)(shortEntryByte % v->BytesPerSector);
|
||||
if (direntLbaOut) *direntLbaOut = direntLba;
|
||||
if (direntOffsetOut) *direntOffsetOut = direntOffset;
|
||||
|
||||
Fat32PathCacheInvalidate(v);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Initializes one dot entry in a new directory cluster.
|
||||
static void Fat32InitDotEntry(DIRENT *d, const char *name,
|
||||
uint32_t firstCluster,
|
||||
const FAT32_ENCODED_TIME *now) {
|
||||
memset(d, 0, sizeof(*d));
|
||||
memset(d->Name, ' ', sizeof(d->Name));
|
||||
memset(d->Ext, ' ', sizeof(d->Ext));
|
||||
d->Name[0] = name[0];
|
||||
if (name[1]) d->Name[1] = name[1];
|
||||
d->Attr = ATTR_DIRECTORY;
|
||||
d->FirstClusterHi = (uint16_t)(firstCluster >> 16);
|
||||
d->FirstClusterLo = (uint16_t)(firstCluster & 0xFFFFu);
|
||||
d->CrtTimeTenth = now->CreateTimeTenth;
|
||||
d->CrtTime = now->Time;
|
||||
d->CrtDate = now->Date;
|
||||
d->LastAccDate = now->Date;
|
||||
d->WrtTime = now->Time;
|
||||
d->WrtDate = now->Date;
|
||||
}
|
||||
|
||||
// Writes the initial contents of a newly allocated FAT directory cluster.
|
||||
static KSTATUS Fat32InitializeDirectoryCluster(FAT32_VOL *v, uint32_t dirCluster,
|
||||
uint32_t parentCluster) {
|
||||
if (!v || dirCluster < 2)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
|
||||
if (!clusterBuf) return STATUS_NO_MEMORY;
|
||||
memset(clusterBuf, 0, v->BytesPerCluster);
|
||||
|
||||
FAT32_ENCODED_TIME now;
|
||||
FatQueryEncodedTime(&now);
|
||||
DIRENT *dir = (DIRENT *)clusterBuf;
|
||||
Fat32InitDotEntry(&dir[0], ".", dirCluster, &now);
|
||||
Fat32InitDotEntry(&dir[1], "..", parentCluster, &now);
|
||||
|
||||
KSTATUS s = WriteSectors(v, ClusterToLba(v, dirCluster),
|
||||
v->SectorsPerCluster, clusterBuf);
|
||||
MmFreePool(clusterBuf);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Tests whether a FAT directory entry is one of the synthetic dot entries.
|
||||
static int Fat32IsDotEntry(const DIRENT *d) {
|
||||
if (!d || !(d->Attr & ATTR_DIRECTORY)) return 0;
|
||||
if (d->Name[0] != '.') return 0;
|
||||
if (d->Name[1] == ' ')
|
||||
return 1;
|
||||
return d->Name[1] == '.' && d->Name[2] == ' ';
|
||||
}
|
||||
|
||||
// Tests whether a directory contains any real child entries.
|
||||
static KSTATUS Fat32DirectoryIsEmpty(FAT32_VOL *v, uint32_t dirCluster,
|
||||
int *emptyOut) {
|
||||
if (!v || !emptyOut || dirCluster < 2)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
|
||||
if (!clusterBuf) return STATUS_NO_MEMORY;
|
||||
|
||||
uint32_t cluster = dirCluster;
|
||||
while (cluster >= 2 && cluster < FAT32_EOC) {
|
||||
KSTATUS s = ReadSectors(v, ClusterToLba(v, cluster),
|
||||
v->SectorsPerCluster, clusterBuf);
|
||||
if (!KSUCCESS(s)) { MmFreePool(clusterBuf); return s; }
|
||||
|
||||
uint32_t entries = v->BytesPerCluster / sizeof(DIRENT);
|
||||
DIRENT *dir = (DIRENT *)clusterBuf;
|
||||
for (uint32_t i = 0; i < entries; i++) {
|
||||
uint8_t first = (uint8_t)dir[i].Name[0];
|
||||
if (first == 0x00) { MmFreePool(clusterBuf); *emptyOut = 1; return STATUS_SUCCESS; }
|
||||
if (first == 0xE5 || dir[i].Attr == ATTR_LFN) continue;
|
||||
if ((dir[i].Attr & ATTR_VOLUME_ID) && !(dir[i].Attr & ATTR_DIRECTORY)) continue;
|
||||
if (Fat32IsDotEntry(&dir[i])) continue;
|
||||
|
||||
MmFreePool(clusterBuf);
|
||||
*emptyOut = 0;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
cluster = FatEntry(v, cluster);
|
||||
}
|
||||
|
||||
MmFreePool(clusterBuf);
|
||||
*emptyOut = 1;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Deletes the directory-entry run for one child name from a parent directory.
|
||||
static KSTATUS Fat32DeleteDirentRunV(FAT32_VOL *v, uint32_t parentCluster,
|
||||
SYS_STRING_VIEW name) {
|
||||
if (!v || !Fat32NameViewIsValid(name))
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
|
||||
if (!clusterBuf) return STATUS_NO_MEMORY;
|
||||
|
||||
char lfn[MAX_LFN];
|
||||
int hasLfn = 0;
|
||||
uint32_t lfnStart = 0;
|
||||
memset(lfn, 0, sizeof(lfn));
|
||||
|
||||
char wantName83[8], wantExt83[3];
|
||||
int allowShortMatch = FatNameIsStructurallyShort83V(name);
|
||||
Make83NameV(name, wantName83, wantExt83);
|
||||
|
||||
uint32_t cluster = parentCluster;
|
||||
while (cluster >= 2 && cluster < FAT32_EOC) {
|
||||
uint64_t clusterLba = ClusterToLba(v, cluster);
|
||||
KSTATUS s = ReadSectors(v, clusterLba, v->SectorsPerCluster, clusterBuf);
|
||||
if (!KSUCCESS(s)) { MmFreePool(clusterBuf); return s; }
|
||||
|
||||
uint32_t entries = v->BytesPerCluster / sizeof(DIRENT);
|
||||
DIRENT *dir = (DIRENT *)clusterBuf;
|
||||
hasLfn = 0;
|
||||
memset(lfn, 0, sizeof(lfn));
|
||||
for (uint32_t i = 0; i < entries; i++) {
|
||||
uint8_t first = (uint8_t)dir[i].Name[0];
|
||||
if (first == 0x00) { MmFreePool(clusterBuf); return STATUS_NOT_FOUND; }
|
||||
if (first == 0xE5) { hasLfn = 0; memset(lfn, 0, sizeof(lfn)); continue; }
|
||||
if (dir[i].Attr == ATTR_LFN) {
|
||||
if (!hasLfn) lfnStart = i;
|
||||
LfnAppend(lfn, (LFN_ENTRY *)&dir[i]);
|
||||
hasLfn = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
int match = 0;
|
||||
if (hasLfn && lfn[0]) match = LfnMatchV(lfn, name);
|
||||
if (!match && allowShortMatch)
|
||||
match = MatchName83V(dir[i].Name, dir[i].Ext, name);
|
||||
if (!match && allowShortMatch)
|
||||
match = MatchRaw83(dir[i].Name, dir[i].Ext, wantName83, wantExt83);
|
||||
|
||||
if (match) {
|
||||
uint32_t start = hasLfn ? lfnStart : i;
|
||||
for (uint32_t j = start; j <= i; j++)
|
||||
dir[j].Name[0] = (char)0xE5;
|
||||
s = WriteSectors(v, clusterLba, v->SectorsPerCluster, clusterBuf);
|
||||
MmFreePool(clusterBuf);
|
||||
if (KSUCCESS(s)) Fat32PathCacheInvalidate(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
hasLfn = 0;
|
||||
memset(lfn, 0, sizeof(lfn));
|
||||
}
|
||||
|
||||
cluster = FatEntry(v, cluster);
|
||||
}
|
||||
|
||||
MmFreePool(clusterBuf);
|
||||
return STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Open / Close.
|
||||
KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||
@@ -1626,7 +1873,7 @@ KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||
|
||||
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
||||
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
|
||||
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &direntLba, &direntOffset);
|
||||
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, NULL, &direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
FAT32_FILE *f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE));
|
||||
@@ -1646,7 +1893,7 @@ void Fat32Close(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
MmFreePool(fileCtx);
|
||||
}
|
||||
|
||||
// Read (fix #4: contiguous-run coalescing)
|
||||
// Read (fix #4: contiguous-run coalescing)
|
||||
// Cluster-chain lookup is cached per open file, and data spans are allowed to
|
||||
// grow beyond the AHCI bounce size because the device layer now splits large
|
||||
// sector requests for the concrete controller.
|
||||
@@ -1722,7 +1969,7 @@ KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
|
||||
if (totalRead != wanted) {
|
||||
// The directory entry says the file has more data than the cluster chain
|
||||
// or disk read actually supplied. Clear the caller's remainder so stale
|
||||
// or disk read actually supplied. Clear the caller's remainder so stale
|
||||
// memory cannot be interpreted as file contents.
|
||||
memset(out, 0, wanted - totalRead);
|
||||
}
|
||||
@@ -1734,8 +1981,8 @@ KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Write (fix #4: contiguous-run coalescing)
|
||||
// Same strategy as read. Whole-cluster-aligned runs skip the read-modify-
|
||||
// Write (fix #4: contiguous-run coalescing)
|
||||
// Same strategy as read. Whole-cluster-aligned runs skip the read-modify-
|
||||
// write; partial head/tail clusters still require it.
|
||||
|
||||
KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
@@ -1852,121 +2099,99 @@ KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
}
|
||||
|
||||
// Create.
|
||||
// Creates the requested object.
|
||||
// Creates the requested file and fails if the name already exists.
|
||||
KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
||||
SYS_STRING_VIEW parentPath;
|
||||
SYS_STRING_VIEW filenameView;
|
||||
KSTATUS s = SplitPathV(path, &parentPath, &filenameView);
|
||||
uint64_t direntLba = 0;
|
||||
uint32_t direntOffset = 0;
|
||||
KSTATUS s = Fat32CreateDirent(v, path, ATTR_ARCHIVE, 0, 0,
|
||||
&direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
uint32_t parentCluster, parentSize;
|
||||
s = TraverseV(v, parentPath, &parentCluster, &parentSize);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
{
|
||||
uint32_t ec, es; uint8_t ea;
|
||||
uint64_t existingLba = 0; uint32_t existingOffset = 0;
|
||||
if (SearchDirV(v, parentCluster, filenameView, &ec, &es, &ea,
|
||||
&existingLba, &existingOffset)) {
|
||||
if (ea & ATTR_DIRECTORY)
|
||||
return STATUS_OBJECT_NAME_COLLISION;
|
||||
|
||||
FAT32_FILE *f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE));
|
||||
if (!f) return STATUS_NO_MEMORY;
|
||||
f->StartCluster = ec;
|
||||
f->FileSize = es;
|
||||
f->Vol = v;
|
||||
f->DirentLba = existingLba;
|
||||
f->DirentOffset = existingOffset;
|
||||
|
||||
// Create means overwrite by default for regular files. Reuse the
|
||||
// existing directory entry, drop the old cluster chain, and return
|
||||
// an open zero-length file instead of allocating a duplicate name.
|
||||
KSTATUS ts = Fat32Truncate(fs, f, 0, 0);
|
||||
if (!KSUCCESS(ts)) { MmFreePool(f); return ts; }
|
||||
*fileCtx = f;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
int useLfn = FatNameNeedsLfnForCreateV(filenameView);
|
||||
char shortName[8], shortExt[3];
|
||||
if (useLfn) {
|
||||
s = DeleteLegacyBrokenDotAliasV(v, parentCluster, filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!MakeUniqueShortAliasV(v, parentCluster, filenameView, shortName, shortExt))
|
||||
return STATUS_OBJECT_NAME_COLLISION;
|
||||
} else {
|
||||
Make83NameV(filenameView, shortName, shortExt);
|
||||
}
|
||||
|
||||
size_t nameLen = filenameView.Length;
|
||||
if (nameLen == 0 || nameLen >= MAX_LFN) return STATUS_INVALID_PARAMETER;
|
||||
uint32_t lfnCount = useLfn ? (uint32_t)((nameLen + 12u) / 13u) : 0;
|
||||
uint32_t neededEntries = lfnCount + 1u;
|
||||
|
||||
uint32_t entryCluster, firstIndex;
|
||||
if (!FindFreeDirentRun(v, parentCluster, neededEntries, &entryCluster, &firstIndex))
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
|
||||
if (!clusterBuf) return STATUS_NO_MEMORY;
|
||||
|
||||
uint64_t clusterLba = ClusterToLba(v, entryCluster);
|
||||
s = ReadSectors(v, clusterLba, v->SectorsPerCluster, clusterBuf);
|
||||
if (!KSUCCESS(s)) { MmFreePool(clusterBuf); return s; }
|
||||
|
||||
DIRENT *dir = (DIRENT *)clusterBuf;
|
||||
uint32_t shortIndex = firstIndex + lfnCount;
|
||||
|
||||
if (useLfn) {
|
||||
uint8_t checksum = LfnChecksum(shortName, shortExt);
|
||||
for (uint32_t slot = 0; slot < lfnCount; slot++) {
|
||||
uint32_t ordinal = lfnCount - slot;
|
||||
BuildLfnEntry((LFN_ENTRY *)&dir[firstIndex + slot], filenameView.Data,
|
||||
nameLen, ordinal, lfnCount, checksum);
|
||||
}
|
||||
}
|
||||
|
||||
DIRENT *d = &dir[shortIndex];
|
||||
memset(d, 0, sizeof(DIRENT));
|
||||
memcpy(d->Name, shortName, 8);
|
||||
memcpy(d->Ext, shortExt, 3);
|
||||
d->Attr = ATTR_ARCHIVE;
|
||||
d->Reserved = FatShortNameCaseFlagsV(filenameView);
|
||||
{
|
||||
FAT32_ENCODED_TIME now;
|
||||
FatQueryEncodedTime(&now);
|
||||
d->CrtTimeTenth = now.CreateTimeTenth;
|
||||
d->CrtTime = now.Time;
|
||||
d->CrtDate = now.Date;
|
||||
d->LastAccDate = now.Date;
|
||||
d->WrtTime = now.Time;
|
||||
d->WrtDate = now.Date;
|
||||
}
|
||||
|
||||
s = WriteSectors(v, clusterLba, v->SectorsPerCluster, clusterBuf);
|
||||
MmFreePool(clusterBuf);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
uint64_t shortEntryByte = (uint64_t)shortIndex * sizeof(DIRENT);
|
||||
uint64_t direntLba = clusterLba + shortEntryByte / v->BytesPerSector;
|
||||
uint32_t direntOffset = (uint32_t)(shortEntryByte % v->BytesPerSector);
|
||||
|
||||
FAT32_FILE *f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE));
|
||||
if (!f) return STATUS_NO_MEMORY;
|
||||
f->StartCluster = 0; f->FileSize = 0; f->Vol = v;
|
||||
f->DirentLba = direntLba; f->DirentOffset = direntOffset;
|
||||
f->StartCluster = 0;
|
||||
f->FileSize = 0;
|
||||
f->Vol = v;
|
||||
f->DirentLba = direntLba;
|
||||
f->DirentOffset = direntOffset;
|
||||
Fat32FileInitChainCache(f);
|
||||
Fat32PathCacheInvalidate(v);
|
||||
*fileCtx = f;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Creates a directory and initializes its dot entries.
|
||||
KSTATUS Fat32CreateDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
|
||||
if (!fs || !SysSvIsValid(path) || path.Length == 0)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
||||
uint32_t parentCluster = 0;
|
||||
SYS_STRING_VIEW filenameView;
|
||||
KSTATUS s = Fat32ResolveParentForMutation(v, path, &parentCluster, &filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
(void)filenameView;
|
||||
|
||||
uint32_t dirCluster = AllocCluster(v);
|
||||
if (!dirCluster) return STATUS_NO_MEMORY;
|
||||
|
||||
s = Fat32InitializeDirectoryCluster(v, dirCluster, parentCluster);
|
||||
if (!KSUCCESS(s)) {
|
||||
FreeClusterChain(v, dirCluster);
|
||||
FlushFatCache(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
s = Fat32CreateDirent(v, path, ATTR_DIRECTORY, dirCluster, 0, NULL, NULL);
|
||||
if (!KSUCCESS(s)) {
|
||||
FreeClusterChain(v, dirCluster);
|
||||
FlushFatCache(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
FlushFatCache(v);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Removes an empty directory.
|
||||
KSTATUS Fat32RemoveDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
|
||||
if (!fs || !SysSvIsValid(path) || path.Length == 0)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
||||
uint32_t cluster = 0;
|
||||
uint32_t size = 0;
|
||||
uint8_t attr = 0;
|
||||
uint64_t direntLba = 0;
|
||||
uint32_t direntOffset = 0;
|
||||
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &attr,
|
||||
&direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!direntLba) return STATUS_ACCESS_DENIED;
|
||||
if (!(attr & ATTR_DIRECTORY)) return STATUS_ACCESS_DENIED;
|
||||
|
||||
int empty = 0;
|
||||
s = Fat32DirectoryIsEmpty(v, cluster, &empty);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!empty) return STATUS_ACCESS_DENIED;
|
||||
|
||||
uint32_t parentCluster = 0;
|
||||
SYS_STRING_VIEW filenameView;
|
||||
s = Fat32ResolveParentForMutation(v, path, &parentCluster, &filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
s = Fat32DeleteDirentRunV(v, parentCluster, filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
if (cluster >= 2) FreeClusterChain(v, cluster);
|
||||
FlushFatCache(v);
|
||||
Fat32PathCacheInvalidate(v);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Truncate.
|
||||
KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize, uint32_t fileFlags) {
|
||||
(void)fs;
|
||||
@@ -2044,7 +2269,7 @@ KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
|
||||
|
||||
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
||||
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
|
||||
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &direntLba, &direntOffset);
|
||||
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, NULL, &direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!direntLba) return STATUS_ACCESS_DENIED;
|
||||
|
||||
@@ -2066,6 +2291,139 @@ uint64_t Fat32GetSize(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
(void)fs; return ((FAT32_FILE *)fileCtx)->FileSize;
|
||||
}
|
||||
|
||||
// Returns a stable volume identity for metadata and cache keys.
|
||||
static uint64_t Fat32VolumeIdentity(FAT32_VOL *v) {
|
||||
if (!v) return 0;
|
||||
return ((uint64_t)v->DriveIndex << 56) ^
|
||||
((uint64_t)v->PartitionLba << 8) ^
|
||||
((uint64_t)v->RootCluster << 1) ^
|
||||
0x4641543332494445ULL;
|
||||
}
|
||||
|
||||
// Converts FAT attributes to the public file type.
|
||||
static uint32_t Fat32FileTypeFromAttr(uint8_t attr) {
|
||||
return (attr & ATTR_DIRECTORY) ? SYS_FILE_TYPE_DIRECTORY : SYS_FILE_TYPE_REGULAR;
|
||||
}
|
||||
|
||||
// Fills public metadata from one FAT directory entry.
|
||||
static void Fat32FillStatFromDirent(FAT32_VOL *v, const DIRENT *d,
|
||||
uint64_t direntLba, uint32_t direntOffset,
|
||||
SYS_FILE_STAT *out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Size = sizeof(*out);
|
||||
out->Type = Fat32FileTypeFromAttr(d->Attr);
|
||||
out->Attributes = d->Attr;
|
||||
out->LinkCount = 1;
|
||||
out->FileSize = d->FileSize;
|
||||
out->VolumeId = Fat32VolumeIdentity(v);
|
||||
|
||||
uint32_t firstCluster = ((uint32_t)d->FirstClusterHi << 16) | d->FirstClusterLo;
|
||||
out->FileId = direntLba ^
|
||||
((uint64_t)direntOffset << 32) ^
|
||||
((uint64_t)firstCluster << 1);
|
||||
|
||||
FatDecodeDateTime(d->CrtDate, d->CrtTime, d->CrtTimeTenth, &out->CreationTime);
|
||||
FatDecodeAccessDate(d->LastAccDate, &out->LastAccessTime);
|
||||
FatDecodeDateTime(d->WrtDate, d->WrtTime, 0, &out->LastWriteTime);
|
||||
out->ChangeTime = out->LastWriteTime;
|
||||
}
|
||||
|
||||
// Fills public metadata for the filesystem root.
|
||||
static void Fat32FillRootStat(FAT32_VOL *v, SYS_FILE_STAT *out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Size = sizeof(*out);
|
||||
out->Type = SYS_FILE_TYPE_DIRECTORY;
|
||||
out->Attributes = SYS_FILE_ATTR_DIRECTORY;
|
||||
out->LinkCount = 1;
|
||||
out->FileSize = 0;
|
||||
out->VolumeId = Fat32VolumeIdentity(v);
|
||||
out->FileId = ((uint64_t)v->RootCluster << 1) ^ 0x524f4f54ULL;
|
||||
}
|
||||
|
||||
// Queries metadata for an open FAT32 file.
|
||||
KSTATUS Fat32QueryInfo(IO_FILESYSTEM *fs, void *fileCtx, SYS_FILE_STAT *out) {
|
||||
(void)fs;
|
||||
if (!fileCtx || !out) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
|
||||
FAT32_VOL *v = f->Vol;
|
||||
if (!v) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
if (f->DirentLba == 0) {
|
||||
if (f->StartCluster == v->RootCluster && f->FileSize == 0) {
|
||||
Fat32FillRootStat(v, out);
|
||||
} else {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Size = sizeof(*out);
|
||||
out->Type = SYS_FILE_TYPE_REGULAR;
|
||||
out->Attributes = SYS_FILE_ATTR_ARCHIVE;
|
||||
out->LinkCount = 1;
|
||||
out->FileSize = f->FileSize;
|
||||
out->VolumeId = Fat32VolumeIdentity(v);
|
||||
out->FileId = ((uint64_t)f->StartCluster << 1);
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t *sec = (uint8_t *)MmAllocatePool(v->BytesPerSector);
|
||||
if (!sec) return STATUS_NO_MEMORY;
|
||||
|
||||
KSTATUS s = ReadSectors(v, f->DirentLba, 1, sec);
|
||||
if (!KSUCCESS(s)) { MmFreePool(sec); return s; }
|
||||
|
||||
DIRENT *d = (DIRENT *)(sec + f->DirentOffset);
|
||||
if ((uint8_t)d->Name[0] == 0x00 || (uint8_t)d->Name[0] == 0xE5) {
|
||||
MmFreePool(sec);
|
||||
return STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
Fat32FillStatFromDirent(v, d, f->DirentLba, f->DirentOffset, out);
|
||||
f->StartCluster = ((uint32_t)d->FirstClusterHi << 16) | d->FirstClusterLo;
|
||||
f->FileSize = d->FileSize;
|
||||
MmFreePool(sec);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Queries metadata for a FAT32 path.
|
||||
KSTATUS Fat32Stat(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, SYS_FILE_STAT *out) {
|
||||
if (!fs || !out || !SysSvIsValid(path)) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
|
||||
if (!v) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
uint32_t cluster = 0;
|
||||
uint32_t size = 0;
|
||||
uint8_t attr = ATTR_DIRECTORY;
|
||||
uint64_t direntLba = 0;
|
||||
uint32_t direntOffset = 0;
|
||||
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &attr, &direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
(void)cluster;
|
||||
|
||||
if (direntLba == 0) {
|
||||
Fat32FillRootStat(v, out);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t *sec = (uint8_t *)MmAllocatePool(v->BytesPerSector);
|
||||
if (!sec) return STATUS_NO_MEMORY;
|
||||
|
||||
s = ReadSectors(v, direntLba, 1, sec);
|
||||
if (!KSUCCESS(s)) { MmFreePool(sec); return s; }
|
||||
|
||||
DIRENT *d = (DIRENT *)(sec + direntOffset);
|
||||
if ((uint8_t)d->Name[0] == 0x00 || (uint8_t)d->Name[0] == 0xE5) {
|
||||
MmFreePool(sec);
|
||||
return STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
Fat32FillStatFromDirent(v, d, direntLba, direntOffset, out);
|
||||
out->Type = Fat32FileTypeFromAttr(attr);
|
||||
out->FileSize = size;
|
||||
MmFreePool(sec);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Mixes FAT directory metadata into a per-file content generation.
|
||||
static uint64_t Fat32DirentGeneration(const DIRENT *d) {
|
||||
if (!d)
|
||||
@@ -2123,10 +2481,7 @@ KSTATUS Fat32QueryIdentity(IO_FILESYSTEM *fs, void *fileCtx, IO_FILE_IDENTITY *o
|
||||
MmFreePool(sec);
|
||||
}
|
||||
|
||||
out->VolumeId = ((uint64_t)v->DriveIndex << 56) ^
|
||||
((uint64_t)v->PartitionLba << 8) ^
|
||||
((uint64_t)v->RootCluster << 1) ^
|
||||
0x4641543332494445ULL;
|
||||
out->VolumeId = Fat32VolumeIdentity(v);
|
||||
out->FileId = f->DirentLba ^
|
||||
((uint64_t)f->DirentOffset << 32) ^
|
||||
((uint64_t)startCluster << 1);
|
||||
@@ -2135,7 +2490,7 @@ KSTATUS Fat32QueryIdentity(IO_FILESYSTEM *fs, void *fileCtx, IO_FILE_IDENTITY *o
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Directory enumeration (fix #2: cluster buffer cached in context)
|
||||
// Directory enumeration (fix #2: cluster buffer cached in context)
|
||||
// Previously: allocate + ReadSectors + return 1 entry + free, every call.
|
||||
// Now: buffer lives in FAT32_DIR_CTX; ReadSectors fires only on cluster
|
||||
// boundaries.
|
||||
@@ -2418,7 +2773,7 @@ static uint32_t Fat32EnumerateDiskVolumes(uint8_t diskIndex,
|
||||
uint32_t size = ReadLe32(entry + 12);
|
||||
if (!start || !size) continue;
|
||||
|
||||
// FAT32 CHS/LBA plus EFI System Partition. The VBR probe is the
|
||||
// FAT32 CHS/LBA plus EFI System Partition. The VBR probe is the
|
||||
// authority, so accepting 0xEF here lets FAT32 ESPs mount too.
|
||||
if (type == 0x0B || type == 0x0C || type == 0xEF)
|
||||
Fat32AddCandidate(&probe, out, &count, maxCount, start, size);
|
||||
@@ -2928,7 +3283,7 @@ static uint32_t Fat32TryMountDisk(uint8_t disk) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Only remember a completed probe. A transient hotplug/readiness failure
|
||||
// Only remember a completed probe. A transient hotplug/readiness failure
|
||||
// must not permanently suppress a later mount attempt for the same disk
|
||||
// index after a device is unplugged and replugged.
|
||||
gDiskScanned[disk] = true;
|
||||
@@ -3029,7 +3384,7 @@ static void Fat32UnmountDisk(uint8_t diskIndex, IO_DEVICE_CHANGE change) {
|
||||
continue;
|
||||
|
||||
// Surprise removal means the backing device may already be gone; do not
|
||||
// attempt writeback here. Drop cached FAT sectors instead.
|
||||
// attempt writeback here. Drop cached FAT sectors instead.
|
||||
FatCacheInvalidate();
|
||||
(void)IoUnregisterFilesystem(i);
|
||||
memset(&gVols[i], 0, sizeof(gVols[i]));
|
||||
|
||||
@@ -28,9 +28,13 @@ KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, const void
|
||||
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 Fat32CreateDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW path);
|
||||
KSTATUS Fat32RemoveDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW path);
|
||||
KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path);
|
||||
KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize, uint32_t fileFlags);
|
||||
uint64_t Fat32GetSize(IO_FILESYSTEM *fs, void *fileCtx);
|
||||
KSTATUS Fat32QueryInfo(IO_FILESYSTEM *fs, void *fileCtx, SYS_FILE_STAT *out);
|
||||
KSTATUS Fat32Stat(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, SYS_FILE_STAT *out);
|
||||
KSTATUS Fat32QueryIdentity(IO_FILESYSTEM *fs, void *fileCtx, IO_FILE_IDENTITY *out);
|
||||
KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **dirCtx);
|
||||
KSTATUS Fat32ReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out);
|
||||
|
||||
@@ -12,8 +12,12 @@ void Fat32InitVtable(IO_FILESYSTEM* fs, FAT32_VOL* vol) {
|
||||
fs->WriteAsync = Fat32WriteAsync;
|
||||
fs->Create = Fat32Create;
|
||||
fs->Delete = Fat32Delete;
|
||||
fs->CreateDir = Fat32CreateDir;
|
||||
fs->RemoveDir = Fat32RemoveDir;
|
||||
fs->Truncate = Fat32Truncate;
|
||||
fs->GetSize = Fat32GetSize;
|
||||
fs->QueryInfo = Fat32QueryInfo;
|
||||
fs->Stat = Fat32Stat;
|
||||
fs->QueryIdentity = Fat32QueryIdentity;
|
||||
fs->Close = Fat32Close;
|
||||
fs->DriverCtx = vol;
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
File diff suppressed because it is too large.
Load diff
@@ -2,8 +2,13 @@
|
||||
|
||||
#include "ps2.h"
|
||||
|
||||
static KDPC gKeyboardDpc;
|
||||
static KDPC gMouseDpc;
|
||||
static volatile bool gKeyboardNextExtended;
|
||||
static INPUT_KEY_EVENT gKeyboardDpcQueue[KEYBOARD_DPC_QUEUE_SIZE];
|
||||
static volatile int gKeyboardDpcHead;
|
||||
static volatile int gKeyboardDpcTail;
|
||||
static volatile uint8_t gKeyboardDown[256];
|
||||
static volatile uint8_t gMousePacket[3];
|
||||
static volatile int gMousePacketIndex;
|
||||
static PS2_MOUSE_PACKET gMouseDpcQueue[MOUSE_DPC_QUEUE_SIZE];
|
||||
@@ -121,6 +126,40 @@ static INPUT_KEY Ps2Set1ToKey(uint8_t make, bool extended) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the ps2 Keyboard Dpc Routine operation.
|
||||
static void Ps2KeyboardDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) {
|
||||
(void)dpc;
|
||||
(void)context;
|
||||
(void)systemArgument1;
|
||||
(void)systemArgument2;
|
||||
|
||||
while (gKeyboardDpcHead != gKeyboardDpcTail) {
|
||||
INPUT_KEY_EVENT event = gKeyboardDpcQueue[gKeyboardDpcHead];
|
||||
gKeyboardDpcHead = (gKeyboardDpcHead + 1) & (int)KEYBOARD_DPC_QUEUE_MASK;
|
||||
KeInputSubmitKey(&event);
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the ps2 Queue Keyboard Event operation.
|
||||
static void Ps2QueueKeyboardEvent(INPUT_KEY key, bool pressed) {
|
||||
if (key >= sizeof(gKeyboardDown))
|
||||
return;
|
||||
|
||||
bool wasDown = gKeyboardDown[key] != 0;
|
||||
if (pressed == wasDown)
|
||||
return;
|
||||
|
||||
int next = (gKeyboardDpcTail + 1) & (int)KEYBOARD_DPC_QUEUE_MASK;
|
||||
if (next == gKeyboardDpcHead)
|
||||
return;
|
||||
|
||||
gKeyboardDown[key] = pressed ? 1u : 0u;
|
||||
gKeyboardDpcQueue[gKeyboardDpcTail].Key = key;
|
||||
gKeyboardDpcQueue[gKeyboardDpcTail].Pressed = pressed;
|
||||
gKeyboardDpcTail = next;
|
||||
KeInsertQueueDpc(&gKeyboardDpc, 0, 0);
|
||||
}
|
||||
|
||||
// Implements the ps2 Keyboard Isr operation.
|
||||
static void Ps2KeyboardIsr(void) {
|
||||
uint8_t sc = __inbyte(PS2_DATA);
|
||||
@@ -137,11 +176,7 @@ static void Ps2KeyboardIsr(void) {
|
||||
INPUT_KEY key = Ps2Set1ToKey(make, extended);
|
||||
if (key == INPUT_KEY_NONE) return;
|
||||
|
||||
INPUT_KEY_EVENT event = {
|
||||
.Key = key,
|
||||
.Pressed = !released,
|
||||
};
|
||||
KeInputSubmitKey(&event);
|
||||
Ps2QueueKeyboardEvent(key, !released);
|
||||
}
|
||||
|
||||
// Implements the ps2 Mouse Dpc Routine operation.
|
||||
@@ -224,7 +259,7 @@ static void Ps2ControllerEnableInterrupts(void) {
|
||||
|
||||
// Implements the ps2 Keyboard Initialize operation.
|
||||
static void Ps2KeyboardInitialize(void) {
|
||||
// Enable scanning. If the keyboard is absent, the timeout/ACK failure is
|
||||
// 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");
|
||||
@@ -254,10 +289,11 @@ KSTATUS DriverEntry(const HAL_API* hal) {
|
||||
KeInputRegisterDevice("ps2-keyboard", 0, &keyboardId);
|
||||
KeInputRegisterDevice("ps2-mouse", 0, &mouseId);
|
||||
|
||||
KeInitializeDpc(&gKeyboardDpc, Ps2KeyboardDpcRoutine, 0);
|
||||
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
|
||||
// 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();
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#define SC1_MAKE_MASK 0x7Fu
|
||||
#define SC1_EXT_PREFIX 0xE0u
|
||||
|
||||
#define KEYBOARD_DPC_QUEUE_SIZE 64u
|
||||
#define KEYBOARD_DPC_QUEUE_MASK (KEYBOARD_DPC_QUEUE_SIZE - 1u)
|
||||
#define MOUSE_DPC_QUEUE_SIZE 64u
|
||||
#define MOUSE_DPC_QUEUE_MASK (MOUSE_DPC_QUEUE_SIZE - 1u)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <ntlite/ddi/input_device.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
|
||||
#define SERIAL_COM1_BASE 0x3F8u
|
||||
@@ -35,6 +36,9 @@
|
||||
#define SERIAL_FCR_CLEAR_TX 0x04u
|
||||
#define SERIAL_FCR_TRIGGER_14 0xC0u
|
||||
|
||||
#define SERIAL_RX_QUEUE_SIZE 1024u
|
||||
#define SERIAL_RX_QUEUE_MASK (SERIAL_RX_QUEUE_SIZE - 1u)
|
||||
|
||||
typedef enum SERIAL_ESCAPE_STATE {
|
||||
SerialEscapeNone = 0,
|
||||
SerialEscapeEsc,
|
||||
@@ -42,9 +46,14 @@ typedef enum SERIAL_ESCAPE_STATE {
|
||||
SerialEscapeCsiNumber,
|
||||
} SERIAL_ESCAPE_STATE;
|
||||
|
||||
static KDPC gSerialRxDpc;
|
||||
static volatile SERIAL_ESCAPE_STATE gSerialEscapeState;
|
||||
static volatile uint8_t gSerialCsiNumber;
|
||||
static volatile bool gSerialDropNextLf;
|
||||
static uint8_t gSerialRxQueue[SERIAL_RX_QUEUE_SIZE];
|
||||
static volatile uint32_t gSerialRxHead;
|
||||
static volatile uint32_t gSerialRxTail;
|
||||
static volatile uint32_t gSerialRxDropped;
|
||||
|
||||
// Reads one byte from a COM register.
|
||||
static uint8_t SerialRead(uint16_t reg) {
|
||||
@@ -254,12 +263,41 @@ static void SerialReceiveByte(uint8_t ch) {
|
||||
SerialSubmitPrintable(ch);
|
||||
}
|
||||
|
||||
// Handles queued receive bytes at DISPATCH_LEVEL.
|
||||
static void SerialRxDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) {
|
||||
(void)dpc;
|
||||
(void)context;
|
||||
(void)systemArgument1;
|
||||
(void)systemArgument2;
|
||||
|
||||
while (gSerialRxHead != gSerialRxTail) {
|
||||
uint32_t head = gSerialRxHead;
|
||||
uint8_t ch = gSerialRxQueue[head];
|
||||
gSerialRxHead = (head + 1u) & SERIAL_RX_QUEUE_MASK;
|
||||
SerialReceiveByte(ch);
|
||||
}
|
||||
}
|
||||
|
||||
// Queues a received byte for bottom-half processing.
|
||||
static void SerialQueueRxByte(uint8_t ch) {
|
||||
uint32_t tail = gSerialRxTail;
|
||||
uint32_t next = (tail + 1u) & SERIAL_RX_QUEUE_MASK;
|
||||
if (next == gSerialRxHead) {
|
||||
gSerialRxDropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
gSerialRxQueue[tail] = ch;
|
||||
gSerialRxTail = next;
|
||||
KeInsertQueueDpc(&gSerialRxDpc, 0, 0);
|
||||
}
|
||||
|
||||
// Handles receive interrupts for COM1.
|
||||
static void SerialIsr(void) {
|
||||
for (uint32_t i = 0; i < 256u; i++) {
|
||||
if ((SerialRead(SERIAL_REG_LSR) & SERIAL_LSR_DATA_READY) == 0)
|
||||
break;
|
||||
SerialReceiveByte(SerialRead(SERIAL_REG_DATA));
|
||||
SerialQueueRxByte(SerialRead(SERIAL_REG_DATA));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,9 +327,13 @@ KSTATUS DriverEntry(const HAL_API* hal) {
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
|
||||
KeInitializeDpc(&gSerialRxDpc, SerialRxDpcRoutine, 0);
|
||||
gSerialEscapeState = SerialEscapeNone;
|
||||
gSerialCsiNumber = 0;
|
||||
gSerialDropNextLf = false;
|
||||
gSerialRxHead = 0;
|
||||
gSerialRxTail = 0;
|
||||
gSerialRxDropped = 0;
|
||||
|
||||
SerialInitializeCom1();
|
||||
KeIrqRegister(SERIAL_COM1_IRQ, SerialIsr);
|
||||
|
||||
@@ -32,12 +32,12 @@ typedef struct sys_mbox* sys_mbox_t;
|
||||
#define sys_mbox_set_invalid_val(mbox) do { (mbox) = NULL; } while (0)
|
||||
|
||||
/*
|
||||
* lwIP only treats sys_thread_t as an opaque handle. Keep it pointer-sized.
|
||||
* lwIP only treats sys_thread_t as an opaque handle. Keep it pointer-sized.
|
||||
*
|
||||
* Do NOT make this a by-value incomplete struct. MSVC may compile callers
|
||||
* Do NOT make this a by-value incomplete struct. MSVC may compile callers
|
||||
* that see only the incomplete type with a hidden struct-return parameter,
|
||||
* which shifts sys_thread_new(name, thread, arg, ...) so the callee receives
|
||||
* thread == name and arg == thread. The first tcpip_thread then jumps into
|
||||
* thread == name and arg == thread. The first tcpip_thread then jumps into
|
||||
* the literal string "tcpip" as code.
|
||||
*/
|
||||
typedef void* sys_thread_t;
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
/*
|
||||
* lwipopts.h - kernel port configuration for lwIP 2.2.1.
|
||||
*
|
||||
* This is intentionally a practical first configuration: IPv4, ARP, ICMP,
|
||||
* UDP, TCP, DHCP, DNS and netconn are enabled; IPv6, PPP,
|
||||
* SNMP, lwIP's POSIX socket API, and app protocols stay off.
|
||||
* This is intentionally a practical first configuration: IPv4, IPv6, ARP,
|
||||
* ICMP, ICMPv6, UDP, TCP, DHCP, DNS and netconn are enabled; PPP, SNMP,
|
||||
* lwIP's POSIX socket API, and app protocols stay off.
|
||||
*/
|
||||
|
||||
#define NO_SYS 0
|
||||
@@ -28,10 +28,11 @@
|
||||
#define MEMP_NUM_SYS_TIMEOUT 32
|
||||
|
||||
#define LWIP_IPV4 1
|
||||
#define LWIP_IPV6 0
|
||||
#define LWIP_IPV6 1
|
||||
#define LWIP_ARP 1
|
||||
#define LWIP_ETHERNET 1
|
||||
#define LWIP_ICMP 1
|
||||
#define LWIP_ICMP6 1
|
||||
#define LWIP_IGMP 0
|
||||
#define LWIP_RAW 1
|
||||
#define LWIP_UDP 1
|
||||
@@ -39,6 +40,10 @@
|
||||
#define LWIP_DHCP 1
|
||||
#define LWIP_AUTOIP 0
|
||||
#define LWIP_DNS 1
|
||||
#define LWIP_ND6_QUEUEING 1
|
||||
#define LWIP_IPV6_AUTOCONFIG 1
|
||||
#define LWIP_IPV6_MLD 1
|
||||
#define MEMP_NUM_ND6_QUEUE 8
|
||||
|
||||
#define TCP_MSS 1460
|
||||
#define TCP_WND (4 * TCP_MSS)
|
||||
@@ -81,6 +86,8 @@
|
||||
#define CHECKSUM_CHECK_IP 1
|
||||
#define CHECKSUM_CHECK_UDP 1
|
||||
#define CHECKSUM_CHECK_TCP 1
|
||||
#define CHECKSUM_GEN_ICMP6 1
|
||||
#define CHECKSUM_CHECK_ICMP6 1
|
||||
|
||||
#define LWIP_TIMERS 1
|
||||
#define LWIP_TCPIP_TIMEOUT 1
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
/*
|
||||
* Minimal freestanding stdlib shim for lwIP.
|
||||
*
|
||||
* lwIP 2.2.1 includes <stdlib.h> in a couple of files only for atoi(). If MSVC
|
||||
* lwIP 2.2.1 includes <stdlib.h> in a couple of files only for atoi(). If MSVC
|
||||
* finds the UCRT header, it drags errno.h into the kernel build and then lwIP's
|
||||
* own errno values collide with UCRT's. Put this header first in lwIP's include
|
||||
* own errno values collide with UCRT's. Put this header first in lwIP's include
|
||||
* path so those sources stay freestanding.
|
||||
*/
|
||||
static __inline int atoi(const char* s) {
|
||||
|
||||
@@ -39,6 +39,18 @@ third_party/lwip/src/core/ipv4/ip4_frag.c
|
||||
third_party/lwip/src/core/ipv4/ip4.c
|
||||
third_party/lwip/src/core/ipv4/ip4_addr.c
|
||||
|
||||
|
||||
# IPv6
|
||||
third_party/lwip/src/core/ipv6/dhcp6.c
|
||||
third_party/lwip/src/core/ipv6/ethip6.c
|
||||
third_party/lwip/src/core/ipv6/icmp6.c
|
||||
third_party/lwip/src/core/ipv6/inet6.c
|
||||
third_party/lwip/src/core/ipv6/ip6_frag.c
|
||||
third_party/lwip/src/core/ipv6/ip6.c
|
||||
third_party/lwip/src/core/ipv6/ip6_addr.c
|
||||
third_party/lwip/src/core/ipv6/mld6.c
|
||||
third_party/lwip/src/core/ipv6/nd6.c
|
||||
|
||||
# netconn/socket API
|
||||
third_party/lwip/src/api/api_lib.c
|
||||
third_party/lwip/src/api/api_msg.c
|
||||
|
||||
@@ -22,13 +22,16 @@
|
||||
#include <lwip/netif.h>
|
||||
#include <lwip/pbuf.h>
|
||||
#include <lwip/etharp.h>
|
||||
#include <lwip/ethip6.h>
|
||||
#include <lwip/dhcp.h>
|
||||
#include <lwip/dns.h>
|
||||
#include <lwip/ip4_addr.h>
|
||||
#include <lwip/ip6_addr.h>
|
||||
#include <lwip/ip_addr.h>
|
||||
#include <lwip/api.h>
|
||||
#include <lwip/err.h>
|
||||
#include <lwip/prot/ip.h>
|
||||
#include <lwip/prot/ip6.h>
|
||||
#include <netif/ethernet.h>
|
||||
|
||||
#define LWIP_MAX_NETIFS 8u
|
||||
@@ -42,12 +45,18 @@
|
||||
|
||||
typedef struct LWIP_NETIF_CONFIG {
|
||||
uint8_t UseDhcp;
|
||||
uint8_t HasDns;
|
||||
uint8_t HasDns4;
|
||||
uint8_t EnableIpv6;
|
||||
uint8_t Ipv6Autoconfig;
|
||||
uint8_t HasStaticIpv6;
|
||||
uint8_t HasDns6;
|
||||
char Section[64];
|
||||
ip4_addr_t Address;
|
||||
ip4_addr_t Netmask;
|
||||
ip4_addr_t Gateway;
|
||||
ip4_addr_t Dns;
|
||||
ip4_addr_t Dns4;
|
||||
ip6_addr_t Address6;
|
||||
ip6_addr_t Dns6;
|
||||
} LWIP_NETIF_CONFIG;
|
||||
|
||||
typedef struct LWIP_BINDING {
|
||||
@@ -94,19 +103,19 @@ static KSTATUS LwipProviderAttachDevice(NET_DEVICE* device);
|
||||
static KSTATUS LwipProviderReceiveFrame(NET_DEVICE* device, const void* frame, size_t length);
|
||||
static KSTATUS LwipProviderSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, void** outSocket);
|
||||
static void LwipProviderSocketClose(void* socket);
|
||||
static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize);
|
||||
static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
|
||||
static KSTATUS LwipProviderListen(void* socket, uint32_t backlog);
|
||||
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut);
|
||||
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize);
|
||||
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut);
|
||||
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
|
||||
static KSTATUS LwipProviderSend(void* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut);
|
||||
static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut);
|
||||
static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_IPV4_K* remote, size_t remoteSize, size_t* sentOut);
|
||||
static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut, size_t* receivedOut);
|
||||
static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut);
|
||||
static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut);
|
||||
static KSTATUS LwipProviderShutdown(void* socket, uint32_t how);
|
||||
static KSTATUS LwipProviderSetOption(void* socket, uint32_t option, const void* value, size_t valueSize);
|
||||
static KSTATUS LwipProviderGetOption(void* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut);
|
||||
static KSTATUS LwipProviderPoll(void* socket, uint32_t events, uint32_t* returnedEventsOut);
|
||||
static KSTATUS LwipProviderResolveHostIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4_K* addressOut);
|
||||
static KSTATUS LwipProviderResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut);
|
||||
|
||||
static const NET_STACK_PROVIDER gLwipProvider = {
|
||||
NET_STACK_PROVIDER_VERSION,
|
||||
@@ -127,7 +136,7 @@ static const NET_STACK_PROVIDER gLwipProvider = {
|
||||
LwipProviderSetOption,
|
||||
LwipProviderGetOption,
|
||||
LwipProviderPoll,
|
||||
LwipProviderResolveHostIpv4,
|
||||
LwipProviderResolveHost,
|
||||
};
|
||||
|
||||
// Implements the err To Status operation.
|
||||
@@ -137,7 +146,7 @@ static KSTATUS LwipErrToStatus(err_t err) {
|
||||
case ERR_MEM: return STATUS_NO_MEMORY;
|
||||
case ERR_BUF: return STATUS_BUFFER_TOO_SMALL;
|
||||
case ERR_TIMEOUT: return STATUS_TIMEOUT;
|
||||
case ERR_WOULDBLOCK:return STATUS_TIMEOUT;
|
||||
case ERR_WOULDBLOCK:return STATUS_WOULD_BLOCK;
|
||||
case ERR_RTE: return STATUS_NOT_FOUND;
|
||||
case ERR_VAL: return STATUS_INVALID_PARAMETER;
|
||||
case ERR_ARG: return STATUS_INVALID_PARAMETER;
|
||||
@@ -209,6 +218,57 @@ static KSTATUS LwipGetSettingIpv4(const char* section, const char* key,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS LwipGetSettingIpv6(const char* section, const char* key,
|
||||
ip6_addr_t* out, int required) {
|
||||
char value[LWIP_SETTINGS_VALUE_MAX];
|
||||
KSTATUS st = KeGetSettingString(section, key, value, sizeof(value));
|
||||
if (st == STATUS_NOT_FOUND && !required)
|
||||
return STATUS_NOT_FOUND;
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
if (!ip6addr_aton(value, out)) {
|
||||
DbgPrint("lwIP: invalid IPv6 setting [%s] %s=%s\n", section, key, value);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS LwipLoadIpv6Config(const char* section, LWIP_NETIF_CONFIG* cfg) {
|
||||
char mode[LWIP_SETTINGS_VALUE_MAX];
|
||||
KSTATUS st = KeGetSettingString(section, "ipv6", mode, sizeof(mode));
|
||||
if (st == STATUS_NOT_FOUND) {
|
||||
cfg->EnableIpv6 = 1;
|
||||
cfg->Ipv6Autoconfig = 1;
|
||||
} else if (!KSUCCESS(st)) {
|
||||
return st;
|
||||
} else if (strcmp(mode, "off") == 0 || strcmp(mode, "disabled") == 0) {
|
||||
cfg->EnableIpv6 = 0;
|
||||
} else if (strcmp(mode, "auto") == 0) {
|
||||
cfg->EnableIpv6 = 1;
|
||||
cfg->Ipv6Autoconfig = 1;
|
||||
} else if (strcmp(mode, "static") == 0) {
|
||||
cfg->EnableIpv6 = 1;
|
||||
cfg->HasStaticIpv6 = 1;
|
||||
st = LwipGetSettingIpv6(section, "address6", &cfg->Address6, 1);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
} else {
|
||||
DbgPrint("lwIP: invalid IPv6 mode [%s] ipv6=%s\n", section, mode);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!cfg->EnableIpv6)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
st = LwipGetSettingIpv6(section, "dns6", &cfg->Dns6, 0);
|
||||
if (KSUCCESS(st))
|
||||
cfg->HasDns6 = 1;
|
||||
else if (st != STATUS_NOT_FOUND)
|
||||
return st;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Copies section Name.
|
||||
static KSTATUS LwipCopySectionName(char* out, size_t outSize, const char* prefix, const char* name) {
|
||||
if (!out || !outSize || !prefix || !name)
|
||||
@@ -248,20 +308,20 @@ static KSTATUS LwipLoadConfigFromSection(const char* section, LWIP_NETIF_CONFIG*
|
||||
if (!KSUCCESS(st)) return st;
|
||||
st = LwipGetSettingIpv4(section, "gateway", &cfg->Gateway, 1);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
st = LwipGetSettingIpv4(section, "dns", &cfg->Dns, 1);
|
||||
st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, 1);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
cfg->HasDns = 1;
|
||||
return STATUS_SUCCESS;
|
||||
cfg->HasDns4 = 1;
|
||||
return LwipLoadIpv6Config(section, cfg);
|
||||
}
|
||||
|
||||
if (strcmp(mode, "dhcp") == 0) {
|
||||
cfg->UseDhcp = 1;
|
||||
st = LwipGetSettingIpv4(section, "dns", &cfg->Dns, 0);
|
||||
st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, 0);
|
||||
if (KSUCCESS(st))
|
||||
cfg->HasDns = 1;
|
||||
cfg->HasDns4 = 1;
|
||||
else if (st != STATUS_NOT_FOUND)
|
||||
return st;
|
||||
return STATUS_SUCCESS;
|
||||
return LwipLoadIpv6Config(section, cfg);
|
||||
}
|
||||
|
||||
DbgPrint("lwIP: invalid network mode [%s] mode=%s\n", section, mode);
|
||||
@@ -372,18 +432,71 @@ static KSTATUS LwipSocketAllocateWithConn(uint32_t domain, uint32_t type, uint32
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int LwipSocketAddressValid(const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
|
||||
if (!address || addressSize < sizeof(SYS_SOCKET_ADDRESS_K))
|
||||
return 0;
|
||||
if (address->Size < sizeof(SYS_SOCKET_ADDRESS_K))
|
||||
return 0;
|
||||
return address->Family == SYS_AF_INET || address->Family == SYS_AF_INET6;
|
||||
}
|
||||
|
||||
static int LwipSocketFamilyMatches(const LWIP_PROVIDER_SOCKET* s, const SYS_SOCKET_ADDRESS_K* address) {
|
||||
if (!s || !address)
|
||||
return 0;
|
||||
if (s->Domain == SYS_AF_INET)
|
||||
return address->Family == SYS_AF_INET;
|
||||
if (s->Domain == SYS_AF_INET6)
|
||||
return address->Family == SYS_AF_INET6;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implements the socket Make Ip operation.
|
||||
static void LwipSocketMakeIp(const SYS_SOCKET_ADDRESS_IPV4_K* address, ip_addr_t* out) {
|
||||
IP_ADDR4(out, address->Address[0], address->Address[1], address->Address[2], address->Address[3]);
|
||||
static KSTATUS LwipSocketMakeIp(const SYS_SOCKET_ADDRESS_K* address, ip_addr_t* out) {
|
||||
if (!address || !out)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (address->Family == SYS_AF_INET) {
|
||||
IP_ADDR4(out, address->Address[0], address->Address[1], address->Address[2], address->Address[3]);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
if (address->Family == SYS_AF_INET6) {
|
||||
uint32_t a0 = ((uint32_t)address->Address[0] << 24) | ((uint32_t)address->Address[1] << 16) |
|
||||
((uint32_t)address->Address[2] << 8) | (uint32_t)address->Address[3];
|
||||
uint32_t a1 = ((uint32_t)address->Address[4] << 24) | ((uint32_t)address->Address[5] << 16) |
|
||||
((uint32_t)address->Address[6] << 8) | (uint32_t)address->Address[7];
|
||||
uint32_t a2 = ((uint32_t)address->Address[8] << 24) | ((uint32_t)address->Address[9] << 16) |
|
||||
((uint32_t)address->Address[10] << 8) | (uint32_t)address->Address[11];
|
||||
uint32_t a3 = ((uint32_t)address->Address[12] << 24) | ((uint32_t)address->Address[13] << 16) |
|
||||
((uint32_t)address->Address[14] << 8) | (uint32_t)address->Address[15];
|
||||
IP_ADDR6_HOST(out, a0, a1, a2, a3);
|
||||
ip6_addr_set_zone(ip_2_ip6(out), (u8_t)address->ScopeId);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Implements the socket Fill Address operation.
|
||||
static void LwipSocketFillAddress(SYS_SOCKET_ADDRESS_IPV4_K* out, const ip_addr_t* ip, u16_t port) {
|
||||
static void LwipSocketFillAddress(SYS_SOCKET_ADDRESS_K* out, const ip_addr_t* ip, u16_t port) {
|
||||
if (!out)
|
||||
return;
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Family = SYS_AF_INET;
|
||||
out->Size = sizeof(*out);
|
||||
out->Port = port;
|
||||
if (!ip)
|
||||
return;
|
||||
if (IP_IS_V6(ip)) {
|
||||
out->Family = SYS_AF_INET6;
|
||||
out->ScopeId = ip6_addr_zone(ip_2_ip6(ip));
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
uint32_t word = lwip_htonl(ip_2_ip6(ip)->addr[i]);
|
||||
out->Address[i * 4 + 0] = (uint8_t)(word >> 24);
|
||||
out->Address[i * 4 + 1] = (uint8_t)(word >> 16);
|
||||
out->Address[i * 4 + 2] = (uint8_t)(word >> 8);
|
||||
out->Address[i * 4 + 3] = (uint8_t)word;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
out->Family = SYS_AF_INET;
|
||||
out->Address[0] = (uint8_t)ip4_addr1(ip_2_ip4(ip));
|
||||
out->Address[1] = (uint8_t)ip4_addr2(ip_2_ip4(ip));
|
||||
out->Address[2] = (uint8_t)ip4_addr3(ip_2_ip4(ip));
|
||||
@@ -428,11 +541,17 @@ static err_t LwipNetifInit(struct netif* netif) {
|
||||
netif->name[0] = 'e';
|
||||
netif->name[1] = 'n';
|
||||
netif->output = etharp_output;
|
||||
#if LWIP_IPV6
|
||||
netif->output_ip6 = ethip6_output;
|
||||
#endif
|
||||
netif->linkoutput = LwipLowLevelOutput;
|
||||
netif->mtu = (u16_t)(device->Mtu ? device->Mtu : NET_MTU_ETHERNET);
|
||||
netif->hwaddr_len = NET_ETHERNET_ADDRESS_LENGTH;
|
||||
memcpy(netif->hwaddr, device->HardwareAddress, NET_ETHERNET_ADDRESS_LENGTH);
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
||||
#if LWIP_IPV6_MLD
|
||||
netif->flags |= NETIF_FLAG_MLD6;
|
||||
#endif
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@@ -476,12 +595,29 @@ static void LwipAttachCallback(void* arg) {
|
||||
if (netif_default == NULL)
|
||||
netif_set_default(n);
|
||||
|
||||
if (b->Config.HasDns) {
|
||||
if (b->Config.HasDns4) {
|
||||
ip_addr_t dns;
|
||||
ip_addr_copy_from_ip4(dns, b->Config.Dns);
|
||||
ip_addr_copy_from_ip4(dns, b->Config.Dns4);
|
||||
dns_setserver(0, &dns);
|
||||
}
|
||||
|
||||
#if LWIP_IPV6
|
||||
if (b->Config.EnableIpv6) {
|
||||
netif_create_ip6_linklocal_address(n, 1);
|
||||
if (b->Config.Ipv6Autoconfig)
|
||||
n->ip6_autoconfig_enabled = 1;
|
||||
if (b->Config.HasStaticIpv6) {
|
||||
netif_ip6_addr_set(n, 1, &b->Config.Address6);
|
||||
netif_ip6_addr_set_state(n, 1, IP6_ADDR_PREFERRED);
|
||||
}
|
||||
if (b->Config.HasDns6) {
|
||||
ip_addr_t dns6;
|
||||
ip_addr_copy_from_ip6(dns6, b->Config.Dns6);
|
||||
dns_setserver(1, &dns6);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (b->Config.UseDhcp) {
|
||||
#if LWIP_DHCP
|
||||
err_t dhcpErr = dhcp_start(n);
|
||||
@@ -586,11 +722,12 @@ static KSTATUS LwipProviderAttachDevice(NET_DEVICE* device) {
|
||||
}
|
||||
|
||||
gBindings[device->Index] = b;
|
||||
DbgPrint("lwIP: attached %s as netif %c%c%u using [%s] mode=%s\n",
|
||||
DbgPrint("lwIP: attached %s as netif %c%c%u using [%s] mode=%s ipv6=%s\n",
|
||||
device->Name,
|
||||
b->Netif.name[0], b->Netif.name[1], b->Netif.num,
|
||||
b->Config.Section,
|
||||
b->Config.UseDhcp ? "dhcp" : "static");
|
||||
b->Config.UseDhcp ? "dhcp" : "static",
|
||||
b->Config.EnableIpv6 ? (b->Config.HasStaticIpv6 ? "static" : "auto") : "off");
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -636,26 +773,27 @@ static KSTATUS LwipProviderSocketCreate(uint32_t domain, uint32_t type, uint32_t
|
||||
|
||||
if (!gLwipInitialized)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
if (domain != SYS_AF_INET)
|
||||
if (domain != SYS_AF_INET && domain != SYS_AF_INET6)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
int ipv6 = domain == SYS_AF_INET6;
|
||||
enum netconn_type netconnType;
|
||||
u8_t rawProtocol = 0;
|
||||
if (type == SYS_SOCK_STREAM) {
|
||||
if (protocol != 0 && protocol != SYS_IPPROTO_TCP)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
netconnType = NETCONN_TCP;
|
||||
netconnType = ipv6 ? NETCONN_TCP_IPV6 : NETCONN_TCP;
|
||||
protocol = SYS_IPPROTO_TCP;
|
||||
} else if (type == SYS_SOCK_DGRAM) {
|
||||
if (protocol != 0 && protocol != SYS_IPPROTO_UDP)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
netconnType = NETCONN_UDP;
|
||||
netconnType = ipv6 ? NETCONN_UDP_IPV6 : NETCONN_UDP;
|
||||
protocol = SYS_IPPROTO_UDP;
|
||||
} else if (type == SYS_SOCK_RAW) {
|
||||
if (protocol != SYS_IPPROTO_ICMP)
|
||||
if ((!ipv6 && protocol != SYS_IPPROTO_ICMP) || (ipv6 && protocol != SYS_IPPROTO_ICMPV6))
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
netconnType = NETCONN_RAW;
|
||||
rawProtocol = IP_PROTO_ICMP;
|
||||
netconnType = ipv6 ? NETCONN_RAW_IPV6 : NETCONN_RAW;
|
||||
rawProtocol = ipv6 ? IP6_NEXTH_ICMP6 : IP_PROTO_ICMP;
|
||||
} else {
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
@@ -703,15 +841,15 @@ static void LwipProviderSocketClose(void* socket) {
|
||||
}
|
||||
|
||||
// Implements the provider Bind operation.
|
||||
static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize) {
|
||||
static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
|
||||
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
||||
if (!s || !address)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (addressSize < sizeof(SYS_SOCKET_ADDRESS_IPV4_K))
|
||||
if (!LwipSocketAddressValid(address, addressSize))
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
if ((s->Type != SYS_SOCK_STREAM && s->Type != SYS_SOCK_DGRAM) || !s->Conn)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (address->Family != SYS_AF_INET)
|
||||
if (!LwipSocketFamilyMatches(s, address))
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
KSTATUS st = LwipSocketLock(s);
|
||||
@@ -724,7 +862,11 @@ static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* a
|
||||
}
|
||||
|
||||
ip_addr_t ip;
|
||||
LwipSocketMakeIp(address, &ip);
|
||||
st = LwipSocketMakeIp(address, &ip);
|
||||
if (!KSUCCESS(st)) {
|
||||
LwipSocketUnlock(s);
|
||||
return st;
|
||||
}
|
||||
err_t err = netconn_bind(s->Conn, &ip, address->Port);
|
||||
if (err == ERR_OK)
|
||||
s->Bound = 1;
|
||||
@@ -763,7 +905,7 @@ static KSTATUS LwipProviderListen(void* socket, uint32_t backlog) {
|
||||
}
|
||||
|
||||
// Implements the provider Accept operation.
|
||||
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut) {
|
||||
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut) {
|
||||
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
||||
if (!s || !acceptedOut)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
@@ -807,11 +949,12 @@ static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_A
|
||||
ip_addr_t peerIp;
|
||||
u16_t peerPort = 0;
|
||||
err = netconn_peer(acceptedConn, &peerIp, &peerPort);
|
||||
if (err == ERR_OK && IP_IS_V4(&peerIp)) {
|
||||
if (err == ERR_OK) {
|
||||
LwipSocketFillAddress(remoteOut, &peerIp, peerPort);
|
||||
} else {
|
||||
memset(remoteOut, 0, sizeof(*remoteOut));
|
||||
remoteOut->Family = SYS_AF_INET;
|
||||
remoteOut->Size = sizeof(*remoteOut);
|
||||
remoteOut->Family = s->Domain;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,15 +963,15 @@ static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_A
|
||||
}
|
||||
|
||||
// Implements the provider Connect operation.
|
||||
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize) {
|
||||
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
|
||||
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
||||
if (!s || !address)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (addressSize < sizeof(SYS_SOCKET_ADDRESS_IPV4_K))
|
||||
if (!LwipSocketAddressValid(address, addressSize))
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
if ((s->Type != SYS_SOCK_STREAM && s->Type != SYS_SOCK_DGRAM) || !s->Conn)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (address->Family != SYS_AF_INET)
|
||||
if (!LwipSocketFamilyMatches(s, address))
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
KSTATUS st = LwipSocketLock(s);
|
||||
@@ -845,7 +988,11 @@ static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K
|
||||
}
|
||||
|
||||
ip_addr_t ip;
|
||||
LwipSocketMakeIp(address, &ip);
|
||||
st = LwipSocketMakeIp(address, &ip);
|
||||
if (!KSUCCESS(st)) {
|
||||
LwipSocketUnlock(s);
|
||||
return st;
|
||||
}
|
||||
|
||||
err_t err = netconn_connect(s->Conn, &ip, address->Port);
|
||||
if (err == ERR_OK)
|
||||
@@ -1007,13 +1154,13 @@ static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, ui
|
||||
}
|
||||
|
||||
static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags,
|
||||
const SYS_SOCKET_ADDRESS_IPV4_K* remote, size_t remoteSize, size_t* sentOut) {
|
||||
const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut) {
|
||||
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
||||
if (sentOut)
|
||||
*sentOut = 0;
|
||||
if (!s || (!buffer && length) || !remote)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (remoteSize < sizeof(SYS_SOCKET_ADDRESS_IPV4_K))
|
||||
if (!LwipSocketAddressValid(remote, remoteSize))
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
if (flags != 0)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
@@ -1021,7 +1168,7 @@ static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t lengt
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (s->Type == SYS_SOCK_RAW && length > LWIP_PROVIDER_SOCKET_RAW_MAX_BYTES)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (remote->Family != SYS_AF_INET)
|
||||
if (!LwipSocketFamilyMatches(s, remote))
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
if (length == 0)
|
||||
return STATUS_SUCCESS;
|
||||
@@ -1046,8 +1193,11 @@ static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t lengt
|
||||
err_t err = netbuf_ref(nb, buffer, (u16_t)length);
|
||||
if (err == ERR_OK) {
|
||||
ip_addr_t ip;
|
||||
LwipSocketMakeIp(remote, &ip);
|
||||
err = netconn_sendto(s->Conn, nb, &ip, remote->Port);
|
||||
KSTATUS makeStatus = LwipSocketMakeIp(remote, &ip);
|
||||
if (KSUCCESS(makeStatus))
|
||||
err = netconn_sendto(s->Conn, nb, &ip, remote->Port);
|
||||
else
|
||||
err = ERR_ARG;
|
||||
}
|
||||
netbuf_delete(nb);
|
||||
|
||||
@@ -1059,7 +1209,7 @@ static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t lengt
|
||||
}
|
||||
|
||||
static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags,
|
||||
SYS_SOCKET_ADDRESS_IPV4_K* remoteOut, size_t* receivedOut) {
|
||||
SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut) {
|
||||
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
|
||||
if (receivedOut)
|
||||
*receivedOut = 0;
|
||||
@@ -1114,11 +1264,12 @@ static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length
|
||||
|
||||
if (remoteOut) {
|
||||
const ip_addr_t* from = netbuf_fromaddr(nb);
|
||||
if (from && IP_IS_V4(from)) {
|
||||
if (from) {
|
||||
LwipSocketFillAddress(remoteOut, from, netbuf_fromport(nb));
|
||||
} else {
|
||||
memset(remoteOut, 0, sizeof(*remoteOut));
|
||||
remoteOut->Family = SYS_AF_INET;
|
||||
remoteOut->Size = sizeof(*remoteOut);
|
||||
remoteOut->Family = s->Domain;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1271,27 +1422,30 @@ static KSTATUS LwipProviderShutdown(void* socket, uint32_t how) {
|
||||
return LwipErrToStatus(err);
|
||||
}
|
||||
|
||||
// Implements the provider Resolve Host Ipv4 operation.
|
||||
static KSTATUS LwipProviderResolveHostIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4_K* addressOut) {
|
||||
// Implements the provider Resolve Host operation.
|
||||
static KSTATUS LwipProviderResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut) {
|
||||
if (!name || !addressOut)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (!gLwipInitialized)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
u8_t dnsType;
|
||||
if (family == SYS_AF_INET)
|
||||
dnsType = NETCONN_DNS_IPV4;
|
||||
else if (family == SYS_AF_INET6)
|
||||
dnsType = NETCONN_DNS_IPV6;
|
||||
else
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
ip_addr_t resolved;
|
||||
err_t err = netconn_gethostbyname_addrtype(name, &resolved, NETCONN_DNS_IPV4);
|
||||
err_t err = netconn_gethostbyname_addrtype(name, &resolved, dnsType);
|
||||
if (err != ERR_OK)
|
||||
return LwipErrToStatus(err);
|
||||
if (!IP_IS_V4(&resolved))
|
||||
if ((family == SYS_AF_INET && !IP_IS_V4(&resolved)) ||
|
||||
(family == SYS_AF_INET6 && !IP_IS_V6(&resolved)))
|
||||
return STATUS_NOT_FOUND;
|
||||
|
||||
memset(addressOut, 0, sizeof(*addressOut));
|
||||
addressOut->Family = SYS_AF_INET;
|
||||
addressOut->Port = 0;
|
||||
addressOut->Address[0] = (uint8_t)ip4_addr1(ip_2_ip4(&resolved));
|
||||
addressOut->Address[1] = (uint8_t)ip4_addr2(ip_2_ip4(&resolved));
|
||||
addressOut->Address[2] = (uint8_t)ip4_addr3(ip_2_ip4(&resolved));
|
||||
addressOut->Address[3] = (uint8_t)ip4_addr4(ip_2_ip4(&resolved));
|
||||
LwipSocketFillAddress(addressOut, &resolved, 0);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_disable)
|
||||
#pragma intrinsic(_enable)
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <lwip/opt.h>
|
||||
#include <lwip/sys.h>
|
||||
#include <lwip/err.h>
|
||||
@@ -41,7 +40,6 @@ struct sys_mbox {
|
||||
uint32_t Valid;
|
||||
};
|
||||
|
||||
|
||||
static uint64_t (*gLwipQueryTicksUs)(void) = 0;
|
||||
static uint32_t gRandState = 0x4b4e4554u; // 'KNET'
|
||||
|
||||
@@ -70,7 +68,7 @@ u32_t sys_jiffies(void) {
|
||||
return sys_now();
|
||||
}
|
||||
|
||||
// lwIP's core/sys.c provides sys_msleep() when it is not a macro. Do not
|
||||
// lwIP's core/sys.c provides sys_msleep() when it is not a macro. Do not
|
||||
// define it here as well, or MSVC links both sys.obj and lwip_sys_arch.obj
|
||||
// with a duplicate sys_msleep symbol.
|
||||
|
||||
@@ -360,15 +358,10 @@ sys_thread_t sys_thread_new(const char* name, lwip_thread_fn thread, void* arg,
|
||||
}
|
||||
|
||||
sys_prot_t sys_arch_protect(void) {
|
||||
uintptr_t flags = __readeflags();
|
||||
_disable();
|
||||
return (sys_prot_t)flags;
|
||||
return (sys_prot_t)HalDisableInterrupts();
|
||||
}
|
||||
|
||||
// Implements the sys arch unprotect operation.
|
||||
// Restores the interrupt state saved by sys_arch_protect.
|
||||
void sys_arch_unprotect(sys_prot_t pval) {
|
||||
if (((uintptr_t)pval) & 0x200u)
|
||||
_enable();
|
||||
else
|
||||
_disable();
|
||||
HalRestoreInterrupts((uintptr_t)pval);
|
||||
}
|
||||
@@ -63,10 +63,6 @@ static void Rtl8139Write32(RTL8139_DEVICE* r, uint32_t reg, uint32_t value) {
|
||||
else __outdword((unsigned short)(r->IoBase + reg), value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Implements the reset operation.
|
||||
static KSTATUS Rtl8139Reset(RTL8139_DEVICE* r) {
|
||||
Rtl8139Write8(r, RTL8139_REG_CMD, RTL8139_CMD_RST);
|
||||
@@ -93,7 +89,7 @@ static KSTATUS Rtl8139AllocateDma(RTL8139_DEVICE* r) {
|
||||
if (!r->RxPhys || !r->TxPhys)
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
// RTL8139 has 32-bit DMA address registers. This is fine for typical QEMU
|
||||
// RTL8139 has 32-bit DMA address registers. This is fine for typical QEMU
|
||||
// and low-memory hobby OS configs, but make failure explicit.
|
||||
if ((r->RxPhys + RTL8139_RX_ALLOC_SIZE - 1) > 0xFFFFFFFFULL ||
|
||||
(r->TxPhys + RTL8139_TX_SLOTS * RTL8139_TX_BUF_SIZE - 1) > 0xFFFFFFFFULL) {
|
||||
|
||||
@@ -49,9 +49,7 @@ static inline uint64_t cmd_tbl_phys(AHCI_DRIVE *drv, int slot) {
|
||||
+ (uint64_t)slot * PORT_CMDTBL_SIZE;
|
||||
}
|
||||
|
||||
// Port engine start / stop (polling safe).
|
||||
|
||||
|
||||
// Port engine start / stop (polling safe).
|
||||
|
||||
// Port memory initialisation.
|
||||
|
||||
@@ -148,7 +146,7 @@ static KSTATUS port_issue_cmd_poll(AHCI_DRIVE *drv) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Interrupt bottom half. The ISR owns hardware acknowledgement and status
|
||||
// Interrupt bottom half. The ISR owns hardware acknowledgement and status
|
||||
// capture; this DPC wakes the worker thread after the hard IRQ has unwound.
|
||||
|
||||
static void AhciCompletionDpc(KDPC *dpc, void *context,
|
||||
@@ -182,9 +180,9 @@ static void AhciIsr(void) {
|
||||
uint32_t portBit = (1u << drv->portNo);
|
||||
if (!(pending & portBit)) continue;
|
||||
|
||||
// Capture and clear port IS before queueing the bottom half. The
|
||||
// Capture and clear port IS before queueing the bottom half. The
|
||||
// waiter reads lastIs to check for errors without racing the next
|
||||
// command. OR in case hardware reports multiple causes before the
|
||||
// command. OR in case hardware reports multiple causes before the
|
||||
// DPC releases the worker.
|
||||
uint32_t portIs = drv->port->is;
|
||||
drv->port->is = portIs; // W1C - write back to clear
|
||||
@@ -383,7 +381,6 @@ static KSTATUS do_write(AHCI_DRIVE *drv, uint64_t lba,
|
||||
|
||||
// Async IRP dispatch.
|
||||
|
||||
|
||||
static KSTATUS AhciGetIrpReadWrite(IRP* irp, bool* outWrite, uint64_t* outLba,
|
||||
uint32_t* outSectors, void** outBuffer) {
|
||||
if (!irp || !outWrite || !outLba || !outSectors || !outBuffer)
|
||||
@@ -407,7 +404,7 @@ static KSTATUS AhciGetIrpReadWrite(IRP* irp, bool* outWrite, uint64_t* outLba,
|
||||
|
||||
// AhciDispatchReadWrite - DEVICE_OBJECT dispatch entry for disk IRPs.
|
||||
// Accepted IRPs are queued to the per-drive worker and completed later with
|
||||
// IoCompleteRequest(). Rejected IRPs are returned synchronously and are not
|
||||
// IoCompleteRequest(). Rejected IRPs are returned synchronously and are not
|
||||
// completed.
|
||||
static KSTATUS AhciDispatchReadWrite(DEVICE_OBJECT *device, IRP *irp) {
|
||||
if (!device || !irp) return STATUS_INVALID_PARAMETER;
|
||||
@@ -592,26 +589,23 @@ static void AhciWorker(uintptr_t arg) {
|
||||
|
||||
// AHCI controller initialisation.
|
||||
|
||||
|
||||
|
||||
// Before memory init: only check physical link. Signature is not yet valid.
|
||||
|
||||
// After COMRESET + FIS receive: check signature for SATA disk.
|
||||
|
||||
|
||||
// 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.
|
||||
|
||||
// PORT_IE bits we care about:
|
||||
// DHRS (bit 0) - D2H Register FIS received - normal completion
|
||||
// PSS (bit 1) - PIO Setup FIS received
|
||||
// DSS (bit 2) - DMA Setup FIS received
|
||||
// SDBS (bit 3) - Set Device Bits FIS received
|
||||
// TFES (bit 30) - Task File Error Status - error
|
||||
// HBFS (bit 29) - Host Bus Fatal error
|
||||
// HBDS (bit 28) - Host Bus Data error
|
||||
// IFS (bit 27) - Interface Fatal error.
|
||||
// DHRS (bit 0) - D2H Register FIS received - normal completion
|
||||
// PSS (bit 1) - PIO Setup FIS received
|
||||
// DSS (bit 2) - DMA Setup FIS received
|
||||
// SDBS (bit 3) - Set Device Bits FIS received
|
||||
// TFES (bit 30) - Task File Error Status - error
|
||||
// HBFS (bit 29) - Host Bus Fatal error
|
||||
// HBDS (bit 28) - Host Bus Data error
|
||||
// IFS (bit 27) - Interface Fatal error.
|
||||
#define PORT_IE_NORMAL (0x0000000Fu)
|
||||
#define PORT_IE_ERRORS (PORT_IS_TFES | PORT_IS_HBFS | PORT_IS_HBDS | PORT_IS_IFS)
|
||||
#define PORT_IE_ALL (PORT_IE_NORMAL | PORT_IE_ERRORS)
|
||||
|
||||
@@ -221,7 +221,7 @@ typedef volatile struct {
|
||||
|
||||
// Constants.
|
||||
|
||||
// PCI/AHCI binding. PCI enumeration, BAR decoding, interrupt line
|
||||
// PCI/AHCI binding. PCI enumeration, BAR decoding, interrupt line
|
||||
// discovery, and command-register policy are owned by pci.sys.
|
||||
#define AHCI_PCI_BAR_ABAR 5u
|
||||
#define AHCI_CONTROLLER_MAGIC 0x41484349u // 'AHCI'
|
||||
|
||||
@@ -28,7 +28,6 @@ KSTATUS ahci_map_mmio(uint64_t physBase, uint64_t length) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the port stop operation.
|
||||
void port_stop(HBA_PORT *port) {
|
||||
// Clear ST (bit 0).
|
||||
@@ -45,7 +44,6 @@ void port_stop(HBA_PORT *port) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Implements the port start operation.
|
||||
void port_start(HBA_PORT *port) {
|
||||
// Wait until CR is clear before setting ST.
|
||||
@@ -56,7 +54,6 @@ void port_start(HBA_PORT *port) {
|
||||
port->cmd |= PORT_CMD_ST;
|
||||
}
|
||||
|
||||
|
||||
// Implements the hba reset operation.
|
||||
KSTATUS hba_reset(HBA_MEM *hba) {
|
||||
// BIOS/OS handoff (spec §10.6.3).
|
||||
@@ -91,7 +88,6 @@ KSTATUS hba_reset(HBA_MEM *hba) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// Implements the port link up operation.
|
||||
bool port_link_up(HBA_PORT *port) {
|
||||
uint32_t det = port->ssts & 0x0F;
|
||||
@@ -99,20 +95,17 @@ bool port_link_up(HBA_PORT *port) {
|
||||
return (det == PORT_DET_PRESENT && ipm == PORT_IPM_ACTIVE);
|
||||
}
|
||||
|
||||
|
||||
// Implements the port is sata disk operation.
|
||||
bool port_is_sata_disk(HBA_PORT *port) {
|
||||
return port->sig == SATA_SIG_ATA;
|
||||
}
|
||||
|
||||
|
||||
// Implements the ahci delay ms operation.
|
||||
void ahci_delay_ms(uint32_t ms) {
|
||||
for (uint32_t i = 0; i < ms * 1000; i++)
|
||||
__outbyte(0x80, 0);
|
||||
}
|
||||
|
||||
|
||||
// Implements the port comreset operation.
|
||||
KSTATUS port_comreset(AHCI_DRIVE *drv) {
|
||||
HBA_PORT *port = drv->port;
|
||||
|
||||
Reference in new issue
Block a user