From 6523c4ae872b4780886f0fad7a694ba70647ac58 Mon Sep 17 00:00:00 2001 From: y4my4my4m <8145020+y4my4my4m@users.noreply.github.com> Date: Thu, 23 Jul 2026 22:13:04 +0900 Subject: [PATCH 1/5] Add USB support with mass storage and HID drivers This commit introduces a comprehensive USB stack, including support for USB mass storage devices and Human Interface Devices (HID) such as keyboards and mice. Key changes include: - New files for USB core functionality, xHCI host controller, and device enumeration. - Implementation of bulk-only transport for mass storage devices. - Integration of HID protocols for keyboard and mouse input. - Updates to existing block device handling to accommodate USB devices. This enhancement significantly expands the system's capabilities for handling USB peripherals. --- src/Kernel/BlkDev/DiskAddDev.ZC | 18 +- src/Kernel/BlkDev/DiskBlk.ZC | 14 + src/Kernel/BlkDev/DiskBlkDev.ZC | 7 + src/Kernel/BlkDev/DiskDrive.ZC | 4 +- src/Kernel/KGlobals.ZC | 3 + src/Kernel/KernelA.HH | 3 +- src/Kernel/KernelC.HH | 2 + src/System/MakeSystem.ZC | 1 + src/System/Usb/MakeUsb.ZC | 155 +++++++ src/System/Usb/Usb.ZC | 270 +++++++++++++ src/System/Usb/UsbDefs.ZC | 306 ++++++++++++++ src/System/Usb/UsbHid.ZC | 251 ++++++++++++ src/System/Usb/UsbMsd.ZC | 382 +++++++++++++++++ src/System/Usb/Xhci.ZC | 697 ++++++++++++++++++++++++++++++++ 14 files changed, 2108 insertions(+), 5 deletions(-) create mode 100644 src/System/Usb/MakeUsb.ZC create mode 100644 src/System/Usb/Usb.ZC create mode 100644 src/System/Usb/UsbDefs.ZC create mode 100644 src/System/Usb/UsbHid.ZC create mode 100644 src/System/Usb/UsbMsd.ZC create mode 100644 src/System/Usb/Xhci.ZC diff --git a/src/Kernel/BlkDev/DiskAddDev.ZC b/src/Kernel/BlkDev/DiskAddDev.ZC index 3b9531309..b4b2a99e2 100755 --- a/src/Kernel/BlkDev/DiskAddDev.ZC +++ b/src/Kernel/BlkDev/DiskAddDev.ZC @@ -30,6 +30,17 @@ U0 BlkDevLockFwdingSet(CBlkDev *bd) } } +Bool BlkDevAddBlksRead(CBlkDev *bd, U8 *buf, I64 blk, I64 count) +{//Partition-scan read during $LK,"BlkDevAdd",A="MN:BlkDevAdd"$(), before drive is validated. + if (bd->type == BDT_USB) + { + if (fp_usb_msd_blks_read) + return (*fp_usb_msd_blks_read)(bd, buf, blk, count); + return FALSE; + } + return AHCIAtaBlksRead(bd, buf, blk, count); +} + I64 BlkDevAdd(CBlkDev *bd, I64 prt_num=I64_MIN, Bool whole_drive, Bool make_free) {//It will mount just one partition of prt_num>=0. //When repartitioing whole drive, whole_drive=TRUE. @@ -68,6 +79,7 @@ I64 BlkDevAdd(CBlkDev *bd, I64 prt_num=I64_MIN, Bool whole_drive, Bool make_free break; case BDT_ATA: + case BDT_USB: drive->drive_signature = DRIVE_SIGNATURE_VAL; //Temporarily validate if (!BlkDevInit(bd)) drive->drive_signature = 0; //Revoke validation @@ -83,14 +95,14 @@ I64 BlkDevAdd(CBlkDev *bd, I64 prt_num=I64_MIN, Bool whole_drive, Bool make_free drive->size = bd->max_blk + 1 - bd->drv_offset; //The following read is a test read. //if it hangs, the drive is not supported. - AHCIAtaBlksRead(bd, &mbr, 0, 1); + BlkDevAddBlksRead(bd, &mbr, 0, 1); break; } offset = 0; ext_base = INVALID_CLUS; while (prt_num < 0 || num <= prt_num) { - AHCIAtaBlksRead(bd, &mbr, offset, 1); + BlkDevAddBlksRead(bd, &mbr, offset, 1); if (mbr.signature != 0xAA55) break; j = -1; @@ -121,7 +133,7 @@ I64 BlkDevAdd(CBlkDev *bd, I64 prt_num=I64_MIN, Bool whole_drive, Bool make_free case MBR_PT_FAT32d: case MBR_PT_FAT32e: case MBR_PT_FAT32f: - AHCIAtaBlksRead(bd, &br, drive->drv_offset, 1); + BlkDevAddBlksRead(bd, &br, drive->drv_offset, 1); drive->drive_signature = DRIVE_SIGNATURE_VAL; drive->prt_num = num; diff --git a/src/Kernel/BlkDev/DiskBlk.ZC b/src/Kernel/BlkDev/DiskBlk.ZC index 5a6d2f6c4..0bd018a39 100755 --- a/src/Kernel/BlkDev/DiskBlk.ZC +++ b/src/Kernel/BlkDev/DiskBlk.ZC @@ -69,6 +69,13 @@ Bool BlkRead(CDrive *drive, U8 *buf, I64 blk, I64 count) case BDT_ATAPI: res = AHCIAtapiRBlks(drive, buf, blk, count); break; + + case BDT_USB: + if (fp_usb_msd_blks_read) + res = (*fp_usb_msd_blks_read)(bd, buf, blk, count); + else + res = FALSE; + break; } bd->last_time = tS; if (bd->flags & BDF_READ_CACHE) @@ -117,6 +124,13 @@ Bool BlkWrite(CDrive *drive, U8 *buf, I64 blk, I64 count) case BDT_ATAPI: res = AHCIAtaWBlks(drive, buf, blk, count); break; + + case BDT_USB: + if (fp_usb_msd_blks_write) + res = (*fp_usb_msd_blks_write)(bd, buf, blk, count); + else + res = FALSE; + break; } bd->last_time = tS; if (bd->flags & BDF_READ_CACHE) diff --git a/src/Kernel/BlkDev/DiskBlkDev.ZC b/src/Kernel/BlkDev/DiskBlkDev.ZC index 85448bc4a..ca3c13078 100755 --- a/src/Kernel/BlkDev/DiskBlkDev.ZC +++ b/src/Kernel/BlkDev/DiskBlkDev.ZC @@ -125,6 +125,13 @@ Bool BlkDevInit(CBlkDev *bd) if (res = AHCIAtaInit(bd)) drive->size = bd->max_blk + 1; break; + + case BDT_USB: +//USB mass storage driver fills in max_blk before BlkDevAdd(). + bd->max_reads = 64; + bd->max_writes = 64; + res = TRUE; + break; } if (res && bd->flags & BDF_READ_CACHE) DiskCacheInvalidate(drive); diff --git a/src/Kernel/BlkDev/DiskDrive.ZC b/src/Kernel/BlkDev/DiskDrive.ZC index 13508206e..4975522da 100755 --- a/src/Kernel/BlkDev/DiskDrive.ZC +++ b/src/Kernel/BlkDev/DiskDrive.ZC @@ -202,8 +202,10 @@ I64 Letter2BlkDevType(U8 drv_let) return BDT_ISO_FILE_READ; if ('Q' <= drv_let <= 'S') return BDT_ISO_FILE_WRITE; - if ('T' <= drv_let <= 'Z') + if ('T' <= drv_let <= 'U') return BDT_ATAPI; + if ('V' <= drv_let <= 'Z') + return BDT_USB; return BDT_NULL; } diff --git a/src/Kernel/KGlobals.ZC b/src/Kernel/KGlobals.ZC index 8499b3c2c..e48ebd36a 100755 --- a/src/Kernel/KGlobals.ZC +++ b/src/Kernel/KGlobals.ZC @@ -33,6 +33,9 @@ CMouseHardStateGlobals mouse_hard, mouse_hard_last; CScreenCastGlobals screencast; CTextGlobals text; +Bool (*fp_usb_msd_blks_read )(CBlkDev *bd, U8 *buf, I64 blk, I64 count); //Set by USB mass storage driver. +Bool (*fp_usb_msd_blks_write)(CBlkDev *bd, U8 *buf, I64 blk, I64 count); + U8 *(*fp_getstr2)(I64 flags=0); U0 (*fp_update_ctrls)(CTask *task); CDoc *(*fp_doc_put)(CTask *task=NULL); diff --git a/src/Kernel/KernelA.HH b/src/Kernel/KernelA.HH index d9d731b23..0520d0689 100755 --- a/src/Kernel/KernelA.HH +++ b/src/Kernel/KernelA.HH @@ -3319,7 +3319,8 @@ public class CDirEntry #define BDT_ISO_FILE_READ 3 #define BDT_ISO_FILE_WRITE 4 #define BDT_ATAPI 5 -#define BDT_TYPES_NUM 6 +#define BDT_USB 6 +#define BDT_TYPES_NUM 7 #define BDf_REMOVABLE 0 #define BDf_INITIALIZED 1 diff --git a/src/Kernel/KernelC.HH b/src/Kernel/KernelC.HH index 18c28999b..75b6bee2c 100755 --- a/src/Kernel/KernelC.HH +++ b/src/Kernel/KernelC.HH @@ -303,6 +303,8 @@ public extern CBlkDev *BlkDevCheck(CBlkDev *bd, Bool except=TRUE); public extern U0 BlkDevDel( CBlkDev *bd); public extern Bool BlkDevLock( CBlkDev *bd); public extern CBlkDev *BlkDevNextFreeSlot(U8 first_drive_let, I64 type); +public extern Bool (*fp_usb_msd_blks_read )(CBlkDev *bd, U8 *buf, I64 blk, I64 count); +public extern Bool (*fp_usb_msd_blks_write)(CBlkDev *bd, U8 *buf, I64 blk, I64 count); public extern Bool BlkDevUnlock(CBlkDev *bd, Bool reset=FALSE); public extern U0 BlkDevsRelease(); public extern U0 BlkDevRep(); diff --git a/src/System/MakeSystem.ZC b/src/System/MakeSystem.ZC index c62ccd7d6..70a5b2de9 100755 --- a/src/System/MakeSystem.ZC +++ b/src/System/MakeSystem.ZC @@ -25,4 +25,5 @@ LBts(&sys_run_level, RLf_DOC); #include "Define" #include "WallPaper" #include "Mouse" +#include "Usb/MakeUsb" Cd("..");; diff --git a/src/System/Usb/MakeUsb.ZC b/src/System/Usb/MakeUsb.ZC new file mode 100644 index 000000000..f17eaa43e --- /dev/null +++ b/src/System/Usb/MakeUsb.ZC @@ -0,0 +1,155 @@ +//USB stack build + boot task. +Cd(__DIR__);; +#include "UsbDefs" +#include "Xhci" +#include "Usb" +#include "UsbHid" +#include "UsbMsd" +Cd("..");; + +U0 UsbDrvBind(CUsbDev *dev) +{//Attach class drivers to a freshly configured device. + CUsbIf *uif; + I64 i; + + for (i = 0; i < dev->if_count; i++) + { + uif = &dev->ifs[i]; + if (!uif->valid) + goto next_if; + if (uif->if_class == USB_CLASS_HID) + { + if (uif->if_subclass == USB_HID_SUBCLASS_BOOT && + (uif->if_proto == USB_HID_PROTO_KBD || uif->if_proto == USB_HID_PROTO_MOUSE)) + UsbHidAttach(dev, uif); + else + SysLog("USB: port %d: non-boot HID interface ignored.\n", dev->port); + } + else if (uif->if_class == USB_CLASS_MSD) + { + if (uif->if_subclass == USB_MSD_SUBCLASS_SCSI) + UsbMsdAttach(dev, uif); + else + SysLog("USB: port %d: MSD subclass 0x%02X unsupported.\n", + dev->port, uif->if_subclass); + } + else if (uif->if_class == USB_CLASS_HUB) + SysLog("USB: port %d: hubs not yet supported.\n", dev->port); +next_if:; + } +} + +U0 UsbDrvUnbind(CUsbDev *dev) +{ + UsbHidDetach(dev); + UsbMsdDetach(dev); +} + +U0 UsbPortScan() +{//Check all root ports for connects/disconnects. USB task only. + CUsbDev *dev, *ndev; + I64 port, sc; + + for (port = 0; port < usb_xhci.max_ports; port++) + { + sc = XhciPortSC(port); + if (sc & XHCI_PORTSC_CHANGE_BITS) + XhciPortSCWrite(port, XHCI_PORTSC_PP | (sc & XHCI_PORTSC_CHANGE_BITS)); + dev = UsbDevByPort(port); + if (sc & XHCI_PORTSC_CCS) + { + if (!dev) + { + if (XhciPortReset(port)) + { + ndev = UsbDevEnum(port); + if (ndev) + UsbDrvBind(ndev); + } + } + } + else if (dev) + { + UsbDrvUnbind(dev); + UsbDevDetachCore(dev); + SysLog("USB: port %d: device removed.\n", port); + } + } +} + +public U0 UsbRep() +{//Report attached USB devices. + CUsbDev *dev; + CUsbIf *uif; + I64 i, j; + + if (!usb_xhci.present) + { + "No xHCI controller.\n"; + return; + } + "xHCI: %d ports, %d slots.\n", usb_xhci.max_ports, usb_xhci.max_slots; + for (i = 0; i < USB_DEVS_MAX; i++) + { + dev = &usb_devs[i]; + if (dev->present) + { + "Port %2d: %04X:%04X \"%s\"", dev->port, dev->vendor_id, + dev->product_id, dev->desc_name; + for (j = 0; j < dev->if_count; j++) + { + uif = &dev->ifs[j]; + if (uif->if_class == USB_CLASS_HID && uif->if_proto == USB_HID_PROTO_KBD) + " [keyboard]"; + else if (uif->if_class == USB_CLASS_HID && uif->if_proto == USB_HID_PROTO_MOUSE) + " [mouse]"; + else if (uif->if_class == USB_CLASS_MSD) + " [storage]"; + else if (uif->if_class == USB_CLASS_HUB) + " [hub]"; + else + " [class %d]", uif->if_class; + } + "\n"; + } + } + for (i = 0; i < USB_MSDS_MAX; i++) + if (usb_msds[i].valid && usb_msds[i].ready && usb_msds[i].bd) + "Storage %d: %d MB, drives from %C:.\n", i, + ((usb_msds[i].max_lba + 1) * BLK_SIZE) / 0x100000, + usb_msds[i].first_drive_let; +} + +U0 UsbTaskMain(I64 dummy=0) +{ + I64 tick = 0; + + no_warn dummy; + if (!XhciInit) + return; + Sleep(100); //let port connect status settle after reset + UsbPortScan; + while (TRUE) + { + XhciEventPoll; + UsbHidTick; + tick++; + if (usb_xhci.port_change || tick & 0x7F == 0) + { + usb_xhci.port_change = FALSE; + UsbPortScan; + } + Sleep(2); + } +} + +U0 UsbInit() +{ + MemSet(&usb_xhci, 0, sizeof(CXhciGlobals)); + MemSet(usb_devs, 0, sizeof(CUsbDev) * USB_DEVS_MAX); + MemSet(usb_hids, 0, sizeof(CUsbHid) * USB_HIDS_MAX); + MemSet(usb_msds, 0, sizeof(CUsbMsd) * USB_MSDS_MAX); + usb_task = Spawn(&UsbTaskMain, NULL, "USB"); +} + +UsbInit; diff --git a/src/System/Usb/Usb.ZC b/src/System/Usb/Usb.ZC new file mode 100644 index 000000000..f1e96bfaa --- /dev/null +++ b/src/System/Usb/Usb.ZC @@ -0,0 +1,270 @@ +//USB core: device enumeration over xHCI. + +I64 UsbCtrl(CUsbDev *dev, I64 rt, I64 rq, I64 val, I64 idx, U8 *buf, I64 len) +{//Control request with one retry on failure. + I64 code = XhciCtrlXfer(dev, rt, rq, val, idx, buf, len); + + if (code != XHCI_CC_SUCCESS && code != XHCI_CC_SHORT_PKT) + { + Sleep(10); + code = XhciCtrlXfer(dev, rt, rq, val, idx, buf, len); + } + + return code; +} + +I64 UsbGetDesc(CUsbDev *dev, I64 type, I64 idx, U8 *buf, I64 len, I64 lang=0) +{ + return UsbCtrl(dev, USB_RT_DEV_TO_HOST, USB_RQ_GET_DESCRIPTOR, + (type << 8) | idx, lang, buf, len); +} + +I64 UsbClearHalt(CUsbDev *dev, CUsbEP *ep) +{//Recover halted endpoint: xHCI reset + USB CLEAR_FEATURE(HALT). + XhciResetEP(dev, ep); + + return UsbCtrl(dev, USB_RT_HOST_TO_DEV | USB_RT_RECIP_EP, USB_RQ_CLEAR_FEATURE, + USB_FEAT_EP_HALT, ep->addr, NULL, 0); +} + +U0 UsbStrGet(CUsbDev *dev, I64 idx, U8 *dst, I64 dst_len) +{//Fetch string descriptor as ASCII. + U8 *buf; + I64 i, n, code; + + *dst = 0; + if (!idx) + return; + buf = CAlloc(256, sys_task->code_heap); + code = UsbGetDesc(dev, USB_DESC_STR, idx, buf, 255, 0x0409); + if (code == XHCI_CC_SUCCESS || code == XHCI_CC_SHORT_PKT) + { + n = buf[0]; + if (n > 2) + { + n = (n - 2) / 2; + if (n > dst_len - 1) + n = dst_len - 1; + for (i = 0; i < n; i++) + dst[i] = buf[2 + i * 2]; + dst[n] = 0; + } + } + Free(buf); +} + +CUsbDev *UsbDevAlloc() +{ + I64 i; + + for (i = 0; i < USB_DEVS_MAX; i++) + if (!usb_devs[i].present) + return &usb_devs[i]; + + return NULL; +} + +U0 UsbParseConfig(CUsbDev *dev, U8 *buf, I64 len) +{//Walk config descriptor, record interfaces + INT/BULK endpoints. + CUsbIfDesc *ifd; + CUsbEPDesc *epd; + CUsbIf *cur_if = NULL; + CUsbEP *ep; + I64 off = 0, dlen, dtype, ttype; + + dev->if_count = 0; + dev->ep_count = 0; + while (off + 2 <= len) + { + dlen = buf[off]; + dtype = buf[off + 1]; + if (!dlen) + break; + if (dtype == USB_DESC_IF) + { + ifd = &buf[off]; + if (ifd->alt == 0 && dev->if_count < USB_IFS_MAX) + { + cur_if = &dev->ifs[dev->if_count]; + MemSet(cur_if, 0, sizeof(CUsbIf)); + cur_if->valid = TRUE; + cur_if->if_num = ifd->if_num; + cur_if->if_class = ifd->if_class; + cur_if->if_subclass = ifd->if_subclass; + cur_if->if_proto = ifd->if_proto; + dev->if_count++; + } + else + cur_if = NULL; //alternate setting; skip its endpoints + } + else if (dtype == USB_DESC_EP) + { + if (cur_if && dev->ep_count < USB_EPS_MAX && cur_if->ep_count < 4) + { + epd = &buf[off]; + ttype = epd->attr & 3; + if (ttype == USB_EP_TYPE_BULK || ttype == USB_EP_TYPE_INT) + { + ep = &dev->eps[dev->ep_count]; +//Clear all fields but keep any prior ring allocation (ring is first member). + MemSet(&ep->dci, 0, sizeof(CUsbEP) - offset(CUsbEP.dci)); + ep->addr = epd->addr; + ep->attr = epd->attr; + ep->max_packet = epd->max_packet & 0x7FF; + ep->interval = epd->interval; + ep->dci = (epd->addr & 0xF) << 1; + if (epd->addr & 0x80) + ep->dci |= 1; + ep->valid = TRUE; + ep->auto_cb = NULL; + cur_if->ep_idxs[cur_if->ep_count] = dev->ep_count; + cur_if->ep_count++; + dev->ep_count++; + } + } + } + off += dlen; + } +} + +U0 UsbDevDetachCore(CUsbDev *dev) +{//Release xHCI slot; class driver cleanup handled by caller. + I64 i; + + dev->present = FALSE; + dev->configured = FALSE; + if (dev->slot) + { + XhciDisableSlot(dev->slot); + usb_xhci.dcbaa[dev->slot] = 0; + } + dev->slot = 0; + dev->ep0.valid = FALSE; + for (i = 0; i < USB_EPS_MAX; i++) + { + dev->eps[i].valid = FALSE; + dev->eps[i].auto_cb = NULL; + } + dev->drv_data = NULL; +} + +CUsbDev *UsbDevEnum(I64 port) +{//Enumerate device on root port (already reset + enabled). USB task only. + CUsbDev *dev; + CUsbDevDesc *dd; + CUsbConfigDesc *cd; + U8 *buf; + I64 slot, code, sc, total, mps0; + + sc = XhciPortSC(port); + slot = XhciEnableSlot; + if (!slot) + { + "USB: port %d: Enable Slot failed.\n", port; + return NULL; + } + dev = UsbDevAlloc; + if (!dev) + { + XhciDisableSlot(slot); + return NULL; + } + dev->slot = slot; + dev->port = port; + dev->speed = (sc >> 10) & 0xF; + dev->present = TRUE; + dev->configured = FALSE; + dev->drv_data = NULL; + dev->desc_name[0] = 0; + + code = XhciAddressDev(dev); + if (code != XHCI_CC_SUCCESS) + { + "USB: port %d: Address Device failed (%d).\n", port, code; + UsbDevDetachCore(dev); + return NULL; + } + Sleep(5); + + buf = CAlloc(512, sys_task->code_heap); + dd = buf; + +//First 8 bytes to learn EP0 max packet size. + code = UsbGetDesc(dev, USB_DESC_DEV, 0, buf, 8); + if (code != XHCI_CC_SUCCESS && code != XHCI_CC_SHORT_PKT) + { + "USB: port %d: GET_DESCRIPTOR(8) failed (%d).\n", port, code; + Free(buf); + UsbDevDetachCore(dev); + return NULL; + } + mps0 = dd->max_packet0; + if (dev->speed == USB_SPEED_SUPER) + mps0 = 1 << mps0; //SS stores exponent + if (mps0 && mps0 != dev->ep0.max_packet) + XhciEvalCtxMPS0(dev, mps0); + + code = UsbGetDesc(dev, USB_DESC_DEV, 0, buf, 18); + if (code != XHCI_CC_SUCCESS && code != XHCI_CC_SHORT_PKT) + { + "USB: port %d: GET_DESCRIPTOR failed (%d).\n", port, code; + Free(buf); + UsbDevDetachCore(dev); + return NULL; + } + dev->vendor_id = dd->vendor_id; + dev->product_id = dd->product_id; + dev->dev_class = dd->dev_class; + dev->dev_subclass = dd->dev_subclass; + dev->dev_proto = dd->dev_proto; + UsbStrGet(dev, dd->i_product, dev->desc_name, 64); + +//Config descriptor: header first for total length, then full. + code = UsbGetDesc(dev, USB_DESC_CONFIG, 0, buf, 9); + if (code != XHCI_CC_SUCCESS && code != XHCI_CC_SHORT_PKT) + { + "USB: port %d: GET_CONFIG failed (%d).\n", port, code; + Free(buf); + UsbDevDetachCore(dev); + return NULL; + } + cd = buf; + total = cd->total_len; + if (total > 512) + total = 512; + code = UsbGetDesc(dev, USB_DESC_CONFIG, 0, buf, total); + if (code != XHCI_CC_SUCCESS && code != XHCI_CC_SHORT_PKT) + { + Free(buf); + UsbDevDetachCore(dev); + return NULL; + } + dev->config_val = cd->config_val; + UsbParseConfig(dev, buf, total); + Free(buf); + + if (dev->ep_count) + { + code = XhciConfigureEPs(dev); + if (code != XHCI_CC_SUCCESS) + { + "USB: port %d: Configure Endpoint failed (%d).\n", port, code; + UsbDevDetachCore(dev); + return NULL; + } + } + code = UsbCtrl(dev, USB_RT_HOST_TO_DEV, USB_RQ_SET_CONFIG, dev->config_val, 0, NULL, 0); + if (code != XHCI_CC_SUCCESS) + { + "USB: port %d: SET_CONFIGURATION failed (%d).\n", port, code; + UsbDevDetachCore(dev); + return NULL; + } + dev->configured = TRUE; + + SysLog("USB: port %d: %04X:%04X class %d \"%s\" (%d ifs, %d eps)\n", + port, dev->vendor_id, dev->product_id, dev->dev_class, + dev->desc_name, dev->if_count, dev->ep_count); + + return dev; +} diff --git a/src/System/Usb/UsbDefs.ZC b/src/System/Usb/UsbDefs.ZC new file mode 100644 index 000000000..9ce582ee9 --- /dev/null +++ b/src/System/Usb/UsbDefs.ZC @@ -0,0 +1,306 @@ +//USB + xHCI definitions. +//ZealOS USB stack: xHCI host controller, HID boot keyboard/mouse, mass storage. + +#help_index "USB" + +#define USB_DEVS_MAX 16 +#define USB_EPS_MAX 8 +#define USB_IFS_MAX 6 + +//USB descriptor types +#define USB_DESC_DEV 1 +#define USB_DESC_CONFIG 2 +#define USB_DESC_STR 3 +#define USB_DESC_IF 4 +#define USB_DESC_EP 5 +#define USB_DESC_HID 0x21 + +//USB standard requests +#define USB_RQ_GET_STATUS 0 +#define USB_RQ_CLEAR_FEATURE 1 +#define USB_RQ_SET_FEATURE 3 +#define USB_RQ_SET_ADDRESS 5 +#define USB_RQ_GET_DESCRIPTOR 6 +#define USB_RQ_SET_DESCRIPTOR 7 +#define USB_RQ_GET_CONFIG 8 +#define USB_RQ_SET_CONFIG 9 +#define USB_RQ_GET_INTERFACE 10 +#define USB_RQ_SET_INTERFACE 11 + +//bmRequestType bits +#define USB_RT_DEV_TO_HOST 0x80 +#define USB_RT_HOST_TO_DEV 0x00 +#define USB_RT_STD 0x00 +#define USB_RT_CLASS 0x20 +#define USB_RT_VENDOR 0x40 +#define USB_RT_RECIP_DEV 0x00 +#define USB_RT_RECIP_IF 0x01 +#define USB_RT_RECIP_EP 0x02 + +//USB feature selectors +#define USB_FEAT_EP_HALT 0 + +//USB classes +#define USB_CLASS_HID 3 +#define USB_CLASS_MSD 8 +#define USB_CLASS_HUB 9 + +//HID +#define USB_HID_SUBCLASS_BOOT 1 +#define USB_HID_PROTO_KBD 1 +#define USB_HID_PROTO_MOUSE 2 +#define USB_HID_RQ_GET_REPORT 1 +#define USB_HID_RQ_SET_IDLE 10 +#define USB_HID_RQ_SET_PROTOCOL 11 + +//MSD +#define USB_MSD_SUBCLASS_SCSI 6 +#define USB_MSD_PROTO_BOT 0x50 +#define USB_MSD_RQ_RESET 0xFF +#define USB_MSD_RQ_GET_MAX_LUN 0xFE + +//Device speeds, xHCI PORTSC encoding +#define USB_SPEED_FULL 1 +#define USB_SPEED_LOW 2 +#define USB_SPEED_HIGH 3 +#define USB_SPEED_SUPER 4 + +//Endpoint attribute transfer types +#define USB_EP_TYPE_CTRL 0 +#define USB_EP_TYPE_ISOCH 1 +#define USB_EP_TYPE_BULK 2 +#define USB_EP_TYPE_INT 3 + +class CUsbDevDesc +{//18 bytes. HolyC classes are packed. + U8 len, type; + U16 usb_ver; + U8 dev_class, dev_subclass, dev_proto, max_packet0; + U16 vendor_id, product_id, dev_ver; + U8 i_manufacturer, i_product, i_serial, num_configs; +}; + +class CUsbConfigDesc +{//9 bytes + U8 len, type; + U16 total_len; + U8 num_ifs, config_val, i_config, attr, max_power; +}; + +class CUsbIfDesc +{//9 bytes + U8 len, type, if_num, alt, num_eps, if_class, if_subclass, if_proto, i_if; +}; + +class CUsbEPDesc +{//7 bytes + U8 len, type, addr, attr; + U16 max_packet; + U8 interval; +}; + +//======================== xHCI ======================== + +#define XHCI_PCI_CLASS 0x0C0330 + +//Capability register byte offsets +#define XHCI_CAP_CAPLENGTH 0x00 +#define XHCI_CAP_HCIVERSION 0x02 +#define XHCI_CAP_HCSPARAMS1 0x04 +#define XHCI_CAP_HCSPARAMS2 0x08 +#define XHCI_CAP_HCSPARAMS3 0x0C +#define XHCI_CAP_HCCPARAMS1 0x10 +#define XHCI_CAP_DBOFF 0x14 +#define XHCI_CAP_RTSOFF 0x18 + +//Operational register byte offsets +#define XHCI_OP_USBCMD 0x00 +#define XHCI_OP_USBSTS 0x04 +#define XHCI_OP_PAGESIZE 0x08 +#define XHCI_OP_DNCTRL 0x14 +#define XHCI_OP_CRCR 0x18 +#define XHCI_OP_DCBAAP 0x30 +#define XHCI_OP_CONFIG 0x38 +#define XHCI_OP_PORTSC_BASE 0x400 //+ 0x10 * port_index + +//USBCMD bits +#define XHCI_CMD_RS 0x0001 +#define XHCI_CMD_HCRST 0x0002 +#define XHCI_CMD_INTE 0x0004 + +//USBSTS bits +#define XHCI_STS_HCH 0x0001 +#define XHCI_STS_CNR 0x0800 + +//PORTSC bits +#define XHCI_PORTSC_CCS 0x00000001 //Current connect status +#define XHCI_PORTSC_PED 0x00000002 //Port enabled +#define XHCI_PORTSC_PR 0x00000010 //Port reset +#define XHCI_PORTSC_PP 0x00000200 //Port power +#define XHCI_PORTSC_CSC 0x00020000 //Connect status change +#define XHCI_PORTSC_PEC 0x00040000 //Enable change +#define XHCI_PORTSC_WRC 0x00080000 //Warm reset change +#define XHCI_PORTSC_OCC 0x00100000 +#define XHCI_PORTSC_PRC 0x00200000 //Reset change +#define XHCI_PORTSC_PLC 0x00400000 +#define XHCI_PORTSC_CEC 0x00800000 +#define XHCI_PORTSC_CHANGE_BITS 0x00FE0000 +//Write-1-to-clear + preserve mask: RW bits we must not clobber when writing. +//Writing PORTSC: preserve PP, write change bits only to clear them. + +//Runtime register byte offsets (interrupter 0) +#define XHCI_RT_IMAN0 0x20 +#define XHCI_RT_IMOD0 0x24 +#define XHCI_RT_ERSTSZ0 0x28 +#define XHCI_RT_ERSTBA0 0x30 +#define XHCI_RT_ERDP0 0x38 + +//TRB types +#define XHCI_TRB_NORMAL 1 +#define XHCI_TRB_SETUP 2 +#define XHCI_TRB_DATA 3 +#define XHCI_TRB_STATUS 4 +#define XHCI_TRB_ISOCH 5 +#define XHCI_TRB_LINK 6 +#define XHCI_TRB_EVENT_DATA 7 +#define XHCI_TRB_NOOP 8 +#define XHCI_TRB_ENABLE_SLOT 9 +#define XHCI_TRB_DISABLE_SLOT 10 +#define XHCI_TRB_ADDRESS_DEV 11 +#define XHCI_TRB_CONFIG_EP 12 +#define XHCI_TRB_EVAL_CTX 13 +#define XHCI_TRB_RESET_EP 14 +#define XHCI_TRB_STOP_EP 15 +#define XHCI_TRB_SET_TR_DEQ 16 +#define XHCI_TRB_RESET_DEV 17 +#define XHCI_TRB_NOOP_CMD 23 +#define XHCI_TRB_EVT_TRANSFER 32 +#define XHCI_TRB_EVT_CMD_DONE 33 +#define XHCI_TRB_EVT_PORT_SC 34 +#define XHCI_TRB_EVT_HOST 37 + +//TRB control bits +#define XHCI_TRBF_CYCLE 0x0001 +#define XHCI_TRBF_TC 0x0002 //Toggle cycle (link TRB) +#define XHCI_TRBF_ISP 0x0004 //Interrupt on short packet +#define XHCI_TRBF_CH 0x0010 //Chain +#define XHCI_TRBF_IOC 0x0020 //Interrupt on completion +#define XHCI_TRBF_IDT 0x0040 //Immediate data +#define XHCI_TRBF_BSR 0x0200 //Block set address request +#define XHCI_TRBF_DIR_IN 0x10000 //Data/status stage direction + +//Setup stage TRT values (control bits 17:16) +#define XHCI_TRT_NO_DATA 0 +#define XHCI_TRT_OUT 0x20000 +#define XHCI_TRT_IN 0x30000 + +//Completion codes +#define XHCI_CC_SUCCESS 1 +#define XHCI_CC_DATA_BUF 2 +#define XHCI_CC_BABBLE 3 +#define XHCI_CC_TRANS_ERR 4 +#define XHCI_CC_TRB_ERR 5 +#define XHCI_CC_STALL 6 +#define XHCI_CC_SHORT_PKT 13 +#define XHCI_CC_TIMEOUT -1 //ours, not a hardware code + +//Endpoint context EP types +#define XHCI_EP_TYPE_ISOCH_OUT 1 +#define XHCI_EP_TYPE_BULK_OUT 2 +#define XHCI_EP_TYPE_INT_OUT 3 +#define XHCI_EP_TYPE_CTRL 4 +#define XHCI_EP_TYPE_ISOCH_IN 5 +#define XHCI_EP_TYPE_BULK_IN 6 +#define XHCI_EP_TYPE_INT_IN 7 + +#define XHCI_RING_TRBS 256 //4096 / 16 +#define XHCI_EVT_RING_TRBS 256 + +class CXhciTRB +{//16 bytes + U32 lo, hi, status, control; +}; + +class CXhciRing +{//Producer ring (command or transfer) + CXhciTRB *trbs; + I64 enq, //enqueue index + cycle; //producer cycle state +}; + +class CUsbEP +{ + CXhciRing ring; + I64 dci, //xHCI device context index + addr, //USB endpoint address (with 0x80 dir bit) + attr, //descriptor attributes + max_packet, + interval; //descriptor bInterval + Bool valid; +//Sync transfer completion, set by event poll + Bool xfer_done; + I64 xfer_code, //xHCI completion code + xfer_residue; //untransferred bytes +//Async auto-requeue (HID interrupt IN) + U8 *auto_buf; + I64 auto_len; + U0 (*auto_cb)(U8 *dev, U8 *ep); //really (CUsbDev*,CUsbEP*); report in auto_buf + CXhciTRB *pending_trb; +}; + +class CUsbIf +{ + Bool valid; + I64 if_num, if_class, if_subclass, if_proto; + I64 ep_idxs[4], //indices into dev->eps + ep_count; +}; + +class CUsbDev +{ + Bool present, configured; + I64 slot, //xHCI slot id + port, //root port index (0-based) + speed; //USB_SPEED_* + U8 *dev_ctx, + *in_ctx; + CUsbEP ep0; + CUsbEP eps[USB_EPS_MAX]; + I64 ep_count; + CUsbIf ifs[USB_IFS_MAX]; + I64 if_count; + I64 vendor_id, product_id, + dev_class, dev_subclass, dev_proto, + config_val; + U8 *drv_data; //class driver private (CUsbMsd / CUsbHid) + U8 desc_name[64]; //product string, ASCII +}; + +class CXhciGlobals +{ + Bool present, running; + U8 *mmio, //capability base (uncached alias) + *op, //operational base + *rt; //runtime base + U32 *db; //doorbell array + I64 max_slots, max_ports, ctx_size, + scratch_count; + U64 *dcbaa, + *scratch_arr, + *erst; + CXhciRing cmd_ring; + CXhciTRB *evt_ring; + I64 evt_deq, evt_cycle; + I64 lock; //ring enqueue lock +//Command completion, set by event poll + Bool cmd_done; + I64 cmd_code, cmd_slot; + CXhciTRB *cmd_trb; //pending command TRB + Bool port_change; //port status change event seen + I64 pci_bus, pci_dev, pci_fun; +}; + +CXhciGlobals usb_xhci; +CUsbDev usb_devs[USB_DEVS_MAX]; +CTask *usb_task = NULL; +Bool usb_verbose = FALSE; diff --git a/src/System/Usb/UsbHid.ZC b/src/System/Usb/UsbHid.ZC new file mode 100644 index 000000000..3748bab58 --- /dev/null +++ b/src/System/Usb/UsbHid.ZC @@ -0,0 +1,251 @@ +//USB HID boot-protocol keyboard + mouse. +//Keyboard: HID usages -> PS/2 set-1 scancodes -> kbd.fifo2 (reuses kernel KbdHandler). +//Mouse: boot reports -> mouse_hard state (reuses kernel mouse pipeline). + +#define USB_HIDS_MAX 8 + +#define USB_HID_REPEAT_DELAY 0.400 +#define USB_HID_REPEAT_RATE 0.035 + +class CUsbHid +{ + Bool valid; + CUsbDev *dev; + CUsbEP *ep; + I64 proto; //1=kbd, 2=mouse + U8 prev[8]; + I64 repeat_sc; //held key scancode for software typematic + F64 repeat_time; +}; + +CUsbHid usb_hids[USB_HIDS_MAX]; + +//HID usage (page 7) -> set-1 scancode. 0x100 flag = 0xE0-prefixed. +I64 usb_hid_to_sc[0x66] = { + 0, 0, 0, 0, 0x1E, 0x30, 0x2E, 0x20, //00-07: ---- a b c d + 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, //08-0F: e f g h i j k l + 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F, 0x14, //10-17: m n o p q r s t + 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x02, 0x03, //18-1F: u v w x y z 1 2 + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, //20-27: 3 4 5 6 7 8 9 0 + 0x1C, 0x01, 0x0E, 0x0F, 0x39, 0x0C, 0x0D, 0x1A, //28-2F: Ent Esc BS Tab Sp - = [ + 0x1B, 0x2B, 0x2B, 0x27, 0x28, 0x29, 0x33, 0x34, //30-37: ] \ # ; ' ` , . + 0x35, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, //38-3F: / Caps F1-F6 + 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0x137, 0x46, //40-47: F7-F12 PrtSc ScrLk + 0, 0x152, 0x147, 0x149, 0x153, 0x14F, 0x151, 0x14D, //48-4F: Pause Ins Home PgUp Del End PgDn Right + 0x14B, 0x150, 0x148, 0x45, 0x135, 0x37, 0x4A, 0x4E, //50-57: Left Down Up NumLk KP/ KP* KP- KP+ + 0x11C, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47, //58-5F: KPEnt KP1-KP7 + 0x48, 0x49, 0x52, 0x53, 0x56, 0x15D //60-65: KP8 KP9 KP0 KP. NonUS\ App +}; + +//Modifier bit -> scancode: LCtrl LShift LAlt LGUI RCtrl RShift RAlt RGUI +I64 usb_hid_mod_sc[8] = {0x1D, 0x2A, 0x38, 0x15B, 0x11D, 0x36, 0x138, 0x15C}; + +U0 UsbKbdEmit(I64 sc, I64 up) +{//Push set-1 bytes into kernel keyboard fifo. + if (!sc) + return; + if (sc & 0x100) + FifoU8Insert(kbd.fifo2, 0xE0); + if (up) + FifoU8Insert(kbd.fifo2, (sc & 0x7F) | 0x80); + else + FifoU8Insert(kbd.fifo2, sc & 0x7F); +} + +CUsbHid *UsbHidByEP(CUsbEP *ep) +{ + I64 i; + + for (i = 0; i < USB_HIDS_MAX; i++) + if (usb_hids[i].valid && usb_hids[i].ep == ep) + return &usb_hids[i]; + + return NULL; +} + +Bool UsbKbdReportHas(U8 *report, I64 usage) +{ + I64 i; + + for (i = 2; i < 8; i++) + if (report[i] == usage) + return TRUE; + + return FALSE; +} + +U0 UsbKbdCB(U8 *d, U8 *e) +{//Interrupt IN report arrived (8-byte boot keyboard report). + CUsbDev *dev = d; + CUsbEP *ep = e; + CUsbHid *hid = UsbHidByEP(ep); + U8 *rep; + I64 i, mods, prev_mods, sc; + + if (!hid || !dev->present) + return; + rep = ep->auto_buf; + if (rep[2] == 1) + return; //error rollover + mods = rep[0]; + prev_mods = hid->prev[0]; + for (i = 0; i < 8; i++) + { + if ((mods & (1 << i)) && !(prev_mods & (1 << i))) + UsbKbdEmit(usb_hid_mod_sc[i], FALSE); + if (!(mods & (1 << i)) && (prev_mods & (1 << i))) + UsbKbdEmit(usb_hid_mod_sc[i], TRUE); + } +//Released keys + for (i = 2; i < 8; i++) + { + if (hid->prev[i] && !UsbKbdReportHas(rep, hid->prev[i])) + { + if (hid->prev[i] < 0x66) + { + sc = usb_hid_to_sc[hid->prev[i]]; + UsbKbdEmit(sc, TRUE); + if (hid->repeat_sc == sc) + hid->repeat_sc = 0; + } + } + } +//Pressed keys + for (i = 2; i < 8; i++) + { + if (rep[i] && !UsbKbdReportHas(hid->prev, rep[i])) + { + if (rep[i] < 0x66) + { + sc = usb_hid_to_sc[rep[i]]; + UsbKbdEmit(sc, FALSE); + hid->repeat_sc = sc; + hid->repeat_time = tS + USB_HID_REPEAT_DELAY; + } + } + } + MemCopy(hid->prev, rep, 8); +} + +U0 UsbMouseCB(U8 *d, U8 *e) +{//Interrupt IN report arrived (boot mouse report: buttons dx dy [wheel]). + CUsbDev *dev = d; + CUsbEP *ep = e; + U8 *rep; + I64 dx, dy, dz = 0; + + if (!dev->present) + return; + rep = ep->auto_buf; + + dx = rep[1]; + if (dx & 0x80) + dx -= 256; + dy = rep[2]; + if (dy & 0x80) + dy -= 256; + if (ep->auto_len >= 4) + { + dz = rep[3]; + if (dz & 0x80) + dz -= 256; + } + + mouse_hard.timestamp = TSCGet; + MouseHardSetPre; + mouse_hard.bttns[0] = rep[0] & 1; + mouse_hard.bttns[1] = (rep[0] & 2) >> 1; + mouse_hard.bttns[2] = (rep[0] & 4) >> 2; + mouse_hard.prescale.x += dx; + mouse_hard.prescale.y += dy; + mouse_hard.prescale.z += dz; + mouse.show = TRUE; + MouseHardSetPost; +} + +U0 UsbHidTick() +{//Software typematic repeat; called from USB task main loop. + CUsbHid *hid; + I64 i; + + for (i = 0; i < USB_HIDS_MAX; i++) + { + hid = &usb_hids[i]; + if (hid->valid && hid->proto == USB_HID_PROTO_KBD && hid->repeat_sc) + { + if (tS >= hid->repeat_time) + { + UsbKbdEmit(hid->repeat_sc, FALSE); + hid->repeat_time = tS + USB_HID_REPEAT_RATE; + } + } + } +} + +U0 UsbHidAttach(CUsbDev *dev, CUsbIf *uif) +{//Bind boot keyboard or mouse interface. + CUsbHid *hid = NULL; + CUsbEP *ep = NULL, *tmp; + I64 i; + + for (i = 0; i < uif->ep_count; i++) + { + tmp = &dev->eps[uif->ep_idxs[i]]; + if ((tmp->addr & 0x80) && (tmp->attr & 3) == USB_EP_TYPE_INT) + { + ep = tmp; + break; + } + } + if (!ep) + return; + for (i = 0; i < USB_HIDS_MAX; i++) + if (!usb_hids[i].valid) + { + hid = &usb_hids[i]; + break; + } + if (!hid) + return; + MemSet(hid, 0, sizeof(CUsbHid)); + hid->dev = dev; + hid->ep = ep; + hid->proto = uif->if_proto; + hid->valid = TRUE; + +//Boot protocol; idle=0 -> reports only on change. + UsbCtrl(dev, USB_RT_HOST_TO_DEV | USB_RT_CLASS | USB_RT_RECIP_IF, + USB_HID_RQ_SET_PROTOCOL, 0, uif->if_num, NULL, 0); + UsbCtrl(dev, USB_RT_HOST_TO_DEV | USB_RT_CLASS | USB_RT_RECIP_IF, + USB_HID_RQ_SET_IDLE, 0, uif->if_num, NULL, 0); + + if (!ep->auto_buf) + ep->auto_buf = CAlloc(8, sys_task->code_heap); + ep->auto_len = ep->max_packet; + if (ep->auto_len > 8) + ep->auto_len = 8; + if (ep->auto_len < 3) + ep->auto_len = 3; + if (uif->if_proto == USB_HID_PROTO_KBD) + { + ep->auto_len = 8; + ep->auto_cb = &UsbKbdCB; + SysLog("USB: HID keyboard on port %d.\n", dev->port); + } + else + { + ep->auto_cb = &UsbMouseCB; + mouse_hard.installed = TRUE; //menu bar + cursor paths check this + SysLog("USB: HID mouse on port %d.\n", dev->port); + } + XhciIntQueue(dev, ep); +} + +U0 UsbHidDetach(CUsbDev *dev) +{ + I64 i; + + for (i = 0; i < USB_HIDS_MAX; i++) + if (usb_hids[i].valid && usb_hids[i].dev == dev) + usb_hids[i].valid = FALSE; +} diff --git a/src/System/Usb/UsbMsd.ZC b/src/System/Usb/UsbMsd.ZC new file mode 100644 index 000000000..4bc404bed --- /dev/null +++ b/src/System/Usb/UsbMsd.ZC @@ -0,0 +1,382 @@ +//USB Mass Storage: Bulk-Only Transport + SCSI, mounted as BDT_USB block devices. + +#define USB_MSDS_MAX 4 +#define USB_MSD_CHUNK_BLKS 64 //32KB bounce per transfer + +#define USB_MSD_CBW_SIG 0x43425355 +#define USB_MSD_CSW_SIG 0x53425355 + +class CUsbMsdCBW +{//31 bytes + U32 sig, tag, data_len; + U8 flags, lun, cb_len; + U8 cb[16]; +}; + +class CUsbMsdCSW +{//13 bytes + U32 sig, tag, residue; + U8 status; +}; + +class CUsbMsd +{ + Bool valid, ready; + CUsbDev *dev; + CUsbEP *bulk_in, *bulk_out; + I64 if_num, + next_tag, + max_lba, blk_size, + lock; + U8 first_drive_let; + CUsbMsdCBW *cbw; + CUsbMsdCSW *csw; + U8 *bounce; + CBlkDev *bd; +}; + +CUsbMsd usb_msds[USB_MSDS_MAX]; + +U0 UsbMsdLock(CUsbMsd *msd) +{ + while (LBts(&msd->lock, 0)) + Yield; +} + +U0 UsbMsdUnlock(CUsbMsd *msd) +{ + LBtr(&msd->lock, 0); +} + +U0 UsbMsdBotReset(CUsbMsd *msd) +{//Bulk-Only Mass Storage Reset + clear both endpoint halts. + UsbCtrl(msd->dev, USB_RT_HOST_TO_DEV | USB_RT_CLASS | USB_RT_RECIP_IF, + USB_MSD_RQ_RESET, 0, msd->if_num, NULL, 0); + UsbClearHalt(msd->dev, msd->bulk_in); + UsbClearHalt(msd->dev, msd->bulk_out); +} + +I64 UsbMsdCmd(CUsbMsd *msd, U8 *cb, I64 cb_len, U8 *data, I64 data_len, I64 dir_in) +{//One BOT command. Returns CSW status (0 ok, 1 check condition), or -1 transport error. + I64 code, i, residue; + CUsbEP *data_ep; + + MemSet(msd->cbw, 0, sizeof(CUsbMsdCBW)); + msd->cbw->sig = USB_MSD_CBW_SIG; + msd->next_tag++; + msd->cbw->tag = msd->next_tag; + msd->cbw->data_len = data_len; + if (dir_in) + msd->cbw->flags = 0x80; + msd->cbw->lun = 0; + msd->cbw->cb_len = cb_len; + for (i = 0; i < cb_len; i++) + msd->cbw->cb[i] = cb[i]; + + code = XhciBulkXfer(msd->dev, msd->bulk_out, msd->cbw, sizeof(CUsbMsdCBW), 2.0); + if (code == XHCI_CC_STALL) + { + UsbMsdBotReset(msd); + return -1; + } + if (code != XHCI_CC_SUCCESS) + return -1; + + if (data_len) + { + if (dir_in) + data_ep = msd->bulk_in; + else + data_ep = msd->bulk_out; + code = XhciBulkXfer(msd->dev, data_ep, data, data_len, 4.0, &residue); + if (code == XHCI_CC_STALL) + UsbClearHalt(msd->dev, data_ep); + else if (code != XHCI_CC_SUCCESS && code != XHCI_CC_SHORT_PKT) + return -1; + } + + MemSet(msd->csw, 0, sizeof(CUsbMsdCSW)); + code = XhciBulkXfer(msd->dev, msd->bulk_in, msd->csw, sizeof(CUsbMsdCSW), 2.0); + if (code == XHCI_CC_STALL) + {//Retry CSW once after clearing halt. + UsbClearHalt(msd->dev, msd->bulk_in); + code = XhciBulkXfer(msd->dev, msd->bulk_in, msd->csw, sizeof(CUsbMsdCSW), 2.0); + } + if (code != XHCI_CC_SUCCESS && code != XHCI_CC_SHORT_PKT) + return -1; + if (msd->csw->sig != USB_MSD_CSW_SIG || msd->csw->tag != msd->cbw->tag) + return -1; + if (msd->csw->status == 2) + {//Phase error + UsbMsdBotReset(msd); + return -1; + } + + return msd->csw->status; +} + +I64 UsbMsdRequestSense(CUsbMsd *msd) +{//Returns sense key, or -1. + U8 cb[6], *buf = msd->bounce; + I64 res; + + MemSet(cb, 0, 6); + cb[0] = 0x03; + cb[4] = 18; + res = UsbMsdCmd(msd, cb, 6, buf, 18, TRUE); + if (res) + return -1; + + return buf[2] & 0xF; +} + +Bool UsbMsdTestReady(CUsbMsd *msd) +{ + U8 cb[6]; + + MemSet(cb, 0, 6); + + return UsbMsdCmd(msd, cb, 6, NULL, 0, TRUE) == 0; +} + +Bool UsbMsdInquiry(CUsbMsd *msd, U8 *buf36) +{ + U8 cb[6]; + I64 res; + + MemSet(cb, 0, 6); + cb[0] = 0x12; + cb[4] = 36; + res = UsbMsdCmd(msd, cb, 6, msd->bounce, 36, TRUE); + if (!res) + MemCopy(buf36, msd->bounce, 36); + + return res == 0; +} + +Bool UsbMsdReadCapacity(CUsbMsd *msd) +{ + U8 cb[10], *buf = msd->bounce; + I64 res; + + MemSet(cb, 0, 10); + cb[0] = 0x25; + res = UsbMsdCmd(msd, cb, 10, buf, 8, TRUE); + if (res) + return FALSE; + msd->max_lba = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + msd->blk_size = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; + + return TRUE; +} + +I64 UsbMsdRW10(CUsbMsd *msd, I64 write, I64 lba, I64 count, U8 *buf) +{//READ(10)/WRITE(10) through bounce buffer. count <= USB_MSD_CHUNK_BLKS. + U8 cb[10]; + I64 res, bytes = count * msd->blk_size; + + MemSet(cb, 0, 10); + if (write) + { + cb[0] = 0x2A; + MemCopy(msd->bounce, buf, bytes); + } + else + cb[0] = 0x28; + cb[2] = (lba >> 24) & 0xFF; + cb[3] = (lba >> 16) & 0xFF; + cb[4] = (lba >> 8) & 0xFF; + cb[5] = lba & 0xFF; + cb[7] = (count >> 8) & 0xFF; + cb[8] = count & 0xFF; + res = UsbMsdCmd(msd, cb, 10, msd->bounce, bytes, !write); + if (!res && !write) + MemCopy(buf, msd->bounce, bytes); + + return res; +} + +Bool UsbMsdBlksRW(CBlkDev *bd, U8 *buf, I64 blk, I64 count, I64 write) +{ + CUsbMsd *msd; + I64 n, res, retries; + + if (bd->port_num < 0 || bd->port_num >= USB_MSDS_MAX) + return FALSE; + msd = &usb_msds[bd->port_num]; + if (!msd->valid || !msd->dev || !msd->dev->present) + return FALSE; + UsbMsdLock(msd); + while (count > 0) + { + n = count; + if (n > USB_MSD_CHUNK_BLKS) + n = USB_MSD_CHUNK_BLKS; + retries = 2; + do + { + res = UsbMsdRW10(msd, write, blk, n, buf); + if (res > 0) + UsbMsdRequestSense(msd); + retries--; + } + while (res && retries > 0); + if (res) + { + UsbMsdUnlock(msd); + return FALSE; + } + blk += n; + buf += n * msd->blk_size; + count -= n; + } + UsbMsdUnlock(msd); + + return TRUE; +} + +Bool UsbMsdBlksRead(CBlkDev *bd, U8 *buf, I64 blk, I64 count) +{ + return UsbMsdBlksRW(bd, buf, blk, count, FALSE); +} + +Bool UsbMsdBlksWrite(CBlkDev *bd, U8 *buf, I64 blk, I64 count) +{ + return UsbMsdBlksRW(bd, buf, blk, count, TRUE); +} + +Bool UsbMsdKernelHooksSet() +{//Resolve kernel USB hooks at runtime so an old Kernel.ZXE degrades gracefully +//(HID still works, storage disabled) instead of hard-failing the System compile. + CHashGlobalVar *r, *w; + U64 *p; + + r = HashFind("fp_usb_msd_blks_read", Fs->hash_table, HTT_GLOBAL_VAR); + w = HashFind("fp_usb_msd_blks_write", Fs->hash_table, HTT_GLOBAL_VAR); + if (!r || !w || !r->data_addr || !w->data_addr) + return FALSE; + p = r->data_addr; + *p = &UsbMsdBlksRead; + p = w->data_addr; + *p = &UsbMsdBlksWrite; + + return TRUE; +} + +U0 UsbMsdAttach(CUsbDev *dev, CUsbIf *uif) +{//Bind a Bulk-Only SCSI mass storage interface, mount partitions. + CUsbMsd *msd = NULL; + CUsbEP *ep; + CBlkDev *bd; + U8 inq[40]; + I64 i, tries, res, msd_idx = 0; + + if (uif->if_proto != USB_MSD_PROTO_BOT) + { + SysLog("USB: MSD port %d: unsupported protocol 0x%02X.\n", dev->port, uif->if_proto); + return; + } + for (i = 0; i < USB_MSDS_MAX; i++) + if (!usb_msds[i].valid) + { + msd = &usb_msds[i]; + msd_idx = i; + break; + } + if (!msd) + return; +//Clear state fields but keep the persistent DMA buffer allocations. + MemSet(msd, 0, offset(CUsbMsd.cbw)); + msd->dev = dev; + msd->if_num = uif->if_num; + for (i = 0; i < uif->ep_count; i++) + { + ep = &dev->eps[uif->ep_idxs[i]]; + if ((ep->attr & 3) == USB_EP_TYPE_BULK) + { + if (ep->addr & 0x80) + msd->bulk_in = ep; + else + msd->bulk_out = ep; + } + } + if (!msd->bulk_in || !msd->bulk_out) + { + SysLog("USB: MSD port %d: missing bulk endpoints.\n", dev->port); + return; + } + if (!msd->cbw) + msd->cbw = CAlloc(sizeof(CUsbMsdCBW), sys_task->code_heap); + if (!msd->csw) + msd->csw = CAlloc(sizeof(CUsbMsdCSW), sys_task->code_heap); + if (!msd->bounce) + msd->bounce = CAlloc(USB_MSD_CHUNK_BLKS * BLK_SIZE, sys_task->code_heap); + msd->next_tag = 1; + msd->valid = TRUE; + dev->drv_data = msd; + + MemSet(inq, 0, 40); + UsbMsdInquiry(msd, inq); + inq[36] = 0; + + tries = 10; + while (tries > 0 && !UsbMsdTestReady(msd)) + { + UsbMsdRequestSense(msd); + Sleep(100); + tries--; + } + if (!UsbMsdReadCapacity(msd)) + { + SysLog("USB: MSD port %d: READ CAPACITY failed.\n", dev->port); + msd->valid = FALSE; + return; + } + if (msd->blk_size != BLK_SIZE) + { + SysLog("USB: MSD port %d: unsupported block size %d.\n", dev->port, msd->blk_size); + msd->valid = FALSE; + return; + } + msd->ready = TRUE; + + if (!UsbMsdKernelHooksSet) + { + SysLog("USB: MSD port %d: kernel lacks USB block hooks (rebuild kernel); storage not mounted.\n", + dev->port); + return; + } + + msd->first_drive_let = DriveNextFreeLet('V'); + bd = BlkDevNextFreeSlot(msd->first_drive_let, BDT_USB); + bd->max_blk = msd->max_lba; + bd->port_num = msd_idx; + msd->bd = bd; + res = BlkDevAdd(bd, , FALSE, FALSE); + if (res) + SysLog("USB: MSD port %d: \"%s\" %d MB mounted, %d partition(s), first drive %C:.\n", + dev->port, &inq[8], ((msd->max_lba + 1) * BLK_SIZE) / 0x100000, res, + msd->first_drive_let); + else + { + SysLog("USB: MSD port %d: no mountable partitions.\n", dev->port); + msd->bd = NULL; + } +} + +U0 UsbMsdDetach(CUsbDev *dev) +{ + I64 i; + + for (i = 0; i < USB_MSDS_MAX; i++) + if (usb_msds[i].valid && usb_msds[i].dev == dev) + { + usb_msds[i].valid = FALSE; + usb_msds[i].ready = FALSE; + if (usb_msds[i].bd) + { + BlkDevDel(usb_msds[i].bd); + usb_msds[i].bd = NULL; + } + } +} diff --git a/src/System/Usb/Xhci.ZC b/src/System/Usb/Xhci.ZC new file mode 100644 index 000000000..9aec2af24 --- /dev/null +++ b/src/System/Usb/Xhci.ZC @@ -0,0 +1,697 @@ +//xHCI (USB3) host controller driver. Polled, no MSI. +//All USB commands are issued from the USB task only. +//Sync transfers may be awaited from any task (event poll runs in USB task). + +U32 XhciCapR32(I64 off) +{ + U32 *p = usb_xhci.mmio + off; + + return *p; +} + +U32 XhciOpR32(I64 off) +{ + U32 *p = usb_xhci.op + off; + + return *p; +} + +U0 XhciOpW32(I64 off, I64 val) +{ + U32 *p = usb_xhci.op + off; + + *p = val; +} + +U0 XhciOpW64(I64 off, I64 val) +{ + U64 *p = usb_xhci.op + off; + + *p = val; +} + +U32 XhciRtR32(I64 off) +{ + U32 *p = usb_xhci.rt + off; + + return *p; +} + +U0 XhciRtW32(I64 off, I64 val) +{ + U32 *p = usb_xhci.rt + off; + + *p = val; +} + +U0 XhciRtW64(I64 off, I64 val) +{ + U64 *p = usb_xhci.rt + off; + + *p = val; +} + +U32 XhciPortSC(I64 port) +{ + return XhciOpR32(XHCI_OP_PORTSC_BASE + 0x10 * port); +} + +U0 XhciPortSCWrite(I64 port, I64 val) +{ + XhciOpW32(XHCI_OP_PORTSC_BASE + 0x10 * port, val); +} + +U0 XhciLock() +{ + while (LBts(&usb_xhci.lock, 0)) + Yield; +} + +U0 XhciUnlock() +{ + LBtr(&usb_xhci.lock, 0); +} + +U0 XhciRingInit(CXhciRing *ring) +{ + if (!ring->trbs) + ring->trbs = CAllocAligned(XHCI_RING_TRBS * sizeof(CXhciTRB), 0x1000, sys_task->code_heap); + else + MemSet(ring->trbs, 0, XHCI_RING_TRBS * sizeof(CXhciTRB)); + ring->enq = 0; + ring->cycle = 1; +} + +CXhciTRB *XhciRingEnq(CXhciRing *ring, I64 lo, I64 hi, I64 status, I64 control) +{//control passed without cycle bit; caller holds lock. + CXhciTRB *trb = &ring->trbs[ring->enq], *link; + I64 addr; + + trb->lo = lo; + trb->hi = hi; + trb->status = status; + trb->control = (control & ~1) | ring->cycle; + ring->enq++; + if (ring->enq == XHCI_RING_TRBS - 1) + {//Place link TRB back to start, toggle cycle. + link = &ring->trbs[XHCI_RING_TRBS - 1]; + addr = ring->trbs; + link->lo = addr & 0xFFFFFFFF; + link->hi = addr >> 32; + link->status = 0; + link->control = (XHCI_TRB_LINK << 10) | XHCI_TRBF_TC | ring->cycle; + ring->enq = 0; + ring->cycle ^= 1; + } + + return trb; +} + +CUsbDev *UsbDevBySlot(I64 slot) +{ + I64 i; + + for (i = 0; i < USB_DEVS_MAX; i++) + if (usb_devs[i].present && usb_devs[i].slot == slot) + return &usb_devs[i]; + + return NULL; +} + +CUsbDev *UsbDevByPort(I64 port) +{ + I64 i; + + for (i = 0; i < USB_DEVS_MAX; i++) + if (usb_devs[i].present && usb_devs[i].port == port) + return &usb_devs[i]; + + return NULL; +} + +CUsbEP *UsbDevEPByDCI(CUsbDev *dev, I64 dci) +{ + I64 i; + + if (dci == 1) + return &dev->ep0; + for (i = 0; i < dev->ep_count; i++) + if (dev->eps[i].valid && dev->eps[i].dci == dci) + return &dev->eps[i]; + + return NULL; +} + +U0 XhciIntQueue(CUsbDev *dev, CUsbEP *ep) +{//Queue one interrupt IN TRB on ep, report delivered via ep->auto_cb. + XhciLock; + ep->pending_trb = XhciRingEnq(&ep->ring, ep->auto_buf, 0, ep->auto_len, + (XHCI_TRB_NORMAL << 10) | XHCI_TRBF_IOC | XHCI_TRBF_ISP); + usb_xhci.db[dev->slot] = ep->dci; + XhciUnlock; +} + +U0 XhciEventPoll() +{//Drain event ring. Called ONLY in USB task context. + CXhciTRB *e; + CUsbDev *dev; + CUsbEP *ep; + I64 type, code, slot, dci, addr, work = FALSE; + U64 *erdp; + + if (!usb_xhci.running) + return; + while (TRUE) + { + e = &usb_xhci.evt_ring[usb_xhci.evt_deq]; + if ((e->control & 1) != usb_xhci.evt_cycle) + break; + work = TRUE; + type = (e->control >> 10) & 0x3F; + switch (type) + { + case XHCI_TRB_EVT_CMD_DONE: + usb_xhci.cmd_code = (e->status >> 24) & 0xFF; + usb_xhci.cmd_slot = (e->control >> 24) & 0xFF; + addr = usb_xhci.cmd_trb; + if (usb_xhci.cmd_trb && e->lo == addr & 0xFFFFFFFF) + usb_xhci.cmd_done = TRUE; + break; + + case XHCI_TRB_EVT_TRANSFER: + slot = (e->control >> 24) & 0xFF; + dci = (e->control >> 16) & 0x1F; + code = (e->status >> 24) & 0xFF; + dev = UsbDevBySlot(slot); + if (dev) + { + ep = UsbDevEPByDCI(dev, dci); + if (ep) + { + if (ep->auto_cb) + {//HID interrupt IN: deliver + requeue + if (code == XHCI_CC_SUCCESS || code == XHCI_CC_SHORT_PKT) + (*ep->auto_cb)(dev, ep); + XhciIntQueue(dev, ep); + } + else + { + ep->xfer_code = code; + ep->xfer_residue = e->status & 0xFFFFFF; + ep->xfer_done = TRUE; + } + } + } + break; + + case XHCI_TRB_EVT_PORT_SC: + usb_xhci.port_change = TRUE; + break; + } + usb_xhci.evt_deq++; + if (usb_xhci.evt_deq == XHCI_EVT_RING_TRBS) + { + usb_xhci.evt_deq = 0; + usb_xhci.evt_cycle ^= 1; + } + } + if (work) + { + addr = usb_xhci.evt_ring + usb_xhci.evt_deq; + erdp = usb_xhci.rt + XHCI_RT_ERDP0; + *erdp = addr | 8; //8 = clear Event Handler Busy + } +} + +I64 XhciCmd(I64 lo, I64 hi, I64 status, I64 control, I64 *_slot=NULL) +{//Issue one command TRB, wait completion. USB task only. + F64 timeout; + + XhciLock; + usb_xhci.cmd_done = FALSE; + usb_xhci.cmd_code = XHCI_CC_TIMEOUT; + usb_xhci.cmd_trb = XhciRingEnq(&usb_xhci.cmd_ring, lo, hi, status, control); + usb_xhci.db[0] = 0; + XhciUnlock; + timeout = tS + 1.0; + while (!usb_xhci.cmd_done && tS < timeout) + { + XhciEventPoll; + Yield; + } + if (_slot) + *_slot = usb_xhci.cmd_slot; + if (!usb_xhci.cmd_done) + return XHCI_CC_TIMEOUT; + + return usb_xhci.cmd_code; +} + +I64 XhciXferWait(CUsbEP *ep, F64 timeout_s) +{ + F64 timeout = tS + timeout_s; + + while (!ep->xfer_done && tS < timeout) + { + if (Fs == usb_task) + XhciEventPoll; + Yield; + } + if (!ep->xfer_done) + return XHCI_CC_TIMEOUT; + + return ep->xfer_code; +} + +I64 XhciCtrlXfer(CUsbDev *dev, I64 rt, I64 rq, I64 val, I64 idx, U8 *buf, I64 len) +{//Control transfer on EP0. Returns completion code. + CUsbEP *ep = &dev->ep0; + I64 trt, ctl, addr; + + if (len) + { + if (rt & USB_RT_DEV_TO_HOST) + trt = XHCI_TRT_IN; + else + trt = XHCI_TRT_OUT; + } + else + trt = XHCI_TRT_NO_DATA; + XhciLock; + ep->xfer_done = FALSE; + ep->xfer_code = XHCI_CC_TIMEOUT; + ep->xfer_residue = 0; + XhciRingEnq(&ep->ring, rt | (rq << 8) | (val << 16), idx | (len << 16), 8, + (XHCI_TRB_SETUP << 10) | XHCI_TRBF_IDT | trt); + if (len) + { + ctl = XHCI_TRB_DATA << 10; + if (rt & USB_RT_DEV_TO_HOST) + ctl |= XHCI_TRBF_DIR_IN; + addr = buf; + XhciRingEnq(&ep->ring, addr & 0xFFFFFFFF, addr >> 32, len, ctl); + } + ctl = (XHCI_TRB_STATUS << 10) | XHCI_TRBF_IOC; + if (!len || !(rt & USB_RT_DEV_TO_HOST)) + ctl |= XHCI_TRBF_DIR_IN; //status stage opposite dir; IN if no data + XhciRingEnq(&ep->ring, 0, 0, 0, ctl); + usb_xhci.db[dev->slot] = 1; + XhciUnlock; + + return XhciXferWait(ep, 1.0); +} + +I64 XhciBulkXfer(CUsbDev *dev, CUsbEP *ep, U8 *buf, I64 len, F64 timeout_s, I64 *_residue=NULL) +{//Single-TRB bulk transfer (max 64KB). Returns completion code. + I64 addr = buf, code; + + XhciLock; + ep->xfer_done = FALSE; + ep->xfer_code = XHCI_CC_TIMEOUT; + ep->xfer_residue = 0; + XhciRingEnq(&ep->ring, addr & 0xFFFFFFFF, addr >> 32, len, + (XHCI_TRB_NORMAL << 10) | XHCI_TRBF_IOC | XHCI_TRBF_ISP); + usb_xhci.db[dev->slot] = ep->dci; + XhciUnlock; + code = XhciXferWait(ep, timeout_s); + if (_residue) + *_residue = ep->xfer_residue; + + return code; +} + +U32 *XhciInputCtx(CUsbDev *dev, I64 idx) +{//idx: 0=input control ctx, 1=slot ctx, 1+dci=EP ctx + U32 *p = dev->in_ctx + idx * usb_xhci.ctx_size; + + return p; +} + +U32 *XhciDevCtxGet(CUsbDev *dev, I64 idx) +{//idx: 0=slot ctx, dci=EP ctx + U32 *p = dev->dev_ctx + idx * usb_xhci.ctx_size; + + return p; +} + +I64 XhciEPInterval(CUsbDev *dev, CUsbEP *ep) +{//EP context Interval field, units of 125us * 2^n. + I64 v = ep->interval, n; + + if (dev->speed == USB_SPEED_HIGH || dev->speed == USB_SPEED_SUPER) + { + if (v < 1) + v = 1; + if (v > 16) + v = 16; + return v - 1; + } +//FS/LS: bInterval is in ms; pick n with 2^(n-3) ms >= v. + n = 3; + while ((1 << (n - 3)) < v && n < 10) + n++; + + return n; +} + +I64 XhciEPCtxType(CUsbEP *ep) +{ + I64 t = ep->attr & 3, in = FALSE; + + if (ep->addr & 0x80) + in = TRUE; + if (t == USB_EP_TYPE_BULK) + { + if (in) + return XHCI_EP_TYPE_BULK_IN; + return XHCI_EP_TYPE_BULK_OUT; + } + if (t == USB_EP_TYPE_INT) + { + if (in) + return XHCI_EP_TYPE_INT_IN; + return XHCI_EP_TYPE_INT_OUT; + } + if (t == USB_EP_TYPE_ISOCH) + { + if (in) + return XHCI_EP_TYPE_ISOCH_IN; + return XHCI_EP_TYPE_ISOCH_OUT; + } + + return XHCI_EP_TYPE_CTRL; +} + +I64 XhciEnableSlot() +{//Returns slot id, or 0 on failure. + I64 slot = 0, code; + + code = XhciCmd(0, 0, 0, XHCI_TRB_ENABLE_SLOT << 10, &slot); + if (code != XHCI_CC_SUCCESS) + return 0; + + return slot; +} + +U0 XhciDisableSlot(I64 slot) +{ + XhciCmd(0, 0, 0, (XHCI_TRB_DISABLE_SLOT << 10) | (slot << 24)); +} + +I64 XhciAddressDev(CUsbDev *dev) +{//Alloc contexts + EP0 ring, issue Address Device. Returns completion code. + U32 *icc, *slot_ctx, *ep0_ctx; + I64 mps, addr; + + if (!dev->dev_ctx) + dev->dev_ctx = CAllocAligned(usb_xhci.ctx_size * 32, 0x1000, sys_task->code_heap); + else + MemSet(dev->dev_ctx, 0, usb_xhci.ctx_size * 32); + if (!dev->in_ctx) + dev->in_ctx = CAllocAligned(usb_xhci.ctx_size * 33, 0x1000, sys_task->code_heap); + else + MemSet(dev->in_ctx, 0, usb_xhci.ctx_size * 33); + XhciRingInit(&dev->ep0.ring); + dev->ep0.dci = 1; + dev->ep0.valid = TRUE; + dev->ep0.auto_cb = NULL; + + switch (dev->speed) + { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + mps = 8; + break; + case USB_SPEED_HIGH: + mps = 64; + break; + default: + mps = 512; + } + dev->ep0.max_packet = mps; + + icc = XhciInputCtx(dev, 0); + icc[0] = 0; + icc[1] = 3; //add slot + EP0 + slot_ctx = XhciInputCtx(dev, 1); + slot_ctx[0] = (1 << 27) | (dev->speed << 20); + slot_ctx[1] = (dev->port + 1) << 16; + ep0_ctx = XhciInputCtx(dev, 2); + ep0_ctx[1] = (3 << 1) | (XHCI_EP_TYPE_CTRL << 3) | (mps << 16); + addr = dev->ep0.ring.trbs; + ep0_ctx[2] = (addr & 0xFFFFFFFF) | 1; //DCS=1 + ep0_ctx[3] = addr >> 32; + ep0_ctx[4] = 8; + + usb_xhci.dcbaa[dev->slot] = dev->dev_ctx; + addr = dev->in_ctx; + + return XhciCmd(addr & 0xFFFFFFFF, addr >> 32, 0, + (XHCI_TRB_ADDRESS_DEV << 10) | (dev->slot << 24)); +} + +I64 XhciEvalCtxMPS0(CUsbDev *dev, I64 mps) +{//Update EP0 max packet size after reading device descriptor. + U32 *icc, *ep0_ctx; + I64 addr; + + MemSet(dev->in_ctx, 0, usb_xhci.ctx_size * 33); + icc = XhciInputCtx(dev, 0); + icc[0] = 0; + icc[1] = 2; //A1 = EP0 + ep0_ctx = XhciInputCtx(dev, 2); + MemCopy(ep0_ctx, XhciDevCtxGet(dev, 1), usb_xhci.ctx_size); + ep0_ctx[1] = (ep0_ctx[1] & 0xFFFF) | (mps << 16); + dev->ep0.max_packet = mps; + addr = dev->in_ctx; + + return XhciCmd(addr & 0xFFFFFFFF, addr >> 32, 0, + (XHCI_TRB_EVAL_CTX << 10) | (dev->slot << 24)); +} + +I64 XhciConfigureEPs(CUsbDev *dev) +{//Build input context with all parsed EPs, issue Configure Endpoint. + U32 *icc, *slot_ctx, *ep_ctx; + CUsbEP *ep; + I64 i, addr, max_dci = 1, type, ival; + + MemSet(dev->in_ctx, 0, usb_xhci.ctx_size * 33); + icc = XhciInputCtx(dev, 0); + icc[0] = 0; + icc[1] = 1; //A0 = slot + for (i = 0; i < dev->ep_count; i++) + { + ep = &dev->eps[i]; + if (!ep->valid) + goto next_ep; + XhciRingInit(&ep->ring); + icc[1] |= 1 << ep->dci; + if (ep->dci > max_dci) + max_dci = ep->dci; + ep_ctx = XhciInputCtx(dev, 1 + ep->dci); + type = XhciEPCtxType(ep); + ival = XhciEPInterval(dev, ep); + ep_ctx[0] = ival << 16; + ep_ctx[1] = (3 << 1) | (type << 3) | (ep->max_packet << 16); + addr = ep->ring.trbs; + ep_ctx[2] = (addr & 0xFFFFFFFF) | 1; + ep_ctx[3] = addr >> 32; + if ((ep->attr & 3) == USB_EP_TYPE_INT) + ep_ctx[4] = ep->max_packet | (ep->max_packet << 16); + else + ep_ctx[4] = 1024; +next_ep:; + } + slot_ctx = XhciInputCtx(dev, 1); + MemCopy(slot_ctx, XhciDevCtxGet(dev, 0), usb_xhci.ctx_size); + slot_ctx[0] = (slot_ctx[0] & 0x07FFFFFF) | (max_dci << 27); + addr = dev->in_ctx; + + return XhciCmd(addr & 0xFFFFFFFF, addr >> 32, 0, + (XHCI_TRB_CONFIG_EP << 10) | (dev->slot << 24)); +} + +I64 XhciResetEP(CUsbDev *dev, CUsbEP *ep) +{//Recover a halted endpoint: Reset EP + Set TR Dequeue to fresh ring. + I64 code, addr; + + code = XhciCmd(0, 0, 0, (XHCI_TRB_RESET_EP << 10) | (ep->dci << 16) | (dev->slot << 24)); + XhciRingInit(&ep->ring); + addr = ep->ring.trbs; + code = XhciCmd((addr & 0xFFFFFFFF) | 1, addr >> 32, 0, + (XHCI_TRB_SET_TR_DEQ << 10) | (ep->dci << 16) | (dev->slot << 24)); + + return code; +} + +U0 XhciBiosHandoff() +{ + I64 hcc = XhciCapR32(XHCI_CAP_HCCPARAMS1), xecp, id, next; + U32 *cap; + F64 timeout; + + xecp = ((hcc >> 16) & 0xFFFF) * 4; + while (xecp) + { + cap = usb_xhci.mmio + xecp; + id = *cap & 0xFF; + if (id == 1) + {//USB Legacy Support: claim OS ownership + *cap = *cap | (1 << 24); + timeout = tS + 1.0; + while ((*cap & (1 << 16)) && tS < timeout) + Sleep(10); + break; + } + next = (*cap >> 8) & 0xFF; + if (!next) + break; + xecp += next * 4; + } +} + +Bool XhciPortReset(I64 port) +{//Reset root port, wait for enabled. Returns TRUE if port enabled. + I64 sc; + F64 timeout; + + XhciPortSCWrite(port, XHCI_PORTSC_PP | XHCI_PORTSC_PR); + timeout = tS + 0.5; + while (tS < timeout) + { + sc = XhciPortSC(port); + if (sc & XHCI_PORTSC_PED) + break; + Sleep(10); + } +//Clear change bits + XhciPortSCWrite(port, XHCI_PORTSC_PP | XHCI_PORTSC_CHANGE_BITS); + sc = XhciPortSC(port); + + return ToBool(sc & XHCI_PORTSC_PED); +} + +Bool XhciInit() +{//Find + init first xHCI controller. Returns TRUE on success. + I64 bdf, bar_lo, bar_hi, addr, i; + I64 hcs1, hcs2, hcc, cmd; + F64 timeout; + U8 *page; + + bdf = PCIClassFind(XHCI_PCI_CLASS, 0); + if (bdf == -1) + { + "xHCI: no controller found.\n"; + return FALSE; + } + usb_xhci.pci_bus = bdf.u8[2]; + usb_xhci.pci_dev = bdf.u8[1]; + usb_xhci.pci_fun = bdf.u8[0]; + + bar_lo = PCIReadU32(usb_xhci.pci_bus, usb_xhci.pci_dev, usb_xhci.pci_fun, 0x10); + bar_hi = 0; + if ((bar_lo & 6) == 4) //64-bit BAR + bar_hi = PCIReadU32(usb_xhci.pci_bus, usb_xhci.pci_dev, usb_xhci.pci_fun, 0x14); + if (bar_hi) + { + "xHCI: MMIO above 4GB not supported.\n"; + return FALSE; + } + addr = bar_lo & ~0xF; + if (!addr) + { + "xHCI: BAR not assigned.\n"; + return FALSE; + } +//Enable memory space + bus master + cmd = PCIReadU16(usb_xhci.pci_bus, usb_xhci.pci_dev, usb_xhci.pci_fun, 4); + PCIWriteU16(usb_xhci.pci_bus, usb_xhci.pci_dev, usb_xhci.pci_fun, 4, cmd | 6); + + usb_xhci.mmio = dev.uncached_alias + addr; + usb_xhci.op = usb_xhci.mmio + (XhciCapR32(XHCI_CAP_CAPLENGTH) & 0xFF); + usb_xhci.rt = usb_xhci.mmio + (XhciCapR32(XHCI_CAP_RTSOFF) & ~0x1F); + usb_xhci.db = usb_xhci.mmio + (XhciCapR32(XHCI_CAP_DBOFF) & ~3); + + hcs1 = XhciCapR32(XHCI_CAP_HCSPARAMS1); + hcs2 = XhciCapR32(XHCI_CAP_HCSPARAMS2); + hcc = XhciCapR32(XHCI_CAP_HCCPARAMS1); + usb_xhci.max_slots = hcs1 & 0xFF; + usb_xhci.max_ports = (hcs1 >> 24) & 0xFF; + if (hcc & 4) + usb_xhci.ctx_size = 64; + else + usb_xhci.ctx_size = 32; + + XhciBiosHandoff; + +//Stop + reset controller + XhciOpW32(XHCI_OP_USBCMD, XhciOpR32(XHCI_OP_USBCMD) & ~XHCI_CMD_RS); + timeout = tS + 1.0; + while (!(XhciOpR32(XHCI_OP_USBSTS) & XHCI_STS_HCH) && tS < timeout) + Sleep(1); + XhciOpW32(XHCI_OP_USBCMD, XHCI_CMD_HCRST); + timeout = tS + 1.0; + while ((XhciOpR32(XHCI_OP_USBCMD) & XHCI_CMD_HCRST) && tS < timeout) + Sleep(1); + timeout = tS + 1.0; + while ((XhciOpR32(XHCI_OP_USBSTS) & XHCI_STS_CNR) && tS < timeout) + Sleep(1); + + XhciOpW32(XHCI_OP_CONFIG, usb_xhci.max_slots); + +//Device context base address array + if (!usb_xhci.dcbaa) + usb_xhci.dcbaa = CAllocAligned((usb_xhci.max_slots + 1) * 8, 0x1000, sys_task->code_heap); + else + MemSet(usb_xhci.dcbaa, 0, (usb_xhci.max_slots + 1) * 8); + +//Scratchpad buffers + usb_xhci.scratch_count = (((hcs2 >> 21) & 0x1F) << 5) | ((hcs2 >> 27) & 0x1F); + if (usb_xhci.scratch_count) + { + usb_xhci.scratch_arr = CAllocAligned(usb_xhci.scratch_count * 8, 0x1000, sys_task->code_heap); + for (i = 0; i < usb_xhci.scratch_count; i++) + { + page = CAllocAligned(0x1000, 0x1000, sys_task->code_heap); + usb_xhci.scratch_arr[i] = page; + } + usb_xhci.dcbaa[0] = usb_xhci.scratch_arr; + } + XhciOpW64(XHCI_OP_DCBAAP, usb_xhci.dcbaa); + +//Command ring + XhciRingInit(&usb_xhci.cmd_ring); + addr = usb_xhci.cmd_ring.trbs; + XhciOpW64(XHCI_OP_CRCR, addr | 1); + +//Event ring, single segment, interrupter 0 + if (!usb_xhci.evt_ring) + usb_xhci.evt_ring = CAllocAligned(XHCI_EVT_RING_TRBS * sizeof(CXhciTRB), 0x1000, sys_task->code_heap); + else + MemSet(usb_xhci.evt_ring, 0, XHCI_EVT_RING_TRBS * sizeof(CXhciTRB)); + usb_xhci.evt_deq = 0; + usb_xhci.evt_cycle = 1; + if (!usb_xhci.erst) + usb_xhci.erst = CAllocAligned(64, 64, sys_task->code_heap); + addr = usb_xhci.evt_ring; + usb_xhci.erst[0] = addr; + usb_xhci.erst[1] = XHCI_EVT_RING_TRBS; + XhciRtW32(XHCI_RT_ERSTSZ0, 1); + XhciRtW64(XHCI_RT_ERDP0, addr); + addr = usb_xhci.erst; + XhciRtW64(XHCI_RT_ERSTBA0, addr); +//Interrupts stay disabled; we poll. + +//Run + XhciOpW32(XHCI_OP_USBCMD, XhciOpR32(XHCI_OP_USBCMD) | XHCI_CMD_RS); + timeout = tS + 1.0; + while ((XhciOpR32(XHCI_OP_USBSTS) & XHCI_STS_HCH) && tS < timeout) + Sleep(1); + + usb_xhci.present = TRUE; + usb_xhci.running = TRUE; + "xHCI: %d ports, %d slots, ctx size %d, MMIO 0x%X.\n", + usb_xhci.max_ports, usb_xhci.max_slots, usb_xhci.ctx_size, bar_lo & ~0xF; + + return TRUE; +} From ace4434c12420afba69e06f1d373f037c32045f7 Mon Sep 17 00:00:00 2001 From: y4my4my4m <8145020+y4my4my4m@users.noreply.github.com> Date: Thu, 23 Jul 2026 22:31:53 +0900 Subject: [PATCH 2/5] Refactor USB endpoint handling to use auto_kind instead of auto_cb This update modifies the USB stack to replace the function pointer auto_cb with an integer auto_kind for handling HID report callbacks. Changes include: - Updated endpoint descriptor structure to use auto_kind for specifying report handler types (USB_AUTO_NONE, USB_AUTO_KBD, USB_AUTO_MOUSE). - Adjusted relevant functions to accommodate this change, enhancing clarity and maintainability of the code. This refactor streamlines the dispatching of report handlers in the USB HID implementation. --- src/System/Usb/Usb.ZC | 4 ++-- src/System/Usb/UsbDefs.ZC | 18 +++++++++++++++--- src/System/Usb/UsbHid.ZC | 4 ++-- src/System/Usb/Xhci.ZC | 18 ++++++++++++++---- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/System/Usb/Usb.ZC b/src/System/Usb/Usb.ZC index f1e96bfaa..1059884d5 100644 --- a/src/System/Usb/Usb.ZC +++ b/src/System/Usb/Usb.ZC @@ -116,7 +116,7 @@ U0 UsbParseConfig(CUsbDev *dev, U8 *buf, I64 len) if (epd->addr & 0x80) ep->dci |= 1; ep->valid = TRUE; - ep->auto_cb = NULL; + ep->auto_kind = USB_AUTO_NONE; cur_if->ep_idxs[cur_if->ep_count] = dev->ep_count; cur_if->ep_count++; dev->ep_count++; @@ -143,7 +143,7 @@ U0 UsbDevDetachCore(CUsbDev *dev) for (i = 0; i < USB_EPS_MAX; i++) { dev->eps[i].valid = FALSE; - dev->eps[i].auto_cb = NULL; + dev->eps[i].auto_kind = USB_AUTO_NONE; } dev->drv_data = NULL; } diff --git a/src/System/Usb/UsbDefs.ZC b/src/System/Usb/UsbDefs.ZC index 9ce582ee9..953ab2af3 100644 --- a/src/System/Usb/UsbDefs.ZC +++ b/src/System/Usb/UsbDefs.ZC @@ -216,6 +216,16 @@ class CUsbEPDesc #define XHCI_RING_TRBS 256 //4096 / 16 #define XHCI_EVT_RING_TRBS 256 +//Interrupt-IN auto-requeue report handler kinds (see CUsbEP.auto_kind). +#define USB_AUTO_NONE 0 +#define USB_AUTO_KBD 1 +#define USB_AUTO_MOUSE 2 + +//Forward decls: report handlers live in UsbHid.ZC (compiled after Xhci.ZC), +//but XhciEventPoll dispatches to them. Single-pass compiler needs these. +extern U0 UsbKbdCB(U8 *dev, U8 *ep); +extern U0 UsbMouseCB(U8 *dev, U8 *ep); + class CXhciTRB {//16 bytes U32 lo, hi, status, control; @@ -241,10 +251,12 @@ class CUsbEP Bool xfer_done; I64 xfer_code, //xHCI completion code xfer_residue; //untransferred bytes -//Async auto-requeue (HID interrupt IN) +//Async auto-requeue (HID interrupt IN). auto_kind selects the report handler +//(USB_AUTO_*); dispatched in XhciEventPoll. Avoids a function-pointer struct +//member (which the ZealC JIT class parser rejects) and keeps late binding. U8 *auto_buf; - I64 auto_len; - U0 (*auto_cb)(U8 *dev, U8 *ep); //really (CUsbDev*,CUsbEP*); report in auto_buf + I64 auto_len, + auto_kind; CXhciTRB *pending_trb; }; diff --git a/src/System/Usb/UsbHid.ZC b/src/System/Usb/UsbHid.ZC index 3748bab58..e2c4f8340 100644 --- a/src/System/Usb/UsbHid.ZC +++ b/src/System/Usb/UsbHid.ZC @@ -229,12 +229,12 @@ U0 UsbHidAttach(CUsbDev *dev, CUsbIf *uif) if (uif->if_proto == USB_HID_PROTO_KBD) { ep->auto_len = 8; - ep->auto_cb = &UsbKbdCB; + ep->auto_kind = USB_AUTO_KBD; SysLog("USB: HID keyboard on port %d.\n", dev->port); } else { - ep->auto_cb = &UsbMouseCB; + ep->auto_kind = USB_AUTO_MOUSE; mouse_hard.installed = TRUE; //menu bar + cursor paths check this SysLog("USB: HID mouse on port %d.\n", dev->port); } diff --git a/src/System/Usb/Xhci.ZC b/src/System/Usb/Xhci.ZC index 9aec2af24..2f9a7a895 100644 --- a/src/System/Usb/Xhci.ZC +++ b/src/System/Usb/Xhci.ZC @@ -143,7 +143,7 @@ CUsbEP *UsbDevEPByDCI(CUsbDev *dev, I64 dci) } U0 XhciIntQueue(CUsbDev *dev, CUsbEP *ep) -{//Queue one interrupt IN TRB on ep, report delivered via ep->auto_cb. +{//Queue one interrupt IN TRB on ep, report dispatched via ep->auto_kind. XhciLock; ep->pending_trb = XhciRingEnq(&ep->ring, ep->auto_buf, 0, ep->auto_len, (XHCI_TRB_NORMAL << 10) | XHCI_TRBF_IOC | XHCI_TRBF_ISP); @@ -188,10 +188,20 @@ U0 XhciEventPoll() ep = UsbDevEPByDCI(dev, dci); if (ep) { - if (ep->auto_cb) + if (ep->auto_kind) {//HID interrupt IN: deliver + requeue if (code == XHCI_CC_SUCCESS || code == XHCI_CC_SHORT_PKT) - (*ep->auto_cb)(dev, ep); + { + switch (ep->auto_kind) + { + case USB_AUTO_KBD: + UsbKbdCB(dev, ep); + break; + case USB_AUTO_MOUSE: + UsbMouseCB(dev, ep); + break; + } + } XhciIntQueue(dev, ep); } else @@ -414,7 +424,7 @@ I64 XhciAddressDev(CUsbDev *dev) XhciRingInit(&dev->ep0.ring); dev->ep0.dci = 1; dev->ep0.valid = TRUE; - dev->ep0.auto_cb = NULL; + dev->ep0.auto_kind = USB_AUTO_NONE; switch (dev->speed) { From 96b40847014bfd64ed3c252010acc6f3fcf84bae Mon Sep 17 00:00:00 2001 From: y4my4my4m <8145020+y4my4my4m@users.noreply.github.com> Date: Thu, 23 Jul 2026 22:56:22 +0900 Subject: [PATCH 3/5] Refactor CUsbEP to use pointer for CXhciRing This update modifies the CUsbEP class to store a pointer to CXhciRing instead of embedding it directly. This change addresses limitations in the ZealC JIT class parser regarding nested class structures. Additionally, new initialization functions for endpoint rings have been introduced, ensuring proper memory allocation and initialization on first use. The code has been updated accordingly to reflect these changes, enhancing the overall structure and maintainability of the USB endpoint handling. --- src/System/Usb/UsbDefs.ZC | 5 ++++- src/System/Usb/Xhci.ZC | 31 ++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/System/Usb/UsbDefs.ZC b/src/System/Usb/UsbDefs.ZC index 953ab2af3..24e624692 100644 --- a/src/System/Usb/UsbDefs.ZC +++ b/src/System/Usb/UsbDefs.ZC @@ -240,7 +240,10 @@ class CXhciRing class CUsbEP { - CXhciRing ring; +//ring is a POINTER, not embedded: CUsbEP is itself embedded by value in CUsbDev, +//and the ZealC JIT class parser rejects two-level by-value class nesting +//(CUsbDev -> CUsbEP -> CXhciRing). Keeping ring a pointer flattens CUsbEP. + CXhciRing *ring; I64 dci, //xHCI device context index addr, //USB endpoint address (with 0x80 dir bit) attr, //descriptor attributes diff --git a/src/System/Usb/Xhci.ZC b/src/System/Usb/Xhci.ZC index 2f9a7a895..5d90c6302 100644 --- a/src/System/Usb/Xhci.ZC +++ b/src/System/Usb/Xhci.ZC @@ -82,6 +82,15 @@ U0 XhciRingInit(CXhciRing *ring) ring->cycle = 1; } +CXhciRing *XhciEPRingInit(CUsbEP *ep) +{//Alloc ep's ring struct (pointer member) on first use, then (re)init it. + if (!ep->ring) + ep->ring = CAlloc(sizeof(CXhciRing), sys_task->code_heap); + XhciRingInit(ep->ring); + + return ep->ring; +} + CXhciTRB *XhciRingEnq(CXhciRing *ring, I64 lo, I64 hi, I64 status, I64 control) {//control passed without cycle bit; caller holds lock. CXhciTRB *trb = &ring->trbs[ring->enq], *link; @@ -145,7 +154,7 @@ CUsbEP *UsbDevEPByDCI(CUsbDev *dev, I64 dci) U0 XhciIntQueue(CUsbDev *dev, CUsbEP *ep) {//Queue one interrupt IN TRB on ep, report dispatched via ep->auto_kind. XhciLock; - ep->pending_trb = XhciRingEnq(&ep->ring, ep->auto_buf, 0, ep->auto_len, + ep->pending_trb = XhciRingEnq(ep->ring, ep->auto_buf, 0, ep->auto_len, (XHCI_TRB_NORMAL << 10) | XHCI_TRBF_IOC | XHCI_TRBF_ISP); usb_xhci.db[dev->slot] = ep->dci; XhciUnlock; @@ -291,7 +300,7 @@ I64 XhciCtrlXfer(CUsbDev *dev, I64 rt, I64 rq, I64 val, I64 idx, U8 *buf, I64 le ep->xfer_done = FALSE; ep->xfer_code = XHCI_CC_TIMEOUT; ep->xfer_residue = 0; - XhciRingEnq(&ep->ring, rt | (rq << 8) | (val << 16), idx | (len << 16), 8, + XhciRingEnq(ep->ring, rt | (rq << 8) | (val << 16), idx | (len << 16), 8, (XHCI_TRB_SETUP << 10) | XHCI_TRBF_IDT | trt); if (len) { @@ -299,12 +308,12 @@ I64 XhciCtrlXfer(CUsbDev *dev, I64 rt, I64 rq, I64 val, I64 idx, U8 *buf, I64 le if (rt & USB_RT_DEV_TO_HOST) ctl |= XHCI_TRBF_DIR_IN; addr = buf; - XhciRingEnq(&ep->ring, addr & 0xFFFFFFFF, addr >> 32, len, ctl); + XhciRingEnq(ep->ring, addr & 0xFFFFFFFF, addr >> 32, len, ctl); } ctl = (XHCI_TRB_STATUS << 10) | XHCI_TRBF_IOC; if (!len || !(rt & USB_RT_DEV_TO_HOST)) ctl |= XHCI_TRBF_DIR_IN; //status stage opposite dir; IN if no data - XhciRingEnq(&ep->ring, 0, 0, 0, ctl); + XhciRingEnq(ep->ring, 0, 0, 0, ctl); usb_xhci.db[dev->slot] = 1; XhciUnlock; @@ -319,7 +328,7 @@ I64 XhciBulkXfer(CUsbDev *dev, CUsbEP *ep, U8 *buf, I64 len, F64 timeout_s, I64 ep->xfer_done = FALSE; ep->xfer_code = XHCI_CC_TIMEOUT; ep->xfer_residue = 0; - XhciRingEnq(&ep->ring, addr & 0xFFFFFFFF, addr >> 32, len, + XhciRingEnq(ep->ring, addr & 0xFFFFFFFF, addr >> 32, len, (XHCI_TRB_NORMAL << 10) | XHCI_TRBF_IOC | XHCI_TRBF_ISP); usb_xhci.db[dev->slot] = ep->dci; XhciUnlock; @@ -421,7 +430,7 @@ I64 XhciAddressDev(CUsbDev *dev) dev->in_ctx = CAllocAligned(usb_xhci.ctx_size * 33, 0x1000, sys_task->code_heap); else MemSet(dev->in_ctx, 0, usb_xhci.ctx_size * 33); - XhciRingInit(&dev->ep0.ring); + XhciEPRingInit(&dev->ep0); dev->ep0.dci = 1; dev->ep0.valid = TRUE; dev->ep0.auto_kind = USB_AUTO_NONE; @@ -448,7 +457,7 @@ I64 XhciAddressDev(CUsbDev *dev) slot_ctx[1] = (dev->port + 1) << 16; ep0_ctx = XhciInputCtx(dev, 2); ep0_ctx[1] = (3 << 1) | (XHCI_EP_TYPE_CTRL << 3) | (mps << 16); - addr = dev->ep0.ring.trbs; + addr = dev->ep0.ring->trbs; ep0_ctx[2] = (addr & 0xFFFFFFFF) | 1; //DCS=1 ep0_ctx[3] = addr >> 32; ep0_ctx[4] = 8; @@ -494,7 +503,7 @@ I64 XhciConfigureEPs(CUsbDev *dev) ep = &dev->eps[i]; if (!ep->valid) goto next_ep; - XhciRingInit(&ep->ring); + XhciEPRingInit(ep); icc[1] |= 1 << ep->dci; if (ep->dci > max_dci) max_dci = ep->dci; @@ -503,7 +512,7 @@ I64 XhciConfigureEPs(CUsbDev *dev) ival = XhciEPInterval(dev, ep); ep_ctx[0] = ival << 16; ep_ctx[1] = (3 << 1) | (type << 3) | (ep->max_packet << 16); - addr = ep->ring.trbs; + addr = ep->ring->trbs; ep_ctx[2] = (addr & 0xFFFFFFFF) | 1; ep_ctx[3] = addr >> 32; if ((ep->attr & 3) == USB_EP_TYPE_INT) @@ -526,8 +535,8 @@ I64 XhciResetEP(CUsbDev *dev, CUsbEP *ep) I64 code, addr; code = XhciCmd(0, 0, 0, (XHCI_TRB_RESET_EP << 10) | (ep->dci << 16) | (dev->slot << 24)); - XhciRingInit(&ep->ring); - addr = ep->ring.trbs; + XhciEPRingInit(ep); + addr = ep->ring->trbs; code = XhciCmd((addr & 0xFFFFFFFF) | 1, addr >> 32, 0, (XHCI_TRB_SET_TR_DEQ << 10) | (ep->dci << 16) | (dev->slot << 24)); From bc2b7ba3308a6565bdd32f33c3d55b66cbc53679 Mon Sep 17 00:00:00 2001 From: y4my4my4m <8145020+y4my4my4m@users.noreply.github.com> Date: Thu, 23 Jul 2026 23:14:07 +0900 Subject: [PATCH 4/5] Refactor USB endpoint management to use ep_list instead of eps This update modifies the USB device structure and related functions to replace the eps array with ep_list for managing USB endpoints. Changes include updating references in the parsing, attachment, and configuration functions to ensure consistency and improve code clarity. This refactor enhances maintainability and aligns with recent structural changes in the USB stack. --- src/System/Usb/Usb.ZC | 6 +++--- src/System/Usb/UsbDefs.ZC | 2 +- src/System/Usb/UsbHid.ZC | 2 +- src/System/Usb/UsbMsd.ZC | 2 +- src/System/Usb/Xhci.ZC | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/System/Usb/Usb.ZC b/src/System/Usb/Usb.ZC index 1059884d5..69c1033cd 100644 --- a/src/System/Usb/Usb.ZC +++ b/src/System/Usb/Usb.ZC @@ -105,7 +105,7 @@ U0 UsbParseConfig(CUsbDev *dev, U8 *buf, I64 len) ttype = epd->attr & 3; if (ttype == USB_EP_TYPE_BULK || ttype == USB_EP_TYPE_INT) { - ep = &dev->eps[dev->ep_count]; + ep = &dev->ep_list[dev->ep_count]; //Clear all fields but keep any prior ring allocation (ring is first member). MemSet(&ep->dci, 0, sizeof(CUsbEP) - offset(CUsbEP.dci)); ep->addr = epd->addr; @@ -142,8 +142,8 @@ U0 UsbDevDetachCore(CUsbDev *dev) dev->ep0.valid = FALSE; for (i = 0; i < USB_EPS_MAX; i++) { - dev->eps[i].valid = FALSE; - dev->eps[i].auto_kind = USB_AUTO_NONE; + dev->ep_list[i].valid = FALSE; + dev->ep_list[i].auto_kind = USB_AUTO_NONE; } dev->drv_data = NULL; } diff --git a/src/System/Usb/UsbDefs.ZC b/src/System/Usb/UsbDefs.ZC index 24e624692..1cb8d4a0a 100644 --- a/src/System/Usb/UsbDefs.ZC +++ b/src/System/Usb/UsbDefs.ZC @@ -280,7 +280,7 @@ class CUsbDev U8 *dev_ctx, *in_ctx; CUsbEP ep0; - CUsbEP eps[USB_EPS_MAX]; + CUsbEP ep_list[USB_EPS_MAX]; I64 ep_count; CUsbIf ifs[USB_IFS_MAX]; I64 if_count; diff --git a/src/System/Usb/UsbHid.ZC b/src/System/Usb/UsbHid.ZC index e2c4f8340..df8861893 100644 --- a/src/System/Usb/UsbHid.ZC +++ b/src/System/Usb/UsbHid.ZC @@ -190,7 +190,7 @@ U0 UsbHidAttach(CUsbDev *dev, CUsbIf *uif) for (i = 0; i < uif->ep_count; i++) { - tmp = &dev->eps[uif->ep_idxs[i]]; + tmp = &dev->ep_list[uif->ep_idxs[i]]; if ((tmp->addr & 0x80) && (tmp->attr & 3) == USB_EP_TYPE_INT) { ep = tmp; diff --git a/src/System/Usb/UsbMsd.ZC b/src/System/Usb/UsbMsd.ZC index 4bc404bed..0d15c1aa3 100644 --- a/src/System/Usb/UsbMsd.ZC +++ b/src/System/Usb/UsbMsd.ZC @@ -291,7 +291,7 @@ U0 UsbMsdAttach(CUsbDev *dev, CUsbIf *uif) msd->if_num = uif->if_num; for (i = 0; i < uif->ep_count; i++) { - ep = &dev->eps[uif->ep_idxs[i]]; + ep = &dev->ep_list[uif->ep_idxs[i]]; if ((ep->attr & 3) == USB_EP_TYPE_BULK) { if (ep->addr & 0x80) diff --git a/src/System/Usb/Xhci.ZC b/src/System/Usb/Xhci.ZC index 5d90c6302..afb5655eb 100644 --- a/src/System/Usb/Xhci.ZC +++ b/src/System/Usb/Xhci.ZC @@ -145,8 +145,8 @@ CUsbEP *UsbDevEPByDCI(CUsbDev *dev, I64 dci) if (dci == 1) return &dev->ep0; for (i = 0; i < dev->ep_count; i++) - if (dev->eps[i].valid && dev->eps[i].dci == dci) - return &dev->eps[i]; + if (dev->ep_list[i].valid && dev->ep_list[i].dci == dci) + return &dev->ep_list[i]; return NULL; } @@ -500,7 +500,7 @@ I64 XhciConfigureEPs(CUsbDev *dev) icc[1] = 1; //A0 = slot for (i = 0; i < dev->ep_count; i++) { - ep = &dev->eps[i]; + ep = &dev->ep_list[i]; if (!ep->valid) goto next_ep; XhciEPRingInit(ep); From 2f69b00603af5ea24eb843e29914645fe1e6c490 Mon Sep 17 00:00:00 2001 From: y4my4my4m <8145020+y4my4my4m@users.noreply.github.com> Date: Fri, 24 Jul 2026 00:19:14 +0900 Subject: [PATCH 5/5] Fix Mounting USB Drives --- src/Kernel/BlkDev/DiskDrive.ZC | 2 +- src/Kernel/KDefine.ZC | 3 ++- src/System/Usb/UsbHid.ZC | 36 +++++++++++++++++++++++----------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/Kernel/BlkDev/DiskDrive.ZC b/src/Kernel/BlkDev/DiskDrive.ZC index 4975522da..55ff72c9d 100755 --- a/src/Kernel/BlkDev/DiskDrive.ZC +++ b/src/Kernel/BlkDev/DiskDrive.ZC @@ -349,7 +349,7 @@ U8 *DriveModelNum(U8 drv_let=0) return res; } -U8 blkdev_text_attr[BDT_TYPES_NUM] = {BLACK, LTCYAN, WHITE, LTGREEN, LTRED, LTBLUE}; +U8 blkdev_text_attr[BDT_TYPES_NUM] = {BLACK, LTCYAN, WHITE, LTGREEN, LTRED, LTBLUE, LTPURPLE}; U8 drv_text_attr[3] = {BLACK, BLUE, RED}; U8 DriveTextAttrGet(U8 drv_let=0) diff --git a/src/Kernel/KDefine.ZC b/src/Kernel/KDefine.ZC index 235b628cc..2e5c56192 100755 --- a/src/Kernel/KDefine.ZC +++ b/src/Kernel/KDefine.ZC @@ -215,7 +215,8 @@ U0 SysDefinesLoad() "ATA\0" "FILE_READ\0" "FILE_WRITE\0" - "ATAPI\0"); + "ATAPI\0" + "USB\0"); DefineListLoad("ST_DRIVE_TYPES", "NULL\0" diff --git a/src/System/Usb/UsbHid.ZC b/src/System/Usb/UsbHid.ZC index df8861893..89880211e 100644 --- a/src/System/Usb/UsbHid.ZC +++ b/src/System/Usb/UsbHid.ZC @@ -20,6 +20,9 @@ class CUsbHid CUsbHid usb_hids[USB_HIDS_MAX]; +//Absolute cursor position maintained by the USB mouse (pixels). +I64 usb_mouse_x = 0, usb_mouse_y = 0; + //HID usage (page 7) -> set-1 scancode. 0x100 flag = 0xE0-prefixed. I64 usb_hid_to_sc[0x66] = { 0, 0, 0, 0, 0x1E, 0x30, 0x2E, 0x20, //00-07: ---- a b c d @@ -151,16 +154,22 @@ U0 UsbMouseCB(U8 *d, U8 *e) dz -= 256; } - mouse_hard.timestamp = TSCGet; - MouseHardSetPre; - mouse_hard.bttns[0] = rep[0] & 1; - mouse_hard.bttns[1] = (rep[0] & 2) >> 1; - mouse_hard.bttns[2] = (rep[0] & 4) >> 2; - mouse_hard.prescale.x += dx; - mouse_hard.prescale.y += dy; - mouse_hard.prescale.z += dz; - mouse.show = TRUE; - MouseHardSetPost; +//Drive the cursor directly. The kernel prescale/MouseHardSetPost path is meant +//for the PS/2 handler and gets clobbered by WinMgr's per-frame PS/2 polling; +//we own an absolute position and write mouse_hard.pos ourselves. + usb_mouse_x = ClampI64(usb_mouse_x + dx, 0, sys_framebuffer_width - 1); + usb_mouse_y = ClampI64(usb_mouse_y + dy, 0, sys_framebuffer_height - 1); + + mouse_hard.timestamp = TSCGet; + MemCopy(&mouse_hard_last, &mouse_hard, sizeof(CMouseHardStateGlobals)); + mouse_hard.pos.x = usb_mouse_x; + mouse_hard.pos.y = usb_mouse_y; + mouse_hard.pos.z += dz; + mouse_hard.bttns[0] = rep[0] & 1; + mouse_hard.bttns[1] = (rep[0] & 2) >> 1; + mouse_hard.bttns[2] = (rep[0] & 4) >> 2; + mouse_hard.event = TRUE; + mouse.show = TRUE; } U0 UsbHidTick() @@ -235,7 +244,12 @@ U0 UsbHidAttach(CUsbDev *dev, CUsbIf *uif) else { ep->auto_kind = USB_AUTO_MOUSE; - mouse_hard.installed = TRUE; //menu bar + cursor paths check this +//Own the cursor: mark installed so WinMouseUpdate consumes our events, and +//irqs_working so WinMgr does NOT poll (and clobber) the PS/2 mouse state. + mouse_hard.installed = TRUE; + mouse_hard.irqs_working = TRUE; + usb_mouse_x = sys_framebuffer_width >> 1; + usb_mouse_y = sys_framebuffer_height >> 1; SysLog("USB: HID mouse on port %d.\n", dev->port); } XhciIntQueue(dev, ep);