diff --git a/iop/usb/usbd_mini/Makefile b/iop/usb/usbd_mini/Makefile index 1e2e7e713969..88638e0d7cb1 100644 --- a/iop/usb/usbd_mini/Makefile +++ b/iop/usb/usbd_mini/Makefile @@ -6,9 +6,35 @@ # Licenced under Academic Free License version 2.0 # Review ps2sdk README & LICENSE files for further details. -IOP_SRC_DIR = $(PS2SDKSRC)/iop/usb/usbd/src/ +# usbd_mini: FreeUsbd implementation (pre-b1f7ff96), not the SCE rewrite in ../usbd. +# See README.md / ps2sdk issue #908. + +IOP_PREFER_GPOPT = 16384 IOP_CFLAGS += -DMINI_DRIVER -IOP_IMPORT_INCS += usb/usbd/ -include $(PS2SDKSRC)/iop/usb/usbd/Makefile +IOP_IMPORT_INCS += \ + system/intrman \ + system/loadcore \ + system/stdio \ + system/sysclib \ + system/sysmem \ + system/threadman \ + usb/usbd + +IOP_OBJS = \ + hcd.o \ + hub.o \ + interface.o \ + mem.o \ + usbd.o \ + usbio.o \ + driver.o \ + usbd_v12_stubs.o \ + imports.o \ + exports.o + +include $(PS2SDKSRC)/Defs.make +include $(PS2SDKSRC)/iop/Rules.bin.make +include $(PS2SDKSRC)/iop/Rules.make +include $(PS2SDKSRC)/iop/Rules.release diff --git a/iop/usb/usbd_mini/README.md b/iop/usb/usbd_mini/README.md new file mode 100644 index 000000000000..7082c9d06a10 --- /dev/null +++ b/iop/usb/usbd_mini/README.md @@ -0,0 +1,16 @@ +# usbd_mini — FreeUsbd (pre-rewrite) + +This module is built from the **FreeUsbd** sources last known good before +ps2sdk commit `b1f7ff96` (“USBD feature update”, 2024-09-04), i.e. tree +`2dc6b32f`. + +The full `iop/usb/usbd` tree remains the SCE rewrite (usbd 1.2 with HID report +descriptors and multi-isochronous). `usbd_mini` is what OPL and other BDM USB +loaders embed; restoring FreeUsbd here fixes Crash Bandicoot: Wrath of Cortex +over USB on hardware while leaving the rewrite available for other consumers. + +Export table stays **usbd 1.2** with stubs for `usbdReboot`, +`sceUsbdGetReportDescriptor`, and `sceUsbdMultiIsochronousTransfer` +(`USB_RC_NOSUPPORT` / no-op). Mass storage does not use those entry points. + +See: https://github.com/ps2dev/ps2sdk/issues/908 diff --git a/iop/usb/usbd_mini/src/driver.c b/iop/usb/usbd_mini/src/driver.c new file mode 100644 index 000000000000..86230293dfd1 --- /dev/null +++ b/iop/usb/usbd_mini/src/driver.c @@ -0,0 +1,283 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#include "usbdpriv.h" +#include "driver.h" +#include "mem.h" +#include "hub.h" + +#include "defs.h" +#include "stdio.h" +#include "sysclib.h" +#include "thbase.h" +#include "thevent.h" +#include "intrman.h" + +sceUsbdLddOps *drvListStart = NULL, *drvListEnd = NULL; +sceUsbdLddOps *drvAutoLoader = NULL; +IoRequest *cbListStart = NULL, *cbListEnd = NULL; + +int callbackEvent; +int callbackTid = -1; + +int callUsbDriverFunc(int (*func)(int devId), int devId, void *gp) +{ + int res; + + if (func) { + usbdUnlock(); +#if USE_GP_REGISTER + ChangeGP(gp); +#else + (void)gp; +#endif + res = func(devId); +#if USE_GP_REGISTER + SetGP(&_gp); +#endif + usbdLock(); + return res; + } else + return 0; +} + +void probeDeviceTree(Device *tree, sceUsbdLddOps *drv) +{ + Device *curDevice; + for (curDevice = tree->childListStart; curDevice != NULL; curDevice = curDevice->next) + if (curDevice->deviceStatus == DEVICE_READY) { + if (curDevice->devDriver == NULL) { + if (callUsbDriverFunc(drv->probe, curDevice->id, drv->gp) != 0) { + curDevice->devDriver = drv; + callUsbDriverFunc(drv->connect, curDevice->id, drv->gp); + } + } else if (curDevice->childListStart) + probeDeviceTree(curDevice, drv); + } +} + +int doRegisterDriver(sceUsbdLddOps *drv, void *drvGpSeg) +{ + if (drv->next || drv->prev) + return USB_RC_BUSY; + if (drvListStart == drv) + return USB_RC_BUSY; + + if (!drv->name) + return USB_RC_BADDRIVER; + if (drv->reserved1 || drv->reserved2) + return USB_RC_BADDRIVER; + + drv->gp = drvGpSeg; + + drv->prev = drvListEnd; + if (drvListEnd) + drvListEnd->next = drv; + else + drvListStart = drv; + drvListEnd = drv; + + if (drv->probe) + probeDeviceTree(memPool.deviceTreeRoot, drv); + + return 0; +} + +int doRegisterAutoLoader(sceUsbdLddOps *drv, void *drvGpSeg) +{ + if (drv->next || drv->prev) + return USB_RC_BADDRIVER; + if (!drv->name) + return USB_RC_BADDRIVER; + if (drv->reserved1 || drv->reserved2) + return USB_RC_BADDRIVER; + + if (drvAutoLoader != NULL) + return USB_RC_BUSY; + + drv->gp = drvGpSeg; + + drvAutoLoader = drv; + + if (drv->probe) + probeDeviceTree(memPool.deviceTreeRoot, drv); + + return 0; +} + +void disconnectDriver(Device *tree, sceUsbdLddOps *drv) +{ + Endpoint *ep, *nextEp; + if (tree->devDriver == drv) { + if (tree->endpointListStart) { + ep = tree->endpointListStart->next; + + while (ep) { + nextEp = ep->next; + removeEndpointFromDevice(tree, ep); + ep = nextEp; + } + } + tree->devDriver = NULL; + tree->privDataField = NULL; + } + + for (tree = tree->childListStart; tree != NULL; tree = tree->next) + disconnectDriver(tree, drv); +} + +int doUnregisterAutoLoader(void) +{ + drvAutoLoader = NULL; + return 0; +} + +int doUnregisterDriver(sceUsbdLddOps *drv) +{ + sceUsbdLddOps *pos; + for (pos = drvListStart; pos != NULL; pos = pos->next) + if (pos == drv) { + if (drv->next) + drv->next->prev = drv->prev; + else + drvListEnd = drv->prev; + + if (drv->prev) + drv->prev->next = drv->next; + else + drvListStart = drv->next; + + disconnectDriver(memPool.deviceTreeRoot, drv); + return 0; + } + return USB_RC_BADDRIVER; +} + +void connectNewDevice(Device *dev) +{ + sceUsbdLddOps *drv; + dbg_printf("searching driver for dev %d, FA %02X\n", dev->id, dev->functionAddress); + for (drv = drvListStart; drv != NULL; drv = drv->next) + if (callUsbDriverFunc(drv->probe, dev->id, drv->gp) != 0) { + dev->devDriver = drv; + dbg_printf("Driver found (%s)\n", drv->name); + callUsbDriverFunc(drv->connect, dev->id, drv->gp); + return; + } + + // No driver found yet. Call autoloader. + if (drvAutoLoader != NULL) { + drv = drvAutoLoader; + + if (callUsbDriverFunc(drv->probe, dev->id, drv->gp) != 0) { + dev->devDriver = drv; + dbg_printf("(autoloader) Driver found (%s)\n", drv->name); + callUsbDriverFunc(drv->connect, dev->id, drv->gp); + return; + } + } + + dbg_printf("no driver found\n"); +} + +void signalCallbackThreadFunc(IoRequest *req) +{ + int intrStat; + + CpuSuspendIntr(&intrStat); + + req->prev = cbListEnd; + req->next = NULL; + if (cbListEnd) + cbListEnd->next = req; + else + cbListStart = req; + cbListEnd = req; + + CpuResumeIntr(intrStat); + + SetEventFlag(callbackEvent, 1); +} + +void callbackThreadFunc(void *arg) +{ + u32 eventRes; + int intrStat; + IoRequest *req; + IoRequest reqCopy; + + (void)arg; + + while (1) { + WaitEventFlag(callbackEvent, 1, WEF_CLEAR | WEF_OR, &eventRes); + do { + CpuSuspendIntr(&intrStat); + + req = cbListStart; + if (req) { + if (req->next) + req->next->prev = req->prev; + else + cbListEnd = req->prev; + + if (req->prev) + req->prev->next = req->next; + else + cbListStart = req->next; + } + CpuResumeIntr(intrStat); + + if (req) { + memcpy(&reqCopy, req, sizeof(IoRequest)); + usbdLock(); + freeIoRequest(req); + usbdUnlock(); + + if (reqCopy.userCallbackProc) { +#if USE_GP_REGISTER + SetGP(req->gpSeg); +#endif + reqCopy.userCallbackProc(reqCopy.resultCode, reqCopy.transferedBytes, reqCopy.userCallbackArg); +#if USE_GP_REGISTER + SetGP(&_gp); +#endif + } + } + } while (req); + } +} + +int initCallbackThread(void) +{ + iop_event_t event; + iop_thread_t thread; + + event.attr = event.option = event.bits = 0; + callbackEvent = CreateEventFlag(&event); + + thread.attr = TH_C; + thread.option = 0; + thread.thread = callbackThreadFunc; +#ifndef MINI_DRIVER + thread.stacksize = 0x4000; // 16KiB +#else + thread.stacksize = 0x0800; // 2KiB +#endif + thread.priority = usbConfig.cbThreadPrio; + callbackTid = CreateThread(&thread); + StartThread(callbackTid, NULL); + + return 0; +} diff --git a/iop/usb/usbd_mini/src/driver.h b/iop/usb/usbd_mini/src/driver.h new file mode 100644 index 000000000000..4cf5d736d0b3 --- /dev/null +++ b/iop/usb/usbd_mini/src/driver.h @@ -0,0 +1,31 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#ifndef __DRIVER_H__ +#define __DRIVER_H__ + +#include "usbdpriv.h" + +int callUsbDriverFunc(int (*func)(int devId), int devId, void *gp); +int doRegisterDriver(sceUsbdLddOps *drv, void *drvGpSeg); +int doRegisterAutoLoader(sceUsbdLddOps *drv, void *drvGpSeg); +int doUnregisterAutoLoader(void); +int doUnregisterDriver(sceUsbdLddOps *drv); +void signalCallbackThreadFunc(IoRequest *req); +void callbackThreadFunc(void *arg); +void connectNewDevice(Device *dev); +int initCallbackThread(void); + +#endif //__DRIVER_H__ diff --git a/iop/usb/usbd_mini/src/exports.tab b/iop/usb/usbd_mini/src/exports.tab new file mode 100644 index 000000000000..39ac4d3aebed --- /dev/null +++ b/iop/usb/usbd_mini/src/exports.tab @@ -0,0 +1,25 @@ +/**/ + +DECLARE_EXPORT_TABLE(usbd, 1, 2) + DECLARE_EXPORT(_start) + DECLARE_EXPORT(_retonly) + DECLARE_EXPORT(usbdReboot) + DECLARE_EXPORT(_retonly) + DECLARE_EXPORT(sceUsbdRegisterLdd) +/*05*/ DECLARE_EXPORT(sceUsbdUnregisterLdd) + DECLARE_EXPORT(sceUsbdScanStaticDescriptor) + DECLARE_EXPORT(sceUsbdSetPrivateData) + DECLARE_EXPORT(sceUsbdGetPrivateData) + DECLARE_EXPORT(sceUsbdOpenPipe) +/*10*/ DECLARE_EXPORT(sceUsbdClosePipe) + DECLARE_EXPORT(sceUsbdTransferPipe) + DECLARE_EXPORT(sceUsbdOpenPipeAligned) + DECLARE_EXPORT(sceUsbdGetDeviceLocation) + DECLARE_EXPORT(sceUsbdRegisterAutoloader) +/*15*/ DECLARE_EXPORT(sceUsbdUnregisterAutoloader) + DECLARE_EXPORT(sceUsbdChangeThreadPriority) + DECLARE_EXPORT(sceUsbdGetReportDescriptor) + DECLARE_EXPORT(sceUsbdMultiIsochronousTransfer) +END_EXPORT_TABLE + +void _retonly() {} diff --git a/iop/usb/usbd_mini/src/hcd.c b/iop/usb/usbd_mini/src/hcd.c new file mode 100644 index 000000000000..a312b987493a --- /dev/null +++ b/iop/usb/usbd_mini/src/hcd.c @@ -0,0 +1,544 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#include "usbdpriv.h" +#include "mem.h" +#include "usbio.h" +#include "hub.h" +#include "driver.h" + +#include "stdio.h" +#include "sysclib.h" +#include "sysmem.h" +#include "thbase.h" +#include "thevent.h" +#include "thsemap.h" +#include "intrman.h" + +int hcdIrqEvent; +int hcdTid; + +int cleanUpFunc(Device *dev, Endpoint *ep) +{ + if (!ep) + return 0; + + if ((ep < memPool.endpointBuf) || (ep >= memPool.endpointBuf + usbConfig.maxEndpoints)) + return 0; + + if (ep->inTdQueue) + removeEndpointFromQueue(ep); + + if (ep->next) + ep->next->prev = ep->prev; + else + dev->endpointListEnd = ep->prev; + + if (ep->prev) + ep->prev->next = ep->next; + else + dev->endpointListStart = ep->next; + + ep->correspDevice = NULL; + ep->next = NULL; + ep->prev = memPool.freeEpListEnd; + if (memPool.freeEpListEnd) + memPool.freeEpListEnd->next = ep; + else + memPool.freeEpListStart = ep; + memPool.freeEpListEnd = ep; + return 0; +} + +Endpoint *openDeviceEndpoint(Device *dev, UsbEndpointDescriptor *endpDesc, u32 alignFlag) +{ + + u16 flags = 0; + u16 hcMaxPktSize; + u8 endpType = 0; + u8 type; + HcTD *td = NULL; + + Endpoint *newEp = allocEndpointForDevice(dev, alignFlag); + + if (!newEp) { + dbg_printf("ran out of endpoints\n"); + return NULL; + } + + if (endpDesc) { + type = endpDesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + hcMaxPktSize = (endpDesc->wMaxPacketSizeHB << 8) | endpDesc->wMaxPacketSizeLB; + + if (type == USB_ENDPOINT_XFER_ISOC) { + endpType = TYPE_ISOCHRON; + td = (HcTD *)allocIsoTd(); + if (!td) { + cleanUpFunc(dev, newEp); + dbg_printf("Open ISOC EP: no TDs left\n"); + return NULL; + } + } else if (type == USB_ENDPOINT_XFER_CONTROL) { + endpType = TYPE_CONTROL; + if (!alignFlag) + if (hcMaxPktSize >= 0x3F) + hcMaxPktSize = 0x3E; + } else { // BULK or INT + if (type == USB_ENDPOINT_XFER_INT) { + if (endpDesc->bInterval >= 0x20) + endpType = 0x1F; + else if (endpDesc->bInterval >= 0x10) + endpType = 0xF; + else if (endpDesc->bInterval >= 8) + endpType = 7; + else if (endpDesc->bInterval >= 4) + endpType = 3; + else if (endpDesc->bInterval >= 2) + endpType = 1; + else + endpType = 0; + + // todo: add bandwidth scheduling + + dbg_printf("opening INT endpoint (%d - %p), interval %d, list %d\n", newEp->id, newEp, endpDesc->bInterval, endpType); + } else + endpType = TYPE_BULK; + + if (((endpDesc->bEndpointAddress & USB_DIR_IN) == 0) && !alignFlag) + if (hcMaxPktSize >= 0x3F) + hcMaxPktSize = 0x3E; + } + + hcMaxPktSize &= 0x7FF; + if (type == USB_ENDPOINT_XFER_ISOC) + flags |= HCED_ISOC; + if (dev->isLowSpeedDevice) + flags |= HCED_SPEED; + + flags |= (endpDesc->bEndpointAddress & 0xF) << 7; + + if (endpDesc->bEndpointAddress & USB_DIR_IN) + flags |= HCED_DIR_IN; + else + flags |= HCED_DIR_OUT; + + flags |= dev->functionAddress & 0x7F; + + newEp->hcEd.hcArea = flags; + newEp->hcEd.maxPacketSize = hcMaxPktSize; + } else { + newEp->hcEd.maxPacketSize = 8; + if (dev->isLowSpeedDevice) + newEp->hcEd.hcArea = HCED_SPEED; + else + newEp->hcEd.hcArea = 0; + endpType = TYPE_CONTROL; + } + + newEp->endpointType = endpType; + if (!td) { + td = allocTd(); + if (!td) { + dbg_printf("Ran out of TDs\n"); + cleanUpFunc(dev, newEp); + return NULL; + } + } + newEp->hcEd.tdHead = newEp->hcEd.tdTail = td; + addToHcEndpointList(newEp->endpointType, &newEp->hcEd); + return newEp; +} + +Endpoint *doOpenEndpoint(Device *dev, UsbEndpointDescriptor *endpDesc, u32 alignFlag) +{ + if (!dev->parent) + return NULL; + + if (endpDesc == NULL) + return dev->endpointListStart; // default control EP was already opened + else + return openDeviceEndpoint(dev, endpDesc, alignFlag); +} + +int doCloseEndpoint(Endpoint *ep) +{ + Device *dev = ep->correspDevice; + + if (dev->endpointListStart != ep) + return removeEndpointFromDevice(dev, ep); + else + return 0; +} + +void *doGetDeviceStaticDescriptor(int devId, void *data, u8 type) +{ + UsbDeviceDescriptor *descBuf; + Device *dev = fetchDeviceById(devId); + if (!dev) + return NULL; + + if (data) + descBuf = (UsbDeviceDescriptor *)((u8 *)data + ((UsbDeviceDescriptor *)data)->bLength); + else + descBuf = (UsbDeviceDescriptor *)dev->staticDeviceDescPtr; + + if (type == 0) + return descBuf; + + while (((u8 *)descBuf < (u8 *)dev->staticDeviceDescEndPtr) && (descBuf->bLength >= 2)) { + if (descBuf->bDescriptorType == type) + return descBuf; + descBuf = (UsbDeviceDescriptor *)((u8 *)descBuf + descBuf->bLength); + } + return NULL; +} + +void handleRhsc(void) +{ + u32 portNum = 0; + Device *port = memPool.deviceTreeRoot->childListStart; + + while (port) { + u32 status = memPool.ohciRegs->HcRhPortStatus[portNum]; + memPool.ohciRegs->HcRhPortStatus[portNum] = C_PORT_FLAGS; // reset all flags + if (status & BIT(PORT_CONNECTION)) { + if ((port->deviceStatus != DEVICE_NOTCONNECTED) && (status & BIT(C_PORT_CONNECTION))) + flushPort(port); + + if (port->deviceStatus == DEVICE_NOTCONNECTED) { + port->deviceStatus = DEVICE_CONNECTED; + addTimerCallback(&port->timer, (TimerCallback)hubResetDevice, port, 500); + } else if (port->deviceStatus == DEVICE_RESETPENDING) { + if (!(status & BIT(PORT_RESET))) { + port->deviceStatus = DEVICE_RESETCOMPLETE; + port->isLowSpeedDevice = (status >> PORT_LOW_SPEED) & 1; + Endpoint *ep = openDeviceEndpoint(port, NULL, 0); + if (ep) + hubTimedSetFuncAddress(port); + } + } + } else + flushPort(port); + port = port->next; + portNum++; + } +} + +void hcdProcessIntr(void) +{ + u32 intrFlags; + + intrFlags = memPool.ohciRegs->HcInterruptStatus & memPool.ohciRegs->HcInterruptEnable; + + if (intrFlags & OHCI_INT_SO) { + dbg_printf("HC: Scheduling overrun\n"); + memPool.ohciRegs->HcInterruptStatus = OHCI_INT_SO; + intrFlags &= ~OHCI_INT_SO; + } + + HcTD *doneQueue = (HcTD *)((u32)memPool.hcHCCA->DoneHead & ~0xF); + if (doneQueue) { + memPool.hcHCCA->DoneHead = NULL; + memPool.ohciRegs->HcInterruptStatus = OHCI_INT_WDH; + + // reverse queue + HcTD *prev = NULL; + do { + HcTD *tmp = doneQueue; + doneQueue = tmp->next; + tmp->next = prev; + prev = tmp; + } while (doneQueue); + + do { + HcTD *tmp = prev->next; + if ((prev >= memPool.hcTdBuf) && (prev < memPool.hcTdBufEnd)) + processDoneQueue_GenTd(prev); + else if ((prev >= (HcTD *)memPool.hcIsoTdBuf) && (prev < (HcTD *)memPool.hcIsoTdBufEnd)) + processDoneQueue_IsoTd((HcIsoTD *)prev); + prev = tmp; + } while (prev); + + intrFlags &= ~OHCI_INT_WDH; + } + + if (intrFlags & OHCI_INT_SF) { + memPool.ohciRegs->HcInterruptStatus = OHCI_INT_SF; + handleTimerList(); + intrFlags &= ~OHCI_INT_SF; + } + + if (intrFlags & OHCI_INT_UE) { + printf("HC: Unrecoverable error\n"); + memPool.ohciRegs->HcInterruptStatus = OHCI_INT_UE; + intrFlags &= ~OHCI_INT_UE; + } + + if (intrFlags & OHCI_INT_RHSC) { + dbg_printf("RHSC\n"); + memPool.ohciRegs->HcInterruptStatus = OHCI_INT_RHSC; + handleRhsc(); + intrFlags &= ~OHCI_INT_RHSC; + } + + intrFlags &= ~OHCI_INT_MIE; + if (intrFlags) { + dbg_printf("Disable intr: %d\n", intrFlags); + memPool.ohciRegs->HcInterruptDisable = intrFlags; + } +} + +static void PostIntrEnableFunction(void) +{ + memPool.ohciRegs->HcInterruptDisable = OHCI_INT_MIE; + asm volatile("lw $zero, 0xffffffffbfc00000\n"); + memPool.ohciRegs->HcInterruptEnable = OHCI_INT_MIE; +} + +void hcdIrqThread(void *arg) +{ + u32 eventRes; + + (void)arg; + + while (1) { + WaitEventFlag(hcdIrqEvent, 1, WEF_CLEAR | WEF_OR, &eventRes); + + usbdLock(); + hcdProcessIntr(); + EnableIntr(IOP_IRQ_USB); + PostIntrEnableFunction(); + usbdUnlock(); + } +} + +int usbdIntrHandler(void *arg) +{ + iSetEventFlag((int)arg, 1); + return 0; +} + +int initHardware(void) +{ + unsigned int i; + + dbg_printf("Host Controller...\n"); + memPool.ohciRegs->HcInterruptDisable = ~0; + memPool.ohciRegs->HcCommandStatus = OHCI_COM_HCR; + memPool.ohciRegs->HcControl = 0; + + for (i = 0; memPool.ohciRegs->HcCommandStatus & OHCI_COM_HCR; i++) { + if (i == 1000) + return -1; + + asm volatile("lw $zero, 0xffffffffbfc00000\n"); + } + dbg_printf("HC reset done\n"); + *(volatile u32 *)0xBF801570 |= 0x800 << 16; + *(volatile u32 *)0xBF801680 = 1; + + return 0; +} + +int initHcdStructs(void) +{ + int i; + HcCA *hcCommArea; + memPool.ohciRegs = (volatile OhciRegs *)OHCI_REG_BASE; + + initHardware(); + + dbg_printf("Structs...\n"); + + memPool.hcHCCA = NULL; + memPool.hcIsoTdBuf = (HcIsoTD *)(sizeof(HcCA)); + memPool.hcIsoTdBufEnd = memPool.hcIsoTdBuf + usbConfig.maxIsoTransfDesc; + memPool.hcTdBuf = (HcTD *)memPool.hcIsoTdBufEnd; + memPool.hcTdBufEnd = memPool.hcTdBuf + usbConfig.maxTransfDesc; + memPool.hcEdBuf = (HcED *)memPool.hcTdBufEnd; + memPool.endpointBuf = (Endpoint *)(memPool.hcEdBuf + 0x42); + memPool.deviceTreeBuf = (Device *)(memPool.endpointBuf + usbConfig.maxEndpoints); + memPool.ioReqBufPtr = (IoRequest *)(memPool.deviceTreeBuf + usbConfig.maxDevices); + memPool.hcIsoTdToIoReqLUT = (IoRequest **)(memPool.ioReqBufPtr + usbConfig.maxIoReqs); + memPool.hcTdToIoReqLUT = (IoRequest **)(memPool.hcIsoTdToIoReqLUT + usbConfig.maxIsoTransfDesc); + + u8 *devDescBuf = (u8 *)(memPool.hcTdToIoReqLUT + usbConfig.maxTransfDesc); + u32 memSize = ((u32)devDescBuf) + usbConfig.maxDevices * usbConfig.maxStaticDescSize; + + u8 *memBuf = AllocSysMemory(ALLOC_FIRST, memSize, 0); + memset(memBuf, 0, memSize); + + hcCommArea = (HcCA *)memBuf; + memPool.hcHCCA = (HcCA *)((((u32)memBuf + (u32)memPool.hcHCCA) & 0x1FFFFFFF) | 0xA0000000); + memPool.hcIsoTdBuf = (HcIsoTD *)((u32)memBuf + (u32)memPool.hcIsoTdBuf); + memPool.hcIsoTdBufEnd = (HcIsoTD *)((u32)memBuf + (u32)memPool.hcIsoTdBufEnd); + memPool.hcTdBuf = (HcTD *)((u32)memBuf + (u32)memPool.hcTdBuf); + memPool.hcTdBufEnd = (HcTD *)((u32)memBuf + (u32)memPool.hcTdBufEnd); + memPool.hcEdBuf = (HcED *)((u32)memBuf + (u32)memPool.hcEdBuf); + memPool.endpointBuf = (Endpoint *)((u32)memBuf + (u32)memPool.endpointBuf); + memPool.deviceTreeBuf = (Device *)((u32)memBuf + (u32)memPool.deviceTreeBuf); + memPool.ioReqBufPtr = (IoRequest *)((u32)memBuf + (u32)memPool.ioReqBufPtr); + memPool.hcIsoTdToIoReqLUT = (IoRequest **)((u32)memBuf + (u32)memPool.hcIsoTdToIoReqLUT); + memPool.hcTdToIoReqLUT = (IoRequest **)((u32)memBuf + (u32)memPool.hcTdToIoReqLUT); + + devDescBuf = (u8 *)((u32)memBuf + (u32)devDescBuf); + + Endpoint *ep = memPool.endpointBuf; + + for (i = 0; i < usbConfig.maxEndpoints; i++) { + ep->id = i; + + ep->next = NULL; + ep->prev = memPool.freeEpListEnd; + if (memPool.freeEpListEnd) + memPool.freeEpListEnd->next = ep; + else + memPool.freeEpListStart = ep; + memPool.freeEpListEnd = ep; + ep++; + } + + memPool.tdQueueStart[0] = memPool.tdQueueStart[1] = NULL; + memPool.tdQueueEnd[0] = memPool.tdQueueEnd[1] = NULL; + + Device *dev = memPool.deviceTreeBuf; + for (i = 0; i < usbConfig.maxDevices; i++) { + dev->functionAddress = i; + dev->id = i & 0xFF; + + dev->next = NULL; + dev->prev = memPool.freeDeviceListEnd; + if (memPool.freeDeviceListEnd) + memPool.freeDeviceListEnd->next = dev; + else + memPool.freeDeviceListStart = dev; + memPool.freeDeviceListEnd = dev; + + dev->staticDeviceDescPtr = devDescBuf; + dev++; + devDescBuf += usbConfig.maxStaticDescSize; + } + + memPool.deviceTreeRoot = attachChildDevice(NULL, 0); // virtual root + attachChildDevice(memPool.deviceTreeRoot, 1); // root hub port 0 + attachChildDevice(memPool.deviceTreeRoot, 2); // root hub port 1 + + IoRequest *req = memPool.ioReqBufPtr; + for (i = 0; i < usbConfig.maxIoReqs; i++) { + req->next = NULL; + req->prev = memPool.freeIoReqListEnd; + if (memPool.freeIoReqListEnd) + memPool.freeIoReqListEnd->next = req; + else + memPool.freeIoReqList = req; + memPool.freeIoReqListEnd = req; + req++; + } + + HcTD *hcTd = memPool.freeHcTdList = memPool.hcTdBuf; + for (i = 0; i < usbConfig.maxTransfDesc - 1; i++) { + hcTd->next = hcTd + 1; + hcTd++; + } + hcTd->next = NULL; + + HcIsoTD *isoTd = memPool.freeHcIsoTdList = memPool.hcIsoTdBuf; + for (i = 0; i < usbConfig.maxIsoTransfDesc - 1; i++) { + isoTd->next = isoTd + 1; + isoTd++; + } + isoTd->next = NULL; + + // build tree for interrupt table + HcED *ed = memPool.hcEdBuf; + for (i = 0; i < 0x3F; i++) { + ed->hcArea = HCED_SKIP; + if (i == 0) + ed->next = NULL; + else + ed->next = memPool.hcEdBuf + ((i - 1) >> 1); + + int intrId = i - 31; + if (intrId >= 0) { + intrId = ((intrId & 1) << 4) | ((intrId & 2) << 2) | (intrId & 4) | ((intrId & 8) >> 2) | ((intrId & 0x10) >> 4); + hcCommArea->InterruptTable[intrId] = ed; + } + ed++; + } + + ed->hcArea = HCED_SKIP; + memPool.ohciRegs->HcControlHeadEd = ed; + ed++; + + ed->hcArea = HCED_SKIP; + memPool.ohciRegs->HcBulkHeadEd = ed; + ed++; + + ed->hcArea = HCED_SKIP; + memPool.hcEdBuf->next = ed; // the isochronous endpoint + + memPool.ohciRegs->HcHCCA = hcCommArea; + memPool.ohciRegs->HcFmInterval = 0x27782EDF; + memPool.ohciRegs->HcPeriodicStart = 0x2A2F; + memPool.ohciRegs->HcInterruptEnable = OHCI_INT_MIE | OHCI_INT_RHSC | OHCI_INT_UE | OHCI_INT_WDH | OHCI_INT_SO; + memPool.ohciRegs->HcControl = OHCI_CTR_USB_OPERATIONAL | OHCI_CTR_PLE | OHCI_CTR_IE | OHCI_CTR_CLE | OHCI_CTR_BLE | 3; + return 0; +} + +int hcdInit(void) +{ + int irqRes; + iop_event_t event; + iop_thread_t thread; + + dbg_printf("Threads and events...\n"); + event.attr = event.option = event.bits = 0; + hcdIrqEvent = CreateEventFlag(&event); + + dbg_printf("Intr handler...\n"); + DisableIntr(IOP_IRQ_USB, &irqRes); + if (RegisterIntrHandler(IOP_IRQ_USB, 1, usbdIntrHandler, (void *)hcdIrqEvent) != 0) { + if (irqRes == IOP_IRQ_USB) + EnableIntr(IOP_IRQ_USB); + return 1; + } + + dbg_printf("HCD thread...\n"); + thread.attr = TH_C; + thread.option = 0; + thread.thread = hcdIrqThread; +#ifndef MINI_DRIVER + thread.stacksize = 0x4000; // 16KiB +#else + thread.stacksize = 0x0800; // 2KiB +#endif + thread.priority = usbConfig.hcdThreadPrio; + hcdTid = CreateThread(&thread); + StartThread(hcdTid, (void *)hcdIrqEvent); + + dbg_printf("Callback thread...\n"); + initCallbackThread(); + + dbg_printf("HCD init...\n"); + initHcdStructs(); + + dbg_printf("Hub driver...\n"); + initHubDriver(); + + dbg_printf("Enabling interrupts...\n"); + EnableIntr(IOP_IRQ_USB); + + return 0; +} diff --git a/iop/usb/usbd_mini/src/hcd.h b/iop/usb/usbd_mini/src/hcd.h new file mode 100644 index 000000000000..f91376bcc5bc --- /dev/null +++ b/iop/usb/usbd_mini/src/hcd.h @@ -0,0 +1,28 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#ifndef __HCD_H__ +#define __HCD_H__ + +#include "usbdpriv.h" + +int callUsbDriverFunc(int (*func)(int), int devId, void *gp); +Endpoint *openDeviceEndpoint(Device *dev, UsbEndpointDescriptor *endpDesc, u32 alignFlag); +Endpoint *doOpenEndpoint(Device *dev, UsbEndpointDescriptor *endpDesc, u32 alignFlag); +void *doGetDeviceStaticDescriptor(int devId, void *data, u8 type); +int doCloseEndpoint(Endpoint *ep); +int hcdInit(void); + +#endif // __HCD_H__ diff --git a/iop/usb/usbd_mini/src/hub.c b/iop/usb/usbd_mini/src/hub.c new file mode 100644 index 000000000000..654a59e6f302 --- /dev/null +++ b/iop/usb/usbd_mini/src/hub.c @@ -0,0 +1,737 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#include "usbdpriv.h" +#include "hcd.h" +#include "mem.h" +#include "usbio.h" +#include "driver.h" + +#include "defs.h" +#include "stdio.h" +#include "sysclib.h" +#include "sysmem.h" + +static UsbHub *hubBufferList; + +int hubDrvProbe(int devId); +int hubDrvConnect(int devId); +int hubDrvDisconnect(int devId); + +sceUsbdLddOps HubDriver = { + NULL, NULL, // next, prev + "hub", + hubDrvProbe, + hubDrvConnect, + hubDrvDisconnect, + 0, 0, 0, 0, 0, // reserved fields + 0 // gp +}; + +int initHubDriver(void) +{ + int i; + UsbHub *hub; + u32 needMem = usbConfig.maxHubDevices * sizeof(UsbHub); + + hubBufferList = (UsbHub *)AllocSysMemory(ALLOC_FIRST, needMem, 0); + if (!hubBufferList) { + dbg_printf("ERROR: unable to alloc hub buffer\n"); + return -1; + } + memset(hubBufferList, 0, needMem); + + hub = hubBufferList; + for (i = 0; i < usbConfig.maxHubDevices - 1; i++) { + hub->next = hub + 1; + hub++; + } + hub->next = NULL; + +#if USE_GP_REGISTER + doRegisterDriver(&HubDriver, &_gp); +#else + doRegisterDriver(&HubDriver, NULL); +#endif + return 0; +} + +UsbHub *allocHubBuffer(void) +{ + UsbHub *res = hubBufferList; + if (res) { + hubBufferList = res->next; + res->desc.bDescriptorType = 0; + res->controlIoReq.busyFlag = 0; + res->statusIoReq.busyFlag = 0; + } + return res; +} + +void freeHubBuffer(UsbHub *hub) +{ + if (hub) { + hub->next = hubBufferList; + hubBufferList = hub; + } +} + +void HubControlTransfer(UsbHub *hubDev, u8 requestType, u8 request, u16 value, + u16 index, u16 length, void *destData, + void *callback) +{ + if (!hubDev->controlIoReq.busyFlag) { + doControlTransfer(hubDev->controlEp, &hubDev->controlIoReq, + requestType, request, value, index, length, destData, + callback); + } else + dbg_printf("ERROR: HubControlTransfer %p: ioReq busy\n", hubDev); +} + +void hubGetHubStatusCallback(IoRequest *req); +void hubGetPortStatusCallback(IoRequest *req); +void getHubStatusChange(UsbHub *dev); + +void hubStatusChangeCallback(IoRequest *req) +{ + UsbHub *dev = (UsbHub *)req->userCallbackArg; + + if (req->resultCode == USB_RC_OK) { + if (dev->statusChangeInfo[0] & 1) { + dev->statusChangeInfo[0] &= ~1; + HubControlTransfer(dev, + USB_DIR_IN | USB_RT_HUB, USB_REQ_GET_STATUS, 0, 0, 4, &dev->hubStatus, + hubGetHubStatusCallback); + } else { + int port; + + if (dev->hubStatusCounter > 0) { + port = dev->hubStatusCounter; + if (dev->statusChangeInfo[port >> 3] & BIT(port & 7)) { + dev->statusChangeInfo[port >> 3] &= ~BIT(port & 7); + dev->portCounter = port; + HubControlTransfer(dev, + USB_DIR_IN | USB_RT_PORT, USB_REQ_GET_STATUS, 0, port, 4, &dev->portStatusChange, + hubGetPortStatusCallback); + return; + } + } else { + for (port = 1; (u32)port <= dev->numChildDevices; port++) { + if (dev->statusChangeInfo[port >> 3] & BIT(port & 7)) { + dev->statusChangeInfo[port >> 3] &= ~BIT(port & 7); + dev->portCounter = port; + HubControlTransfer(dev, + USB_DIR_IN | USB_RT_PORT, USB_REQ_GET_STATUS, 0, port, 4, &dev->portStatusChange, + hubGetPortStatusCallback); + return; + } + } + } + getHubStatusChange(dev); + } + } else + dbg_printf("hubStatusChangeCallback, iores %d\n", req->resultCode); +} + +void hubGetHubStatusCallback(IoRequest *req) +{ + UsbHub *dev = (UsbHub *)req->userCallbackArg; + if (req->resultCode == USB_RC_OK) { + if (dev->hubStatusChange & BIT(C_HUB_LOCAL_POWER)) { + dev->hubStatusChange &= ~BIT(C_HUB_LOCAL_POWER); + HubControlTransfer(dev, + USB_DIR_OUT | USB_RT_HUB, USB_REQ_CLEAR_FEATURE, C_HUB_LOCAL_POWER, 0, 0, NULL, + hubGetHubStatusCallback); + + } else if (dev->hubStatusChange & BIT(C_HUB_OVER_CURRENT)) { + dev->hubStatusChange &= ~BIT(C_HUB_OVER_CURRENT); + HubControlTransfer(dev, + USB_DIR_OUT | USB_RT_HUB, USB_REQ_CLEAR_FEATURE, C_HUB_OVER_CURRENT, 0, 0, NULL, + hubGetHubStatusCallback); + + } else + hubStatusChangeCallback(&dev->statusIoReq); + } +} + +int cancelTimerCallback(TimerCbStruct *arg) +{ + if (arg->isActive) { + if (arg->next) + arg->next->prev = arg->prev; + else + memPool.timerListEnd = arg->prev; + + if (arg->prev) + arg->prev->next = arg->next; + else + memPool.timerListStart = arg->next; + + arg->prev = arg->next = NULL; + arg->isActive = 0; + return 0; + } else + return -1; +} + +int addTimerCallback(TimerCbStruct *arg, TimerCallback func, void *cbArg, u32 delay) +{ + if (arg->isActive) + return -1; + + arg->isActive = 1; + arg->callbackProc = func; + arg->callbackArg = cbArg; + + TimerCbStruct *pos = memPool.timerListStart; + + while (pos && (delay > pos->delayCount)) { + delay -= pos->delayCount; + pos = pos->next; + } + if (pos) { + arg->prev = pos->prev; + if (pos->prev) + pos->prev->next = arg; + else + memPool.timerListStart = arg; + + arg->next = pos; + pos->prev = arg; + pos->delayCount -= delay; + } else { + arg->prev = memPool.timerListEnd; + if (memPool.timerListEnd) + memPool.timerListEnd->next = arg; + else + memPool.timerListStart = arg; + memPool.timerListEnd = arg; + arg->next = NULL; + } + arg->delayCount = delay; + memPool.ohciRegs->HcInterruptEnable = OHCI_INT_SF; + return 0; +} + +void killEndpoint(Endpoint *ep) +{ + int i = 0; + IoRequest *req; + HcED *hcEd = &ep->hcEd; + if (ep->endpointType == TYPE_ISOCHRON) { + for (i = 0; i < usbConfig.maxIsoTransfDesc; i++) { + req = memPool.hcIsoTdToIoReqLUT[i]; + if (req && (req->correspEndpoint == ep)) { + freeIoRequest(req); + memPool.hcIsoTdToIoReqLUT[i] = NULL; + freeIsoTd(memPool.hcIsoTdBuf + i); + } + } + freeIsoTd((HcIsoTD *)hcEd->tdTail); + } else { + for (i = 0; i < usbConfig.maxTransfDesc; i++) { + req = memPool.hcTdToIoReqLUT[i]; + if (req && (req->correspEndpoint == ep)) { + freeIoRequest(req); + memPool.hcTdToIoReqLUT[i] = NULL; + freeTd(memPool.hcTdBuf + i); + } + } + freeTd(hcEd->tdTail); + } + hcEd->tdTail = NULL; + hcEd->tdHead = NULL; + + while ((req = ep->ioReqListStart)) { + if (req->next) + req->next->prev = req->prev; + else + ep->ioReqListEnd = req->prev; + + if (req->prev) + req->prev->next = req->next; + else + ep->ioReqListStart = req->next; + + freeIoRequest(req); + } + removeEndpointFromQueue(ep); + + ep->next = NULL; + ep->prev = memPool.freeEpListEnd; + if (memPool.freeEpListEnd) + memPool.freeEpListEnd->next = ep; + else + memPool.freeEpListStart = ep; + memPool.freeEpListEnd = ep; +} + +int removeEndpointFromDevice(Device *dev, Endpoint *ep) +{ + ep->hcEd.hcArea |= HCED_SKIP; + removeHcEdFromList(ep->endpointType, &ep->hcEd); + + if (ep->next) + ep->next->prev = ep->prev; + else + dev->endpointListEnd = ep->prev; + + if (ep->prev) + ep->prev->next = ep->next; + else + dev->endpointListStart = ep->next; + + ep->correspDevice = NULL; + addTimerCallback(&ep->timer, (TimerCallback)killEndpoint, ep, 200); + return 0; +} + +void hubDeviceResetCallback(IoRequest *arg) +{ + if (arg->resultCode == USB_RC_OK) + getHubStatusChange((UsbHub *)arg->userCallbackArg); + else + dbg_printf("port reset err: %d\n", arg->resultCode); +} + +void hubResetDevice(void *devp) +{ + Device *dev = devp; + if (memPool.delayResets) { + dev->deviceStatus = DEVICE_RESETDELAYED; + } else { + memPool.delayResets = 1; + dev->deviceStatus = DEVICE_RESETPENDING; + dev->resetFlag = 1; + if (dev->parent == memPool.deviceTreeRoot) { // root hub port + memPool.ohciRegs->HcRhPortStatus[dev->attachedToPortNo - 1] = BIT(PORT_RESET); + } else { // normal hub port + UsbHub *hub; + + hub = (UsbHub *)dev->parent->privDataField; + hub->hubStatusCounter = dev->attachedToPortNo; + + HubControlTransfer(hub, + USB_DIR_OUT | USB_RT_PORT, USB_REQ_SET_FEATURE, PORT_RESET, dev->attachedToPortNo, 0, NULL, + hubDeviceResetCallback); + } + } +} + +int checkDelayedResetsTree(Device *tree) +{ + Device *dev; + for (dev = tree->childListStart; dev != NULL; dev = dev->next) { + if (dev->deviceStatus == DEVICE_RESETDELAYED) { + hubResetDevice(dev); + return 1; + } + if ((dev->deviceStatus == DEVICE_READY) && dev->childListStart) { + if (checkDelayedResetsTree(dev)) + return 1; + } + } + return 0; +} + +int checkDelayedResets(Device *dev) +{ + memPool.delayResets = 0; + dev->resetFlag = 0; + checkDelayedResetsTree(memPool.deviceTreeRoot); + return 0; +} + +void killDevice(Device *dev, Endpoint *ep) +{ + removeEndpointFromDevice(dev, ep); + checkDelayedResets(dev); + hubResetDevice(dev); +} + +void flushPort(Device *dev) +{ + Device *child; + if (dev->deviceStatus != DEVICE_NOTCONNECTED) { + dev->deviceStatus = DEVICE_NOTCONNECTED; + if (dev->devDriver) { + callUsbDriverFunc(dev->devDriver->disconnect, dev->id, dev->devDriver->gp); + dev->devDriver = NULL; + } + + if (dev->timer.isActive) + cancelTimerCallback(&dev->timer); + + while (dev->endpointListStart) + removeEndpointFromDevice(dev, dev->endpointListStart); + + while ((child = dev->childListStart)) { + if (child->next) + child->next->prev = child->prev; + else + dev->childListEnd = child->prev; + + if (child->prev) + child->prev->next = child->next; + else + dev->childListStart = child->next; + + flushPort(child); + + freeDevice(child); + } + dev->ioRequest.busyFlag = 0; + dev->privDataField = NULL; + } + if (dev->resetFlag) + checkDelayedResets(dev); +} + +void fetchConfigDescriptors(IoRequest *req) +{ + Endpoint *ep = req->correspEndpoint; + Device *dev = ep->correspDevice; + u16 readLen; + + if ((req->resultCode == USB_RC_OK) || (dev->fetchDescriptorCounter == 0)) { + int fetchDesc; + + u32 curDescNum = dev->fetchDescriptorCounter++; + + fetchDesc = curDescNum & 1; + curDescNum >>= 1; + + if ((curDescNum > 0) && !fetchDesc) { + UsbConfigDescriptor *desc = dev->staticDeviceDescEndPtr; + dev->staticDeviceDescEndPtr = (void *)((u8 *)(dev->staticDeviceDescEndPtr) + READ_UINT16(&desc->wTotalLength)); + } + + if (fetchDesc) { + UsbConfigDescriptor *desc = dev->staticDeviceDescEndPtr; + readLen = READ_UINT16(&desc->wTotalLength); + } else + readLen = 4; + + if ((u8 *)dev->staticDeviceDescEndPtr + readLen > (u8 *)dev->staticDeviceDescPtr + usbConfig.maxStaticDescSize) { + dbg_printf("USBD: Device ignored, Device descriptors too large\n"); + return; // buffer is too small, silently ignore the device + } + + if (curDescNum < ((UsbDeviceDescriptor *)dev->staticDeviceDescPtr)->bNumConfigurations) { + doControlTransfer(ep, &dev->ioRequest, + USB_DIR_IN | USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR, (USB_DT_CONFIG << 8) | curDescNum, 0, readLen, + dev->staticDeviceDescEndPtr, fetchConfigDescriptors); + } else + connectNewDevice(dev); + } else + killDevice(dev, ep); +} + +void requestDeviceDescriptor(IoRequest *req, u16 length); + +void requestDevDescrptCb(IoRequest *req) +{ + Endpoint *ep = req->correspEndpoint; + Device *dev = ep->correspDevice; + UsbDeviceDescriptor *desc = dev->staticDeviceDescPtr; + + if (req->resultCode == USB_RC_OK) { + if (req->transferedBytes < sizeof(UsbDeviceDescriptor)) { + ep->hcEd.maxPacketSize = (ep->hcEd.maxPacketSize & 0xF800) | desc->bMaxPacketSize0; + requestDeviceDescriptor(req, sizeof(UsbDeviceDescriptor)); + } else { + dev->fetchDescriptorCounter = 0; + dev->staticDeviceDescEndPtr = (u8 *)dev->staticDeviceDescEndPtr + sizeof(UsbDeviceDescriptor); + fetchConfigDescriptors(req); + } + } else { + dbg_printf("unable to read device descriptor, err %d\n", req->resultCode); + killDevice(dev, ep); + } +} + +void requestDeviceDescriptor(IoRequest *req, u16 length) +{ + Endpoint *ep = req->correspEndpoint; + Device *dev = ep->correspDevice; + + dev->staticDeviceDescEndPtr = dev->staticDeviceDescPtr; + + doControlTransfer(ep, &dev->ioRequest, + USB_DIR_IN | USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR, USB_DT_DEVICE << 8, 0, length, dev->staticDeviceDescEndPtr, + requestDevDescrptCb); +} + +void hubPeekDeviceDescriptor(IoRequest *req) +{ + requestDeviceDescriptor(req, 8); + + // we've assigned a function address to the device and can reset the next device now, if there is one + checkDelayedResets(req->correspEndpoint->correspDevice); +} + +void hubSetFuncAddress(Endpoint *ep); + +void hubSetFuncAddressCB(IoRequest *req) +{ + Endpoint *ep = req->correspEndpoint; + Device *dev = ep->correspDevice; + + if (req->resultCode == USB_RC_NORESPONSE) { + dbg_printf("device not responding\n"); + dev->functionDelay <<= 1; + if (dev->functionDelay <= 0x500) + addTimerCallback(&dev->timer, (TimerCallback)hubSetFuncAddress, ep, dev->functionDelay); + else + killDevice(dev, ep); + } else { + ep->hcEd.hcArea |= dev->functionAddress & 0x7F; + dev->deviceStatus = DEVICE_READY; + + addTimerCallback(&dev->timer, (TimerCallback)hubPeekDeviceDescriptor, req, 10); + } +} + +void hubSetFuncAddress(Endpoint *ep) +{ + Device *dev = ep->correspDevice; + + // printf("setting FA %02X\n", dev->functionAddress); + doControlTransfer(ep, &dev->ioRequest, + USB_DIR_OUT | USB_RECIP_DEVICE, USB_REQ_SET_ADDRESS, dev->functionAddress, 0, 0, NULL, hubSetFuncAddressCB); +} + +int hubTimedSetFuncAddress(Device *dev) +{ + dev->functionDelay = 20; + addTimerCallback(&dev->timer, (TimerCallback)hubSetFuncAddress, dev->endpointListStart, 20); + return 0; +} + +void hubGetPortStatusCallback(IoRequest *req) +{ + UsbHub *dev = (UsbHub *)req->userCallbackArg; + Device *port; + if (req->resultCode == USB_RC_OK) { + int feature = -1; + + dbg_printf("port status change: %d: %08X\n", dev->portCounter, dev->portStatusChange); + if (dev->portStatusChange & BIT(C_PORT_CONNECTION)) + feature = C_PORT_CONNECTION; + else if (dev->portStatusChange & BIT(C_PORT_ENABLE)) + feature = C_PORT_ENABLE; + else if (dev->portStatusChange & BIT(C_PORT_SUSPEND)) + feature = C_PORT_SUSPEND; + else if (dev->portStatusChange & BIT(C_PORT_OVER_CURRENT)) + feature = C_PORT_OVER_CURRENT; + else if (dev->portStatusChange & BIT(C_PORT_RESET)) + feature = C_PORT_RESET; + + if (feature >= 0) { + dev->portStatusChange &= ~BIT(feature); + HubControlTransfer(dev, + USB_DIR_OUT | USB_RT_PORT, USB_REQ_CLEAR_FEATURE, feature, dev->portCounter, 0, NULL, + hubGetPortStatusCallback); + } else { + port = fetchPortElemByNumber(dev->controlEp->correspDevice, dev->portCounter); + if (port) { + if (dev->portStatusChange & BIT(PORT_CONNECTION)) { + dbg_printf("Hub Port CCS\n"); + if (port->deviceStatus == DEVICE_NOTCONNECTED) { + dbg_printf("resetting dev\n"); + port->deviceStatus = DEVICE_CONNECTED; + addTimerCallback(&port->timer, (TimerCallback)hubResetDevice, port, 500); + return; + } else if (port->deviceStatus == DEVICE_RESETPENDING) { + if (dev->portStatusChange & BIT(PORT_ENABLE)) { + dbg_printf("hub port reset done, opening control EP\n"); + port->deviceStatus = DEVICE_RESETCOMPLETE; + port->isLowSpeedDevice = (dev->portStatusChange >> PORT_LOW_SPEED) & 1; + + if (openDeviceEndpoint(port, NULL, 0)) + hubTimedSetFuncAddress(port); + else + dbg_printf("Can't open default control ep.\n"); + dev->hubStatusCounter = 0; + } + } + } else { + dbg_printf("disconnected; flushing port\n"); + flushPort(port); + } + } + hubStatusChangeCallback(&dev->statusIoReq); + } + } else + dbg_printf("HubGetPortStatusCallback res %d\n", req->resultCode); +} + +void getHubStatusChange(UsbHub *dev) +{ + if (dev->statusIoReq.busyFlag == 0) { + attachIoReqToEndpoint(dev->statusChangeEp, &dev->statusIoReq, + dev->statusChangeInfo, (dev->numChildDevices + 8) >> 3, hubStatusChangeCallback); + } else + dbg_printf("getHubStatusChange: StatusChangeEP IoReq is busy!\n"); +} + +void hubSetPortPower(IoRequest *req) +{ + UsbHub *dev = (UsbHub *)req->userCallbackArg; + + if ((req->resultCode == USB_RC_OK) || (dev->portCounter == 0)) { + // there is no result to check if this is the first call for this hub + dev->portCounter++; + if (dev->portCounter <= dev->numChildDevices) { + HubControlTransfer(dev, + USB_DIR_OUT | USB_RT_PORT, USB_REQ_SET_FEATURE, PORT_POWER, dev->portCounter, 0, NULL, + hubSetPortPower); + } else + getHubStatusChange(dev); + } +} + +void hubSetupPorts(IoRequest *req) +{ + UsbHub *dev = (UsbHub *)req->userCallbackArg; + Device *usbDev = dev->controlEp->correspDevice; + + if (req->resultCode == USB_RC_OK) { + int port; + + dev->hubStatusCounter = 0; + dev->portCounter = 0; + dev->numChildDevices = dev->desc.bNbrPorts; + for (port = 0; port < dev->desc.bNbrPorts; port++) { + if (!attachChildDevice(usbDev, port + 1)) { + dev->numChildDevices = port; + break; + } + } + if (dev->numChildDevices > 0) + hubSetPortPower(req); + } +} + +void hubCheckPorts(IoRequest *req) +{ + UsbHub *dev = (UsbHub *)req->userCallbackArg; + if (req->resultCode == USB_RC_OK) { + if (dev->desc.bNbrPorts <= usbConfig.maxPortsPerHub) { + HubControlTransfer(dev, + USB_DIR_IN | USB_RT_HUB, USB_REQ_GET_STATUS, 0, 0, 4, + &dev->hubStatus, hubSetupPorts); + } else + dbg_printf("Hub has too many ports (%d > %d)\n", dev->desc.bNbrPorts, usbConfig.maxPortsPerHub); + } +} + +void hubCheckDeviceDesc(IoRequest *req) +{ + UsbHub *dev = (UsbHub *)req->userCallbackArg; + if (req->resultCode == USB_RC_OK) { + if (dev->desc.bDescriptorType == USB_DT_HUB) + hubCheckPorts(req); // we've got the descriptor already + else + HubControlTransfer(dev, + USB_DIR_IN | USB_RT_HUB, USB_REQ_GET_DESCRIPTOR, USB_DT_HUB << 8, 0, sizeof(UsbHubDescriptor), &dev->desc, + hubCheckPorts); + } +} + +int hubDrvProbe(int devId) +{ + UsbDeviceDescriptor *devDesc; + devDesc = (UsbDeviceDescriptor *)doGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + + if (devDesc && (devDesc->bDeviceClass == USB_CLASS_HUB) && (devDesc->bNumConfigurations == 1)) + return 1; + else + return 0; +} + +int hubDrvConnect(int devId) +{ + UsbConfigDescriptor *confDesc; + UsbInterfaceDescriptor *intfDesc; + UsbEndpointDescriptor *endpDesc; + UsbHubDescriptor *hubDesc; + Device *dev; + UsbHub *hubDevice; + + dev = fetchDeviceById(devId); + if (!dev) + return -1; + + confDesc = doGetDeviceStaticDescriptor(devId, NULL, USB_DT_CONFIG); + if (!confDesc || (confDesc->bNumInterfaces != 1)) + return -2; + + intfDesc = doGetDeviceStaticDescriptor(devId, confDesc, USB_DT_INTERFACE); + if (!intfDesc || (intfDesc->bNumEndpoints != 1)) + return -3; + + endpDesc = doGetDeviceStaticDescriptor(devId, intfDesc, USB_DT_ENDPOINT); + if (!endpDesc) + return -4; + + if ((endpDesc->bEndpointAddress & USB_DIR_IN) == 0) + return -5; + + if ((endpDesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) + return -6; + + hubDesc = doGetDeviceStaticDescriptor(devId, endpDesc, USB_DT_HUB); + + hubDevice = allocHubBuffer(); + if (!hubDevice) + return -8; + + dev->privDataField = hubDevice; + hubDevice->controlEp = dev->endpointListStart; + + hubDevice->statusChangeEp = doOpenEndpoint(dev, endpDesc, 0); + if (!hubDevice->statusChangeEp) { + freeHubBuffer(hubDevice); + return -9; + } + + hubDevice->controlIoReq.userCallbackArg = hubDevice; + hubDevice->statusIoReq.userCallbackArg = hubDevice; + + if (hubDesc) { + u8 len = hubDesc->bLength; + if (len > sizeof(UsbHubDescriptor)) + len = sizeof(UsbHubDescriptor); + memcpy(&hubDevice->desc, hubDesc, len); + } else + memset(&hubDevice->desc, 0, sizeof(UsbHubDescriptor)); + + HubControlTransfer(hubDevice, + USB_DIR_OUT, USB_REQ_SET_CONFIGURATION, confDesc->bConfigurationValue, 0, 0, NULL, + hubCheckDeviceDesc); + + return 0; +} + +int hubDrvDisconnect(int devId) +{ + Device *dev = fetchDeviceById(devId); + if (dev) { + freeHubBuffer((UsbHub *)dev->privDataField); + return 0; + } else + return -1; +} diff --git a/iop/usb/usbd_mini/src/hub.h b/iop/usb/usbd_mini/src/hub.h new file mode 100644 index 000000000000..26f8cfd3d0c2 --- /dev/null +++ b/iop/usb/usbd_mini/src/hub.h @@ -0,0 +1,19 @@ +/** + * @file + * USB Driver function prototypes and constants. + */ + +#ifndef __HUB_H__ +#define __HUB_H__ + +#include "usbdpriv.h" + +int removeEndpointFromDevice(Device *dev, Endpoint *ep); +int initHubDriver(void); +void flushPort(Device *dev); +int addTimerCallback(TimerCbStruct *arg, TimerCallback func, void *cbArg, u32 delay); +void hubResetDevice(void *devp); +int hubTimedSetFuncAddress(Device *dev); + + +#endif // __HUB_H__ diff --git a/iop/usb/usbd_mini/src/imports.lst b/iop/usb/usbd_mini/src/imports.lst new file mode 100644 index 000000000000..918e0ba8f90a --- /dev/null +++ b/iop/usb/usbd_mini/src/imports.lst @@ -0,0 +1,48 @@ + +loadcore_IMPORTS_start +I_RegisterLibraryEntries +loadcore_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +sysmem_IMPORTS_start +I_AllocSysMemory +sysmem_IMPORTS_end + +sysclib_IMPORTS_start +I_memcpy +I_memset +I_strtol +sysclib_IMPORTS_end + +intrman_IMPORTS_start +I_DisableIntr +I_EnableIntr +I_RegisterIntrHandler +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +thsemap_IMPORTS_start +I_CreateSema +I_WaitSema +I_SignalSema +I_DeleteSema +thsemap_IMPORTS_end + +thevent_IMPORTS_start +I_CreateEventFlag +I_SetEventFlag +I_iSetEventFlag +I_WaitEventFlag +thevent_IMPORTS_end + +thbase_IMPORTS_start +I_CreateThread +I_StartThread +I_DeleteThread +I_DelayThread +I_ChangeThreadPriority +thbase_IMPORTS_end diff --git a/iop/usb/usbd_mini/src/interface.c b/iop/usb/usbd_mini/src/interface.c new file mode 100644 index 000000000000..7589d97a4055 --- /dev/null +++ b/iop/usb/usbd_mini/src/interface.c @@ -0,0 +1,452 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#include +#include +#ifdef DEBUG +#include +#endif + +#include "usbdpriv.h" +#include "driver.h" +#include "mem.h" +#include "hcd.h" +#include "usbio.h" + +extern UsbdConfig usbConfig; +extern int hcdTid; +extern int callbackTid; + +int sceUsbdRegisterLdd(sceUsbdLddOps *driver) +{ + int res; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return USB_RC_BADCONTEXT; + } + +#if USE_GP_REGISTER + res = doRegisterDriver(driver, OldGP); +#else + res = doRegisterDriver(driver, NULL); +#endif + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +int sceUsbdRegisterAutoloader(sceUsbdLddOps *drv) +{ + int res; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return USB_RC_BADCONTEXT; + } + +#if USE_GP_REGISTER + res = doRegisterAutoLoader(drv, OldGP); +#else + res = doRegisterAutoLoader(drv, NULL); +#endif + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +int sceUsbdUnregisterLdd(sceUsbdLddOps *driver) +{ + int res; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return USB_RC_BADCONTEXT; + } + + res = doUnregisterDriver(driver); + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +int sceUsbdUnregisterAutoloader(void) +{ + int res; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return USB_RC_BADCONTEXT; + } + + res = doUnregisterAutoLoader(); + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +void *sceUsbdScanStaticDescriptor(int devId, void *data, u8 type) +{ + void *res; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return NULL; + } + + res = doGetDeviceStaticDescriptor(devId, data, type); + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +int sceUsbdGetDeviceLocation(int devId, u8 *path) +{ + Device *dev; + int res; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return USB_RC_BADCONTEXT; + } + + dev = fetchDeviceById(devId); + if (dev) + res = doGetDeviceLocation(dev, path); + else + res = USB_RC_BADDEV; + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +int sceUsbdSetPrivateData(int devId, void *data) +{ + Device *dev; + int res = USB_RC_OK; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return USB_RC_BADCONTEXT; + } + + dev = fetchDeviceById(devId); + if (dev) + dev->privDataField = data; + else + res = USB_RC_BADDEV; + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +void *sceUsbdGetPrivateData(int devId) +{ + Device *dev; + void *res = NULL; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return NULL; + } + + dev = fetchDeviceById(devId); + if (dev) + res = dev->privDataField; + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +int sceUsbdOpenPipe(int devId, UsbEndpointDescriptor *desc) +{ + Device *dev; + Endpoint *ep; + int res = -1; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return -1; + } + + dev = fetchDeviceById(devId); + if (dev) { + ep = doOpenEndpoint(dev, desc, 0); + if (ep) + res = ep->id; + } + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +int sceUsbdClosePipe(int id) +{ + Endpoint *ep; + int res; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return -1; + } + + ep = fetchEndpointById(id); + if (ep) + res = doCloseEndpoint(ep); + else + res = USB_RC_BADPIPE; + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +int sceUsbdTransferPipe(int id, void *data, u32 len, void *option, sceUsbdDoneCallback callback, void *cbArg) +{ + UsbDeviceRequest *ctrlPkt = (UsbDeviceRequest *)option; + IoRequest *req; + Endpoint *ep; + int res = 0; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return USB_RC_BADCONTEXT; + } + + ep = fetchEndpointById(id); + if (!ep) { + dbg_printf("sceUsbdTransferPipe: Endpoint %d not found\n", id); + res = USB_RC_BADPIPE; + } + + if ((res == 0) && data && len) { + if ((((u32)((u8 *)data + len - 1) >> 12) - ((u32)data >> 12)) > 1) + res = USB_RC_BADLENGTH; + else if (ep->alignFlag && ((u32)data & 3)) + res = USB_RC_BADALIGN; + else if ((ep->endpointType == TYPE_ISOCHRON) && ((ep->hcEd.maxPacketSize & 0x7FF) < len)) + res = USB_RC_BADLENGTH; + } + if (res == 0) { + req = allocIoRequest(); + if (!req) { + dbg_printf("Ran out of IoReqs\n"); + res = USB_RC_IOREQ; + } + } + if (res == 0) { + req->userCallbackProc = callback; + req->userCallbackArg = cbArg; +#if USE_GP_REGISTER + req->gpSeg = GetGP(); // gp of the calling module +#endif + if (ep->endpointType == TYPE_CONTROL) { + if (!ctrlPkt) { + res = USB_RC_BADOPTION; + freeIoRequest(req); + } else if (ctrlPkt->length != len) { + res = USB_RC_BADLENGTH; + freeIoRequest(req); + } else { + res = doControlTransfer(ep, req, + ctrlPkt->requesttype, ctrlPkt->request, ctrlPkt->value, ctrlPkt->index, ctrlPkt->length, + data, signalCallbackThreadFunc); + } + } else { + if (ep->endpointType == TYPE_ISOCHRON) + req->waitFrames = (u32)option; + res = attachIoReqToEndpoint(ep, req, data, len, signalCallbackThreadFunc); + } + } + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +int sceUsbdOpenPipeAligned(int devId, UsbEndpointDescriptor *desc) +{ + Device *dev; + Endpoint *ep; + int res = -1; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return -1; + } + + dev = fetchDeviceById(devId); + if (dev) { + ep = doOpenEndpoint(dev, desc, 1); + if (ep) + res = ep->id; + } + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} + +int sceUsbdChangeThreadPriority(int prio1, int prio2) +{ + int res; +#if USE_GP_REGISTER + void *OldGP; + + OldGP = SetModuleGP(); +#endif + if (usbdLock() != 0) { +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + return USB_RC_BADCONTEXT; + } + + res = 0; + + if (usbConfig.hcdThreadPrio != prio1) { + usbConfig.hcdThreadPrio = prio1; + res = ChangeThreadPriority(hcdTid, prio1); + } + + if (usbConfig.cbThreadPrio != prio2) { + usbConfig.cbThreadPrio = prio2; + res = ChangeThreadPriority(callbackTid, prio2); + } + + usbdUnlock(); +#if USE_GP_REGISTER + SetGP(OldGP); +#endif + + return res; +} diff --git a/iop/usb/usbd_mini/src/irx_imports.h b/iop/usb/usbd_mini/src/irx_imports.h new file mode 100644 index 000000000000..6d49068adafd --- /dev/null +++ b/iop/usb/usbd_mini/src/irx_imports.h @@ -0,0 +1,28 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# Defines all IRX imports. +*/ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include "irx.h" + +/* Please keep these in alphabetical order! */ +#include "intrman.h" +#include "loadcore.h" +#include "stdio.h" +#include "sysclib.h" +#include "sysmem.h" +#include "thbase.h" +#include "thevent.h" +#include "thsemap.h" + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/iop/usb/usbd_mini/src/mem.c b/iop/usb/usbd_mini/src/mem.c new file mode 100644 index 000000000000..29425e207f3b --- /dev/null +++ b/iop/usb/usbd_mini/src/mem.c @@ -0,0 +1,259 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#include "usbdpriv.h" +#include "mem.h" + +#include "stdio.h" + +MemoryPool memPool; + +HcIsoTD *allocIsoTd(void) +{ + HcIsoTD *newTd = memPool.freeHcIsoTdList; + if (newTd) { + memPool.freeHcIsoTdList = newTd->next; + newTd->next = NULL; + } + return newTd; +} + +void freeIsoTd(HcIsoTD *argTd) +{ + HcIsoTD *pos; + if (argTd) { + for (pos = memPool.freeHcIsoTdList; pos != NULL; pos = pos->next) + if (pos == argTd) { + printf("freeIsoTd %p: already free\n", argTd); + return; + } + argTd->next = memPool.freeHcIsoTdList; + memPool.freeHcIsoTdList = argTd; + } +} + +HcTD *allocTd(void) +{ + HcTD *res = memPool.freeHcTdList; + if (res) { + memPool.freeHcTdList = res->next; + res->next = NULL; + } + return res; +} + +void freeTd(HcTD *argTd) +{ + HcTD *pos; + if (argTd) { + for (pos = memPool.freeHcTdList; pos != NULL; pos = pos->next) + if (pos == argTd) { + printf("FreeTD %p: already free\n", argTd); + return; + } + argTd->next = memPool.freeHcTdList; + memPool.freeHcTdList = argTd; + } +} + +Device *attachChildDevice(Device *parent, u32 portNum) +{ + Device *newDev = memPool.freeDeviceListStart; + if (!newDev) { + dbg_printf("Ran out of device handles\n"); + return NULL; + } + + if (newDev->next) + newDev->next->prev = newDev->prev; + else + memPool.freeDeviceListEnd = newDev->prev; + + if (newDev->prev) + newDev->prev->next = newDev->next; + else + memPool.freeDeviceListStart = newDev->next; + + newDev->endpointListEnd = newDev->endpointListStart = NULL; + newDev->devDriver = NULL; + newDev->deviceStatus = DEVICE_NOTCONNECTED; + newDev->resetFlag = 0; + newDev->childListEnd = newDev->childListStart = NULL; + newDev->parent = parent; + newDev->attachedToPortNo = portNum; + newDev->privDataField = NULL; + if (parent) { + newDev->prev = parent->childListEnd; + if (parent->childListEnd) + parent->childListEnd->next = newDev; + else + parent->childListStart = newDev; + newDev->next = NULL; + parent->childListEnd = newDev; + } else + newDev->next = newDev->prev = NULL; + return newDev; +} + +void freeDevice(Device *dev) +{ + if (!dev) + return; + + if ((dev < memPool.deviceTreeBuf) || (dev >= memPool.deviceTreeBuf + usbConfig.maxDevices)) { + printf("freeDevice %p: Arg is not part of dev buffer\n", dev); + return; + } + + dev->prev = memPool.freeDeviceListEnd; + if (memPool.freeDeviceListEnd) + memPool.freeDeviceListEnd->next = dev; + else + memPool.freeDeviceListStart = dev; + + dev->next = NULL; + dev->parent = NULL; + memPool.freeDeviceListEnd = dev; +} + +Device *fetchPortElemByNumber(Device *hub, int port) +{ + Device *res = hub->childListStart; + while (--port > 0) { + if (!res) + return NULL; + res = res->next; + } + return res; +} + +void addToHcEndpointList(u8 type, HcED *ed) +{ + ed->next = memPool.hcEdBuf[type].next; + memPool.hcEdBuf[type].next = ed; +} + +void removeHcEdFromList(int type, const HcED *hcEd) +{ + HcED *prev = memPool.hcEdBuf + type; + HcED *pos = prev->next; + while (pos) { + if (pos == hcEd) { + prev->next = pos->next; + return; + } + prev = pos; + pos = pos->next; + } +} + +Endpoint *allocEndpointForDevice(Device *dev, u32 align) +{ + Endpoint *newEp = memPool.freeEpListStart; + if (!newEp) + return NULL; + + if (newEp->next) + newEp->next->prev = newEp->prev; + else + memPool.freeEpListEnd = newEp->prev; + + if (newEp->prev) + newEp->prev->next = newEp->next; + else + memPool.freeEpListStart = newEp->next; + + newEp->correspDevice = dev; + newEp->ioReqListStart = newEp->ioReqListEnd = NULL; + newEp->busyNext = newEp->busyPrev = NULL; + newEp->inTdQueue = 0; + newEp->alignFlag = align; + + newEp->next = NULL; + newEp->prev = dev->endpointListEnd; + if (dev->endpointListEnd) + dev->endpointListEnd->next = newEp; + else + dev->endpointListStart = newEp; + + dev->endpointListEnd = newEp; + return newEp; +} + +Device *fetchDeviceById(int devId) +{ + if ((devId > 0) && (devId < usbConfig.maxDevices)) { + Device *dev; + + dev = memPool.deviceTreeBuf + devId; + if (dev->parent) + return dev; + } + return NULL; +} + +Endpoint *fetchEndpointById(int id) +{ + if ((id >= 0) && (id < usbConfig.maxEndpoints)) { + Endpoint *res; + + res = memPool.endpointBuf + id; + if (res->correspDevice) + return res; + } + return NULL; +} + +IoRequest *allocIoRequest(void) +{ + IoRequest *res = memPool.freeIoReqList; + if (res) { + if (res->next) + res->next->prev = res->prev; + else + memPool.freeIoReqListEnd = res->prev; + + if (res->prev) + res->prev->next = res->next; + else + memPool.freeIoReqList = res->next; + res->prev = res->next = NULL; + } else + dbg_printf("ran out of IoReqs\n"); + return res; +} + +void freeIoRequest(IoRequest *req) +{ + int num = req - memPool.ioReqBufPtr; + IoRequest *pos; + if (req) { + if ((num >= 0) && (num < usbConfig.maxIoReqs)) { + for (pos = memPool.freeIoReqList; pos != NULL; pos = pos->next) + if (pos == req) { + printf("freeIoRequest %p: already free.\n", req); + return; + } + req->prev = memPool.freeIoReqListEnd; + if (memPool.freeIoReqListEnd) + memPool.freeIoReqListEnd->next = req; + else + memPool.freeIoReqList = req; + req->next = NULL; + memPool.freeIoReqListEnd = req; + } + req->busyFlag = 0; + } +} diff --git a/iop/usb/usbd_mini/src/mem.h b/iop/usb/usbd_mini/src/mem.h new file mode 100644 index 000000000000..c2aa2859e00e --- /dev/null +++ b/iop/usb/usbd_mini/src/mem.h @@ -0,0 +1,45 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#ifndef __MEM_H__ +#define __MEM_H__ + +#include "usbdpriv.h" + +extern MemoryPool memPool; + +HcIsoTD *allocIsoTd(void); +void freeIsoTd(HcIsoTD *argTd); + +HcTD *allocTd(void); +void freeTd(HcTD *argTd); + +Device *attachChildDevice(Device *parent, u32 portNum); +void freeDevice(Device *dev); + +Device *fetchPortElemByNumber(Device *hub, int port); + +void addToHcEndpointList(u8 type, HcED *ed); +void removeHcEdFromList(int type, const HcED *hcEd); + +Endpoint *allocEndpointForDevice(Device *dev, u32 align); + +Device *fetchDeviceById(int devId); +Endpoint *fetchEndpointById(int id); + +IoRequest *allocIoRequest(void); +void freeIoRequest(IoRequest *req); + +#endif diff --git a/iop/usb/usbd_mini/src/usbd.c b/iop/usb/usbd_mini/src/usbd.c new file mode 100644 index 000000000000..0ce3cfd6ff6d --- /dev/null +++ b/iop/usb/usbd_mini/src/usbd.c @@ -0,0 +1,410 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#include "usbdpriv.h" +#include "mem.h" +#include "hcd.h" +#include "hub.h" +#include "usbio.h" + +#include "stdio.h" +#include "sysclib.h" +#include "thsemap.h" +#include "loadcore.h" +IRX_ID(MODNAME, 1, 1); + +#define WELCOME_STR "FreeUsbd v.0.1.2\n" + +// While the header of the export table is small, the large size of the export table (as a whole) places it in data instead of sdata. +extern struct irx_export_table _exp_usbd __attribute__((section("data"))); + +#ifndef MINI_DRIVER +UsbdConfig usbConfig = { + 0x20, // maxDevices + 0x40, // maxEndpoints + 0x80, // maxTransDesc + 0x80, // maxIsoTransfDesc + 0x100, // maxIoReqs + 0x200, // maxStaticDescSize + 8, // maxHubDevices + 8, // maxPortsPerHub + + 0x1E, // hcdThreadPrio + 0x24 // cbThreadPrio +}; +#else +UsbdConfig usbConfig = { + 0x10, // maxDevices + 0x20, // maxEndpoints + 0x40, // maxTransDesc + 0x40, // maxIsoTransfDesc + 0x100, // maxIoReqs + 0x200, // maxStaticDescSize + 4, // maxHubDevices + 4, // maxPortsPerHub + + 0x1E, // hcdThreadPrio + 0x24 // cbThreadPrio +}; +#endif + +int usbdSema; + +int usbdLock(void) +{ + return WaitSema(usbdSema); +} + +int usbdUnlock(void) +{ + return SignalSema(usbdSema); +} + +int doGetDeviceLocation(Device *dev, u8 *path) +{ + u8 tempPath[6]; + int count; + for (count = 0; (count < 6) && (dev != memPool.deviceTreeRoot); count++) { + tempPath[count] = dev->attachedToPortNo; + dev = dev->parent; + } + if (dev == memPool.deviceTreeRoot) { + int cpCount; + + for (cpCount = 0; cpCount < 7; cpCount++) { + if (cpCount < count) + path[cpCount] = tempPath[count - (cpCount + 1)]; + else + path[cpCount] = 0; + } + return 0; + } else + return USB_RC_BADHUBDEPTH; +} + +void processDoneQueue_IsoTd(HcIsoTD *arg) +{ + u32 tdHcRes = arg->hcArea >> 28; + u32 pswRes = arg->psw[0] >> 12; + u32 pswOfs = arg->psw[0] & 0x7FF; + + IoRequest *listStart = NULL, *listEnd = NULL; + + IoRequest *req = memPool.hcIsoTdToIoReqLUT[arg - memPool.hcIsoTdBuf]; + if (!req) + return; + + memPool.hcIsoTdToIoReqLUT[arg - memPool.hcIsoTdBuf] = NULL; + freeIsoTd(arg); + req->transferedBytes = 0; + req->resultCode = (pswRes << 4) | tdHcRes; + + if ((tdHcRes == USB_RC_OK) && ((pswRes == USB_RC_OK) || (pswRes == USB_RC_DATAUNDER))) { + if ((req->correspEndpoint->hcEd.hcArea & HCED_DIR_MASK) == HCED_DIR_IN) + req->transferedBytes = pswOfs; + else + req->transferedBytes = req->length; + } + + req->prev = listEnd; + if (listEnd) + listEnd->next = req; + else + listStart = req; + req->next = NULL; + listEnd = req; + + HcED *ed = &req->correspEndpoint->hcEd; + if (ED_HALTED(req->correspEndpoint->hcEd)) { + HcIsoTD *curTd = (HcIsoTD *)((u32)ed->tdHead & ~0xF); + + while (curTd && (curTd != (HcIsoTD *)ed->tdTail)) { + HcIsoTD *nextTd = curTd->next; + freeIsoTd(curTd); + + req = memPool.hcIsoTdToIoReqLUT[curTd - memPool.hcIsoTdBuf]; + if (req) { + memPool.hcIsoTdToIoReqLUT[arg - memPool.hcIsoTdBuf] = NULL; + IoRequest *listPos; + for (listPos = listStart; listPos != NULL; listPos = listPos->next) + if (listPos == req) + break; + if (listPos == NULL) { + req->resultCode = USB_RC_ABORTED; + req->prev = listEnd; + if (listEnd) + listEnd->next = req; + else + listStart = req; + req->next = NULL; + listEnd = req; + } + } + curTd = nextTd; + } + ed->tdHead = ed->tdTail; + } + + IoRequest *listPos = listStart; + while (listPos) { + IoRequest *listNext = listPos->next; + listPos->busyFlag = 0; + if (listPos->correspEndpoint->correspDevice) { + if (listPos->callbackProc) + listPos->callbackProc(listPos); + } else + freeIoRequest(listPos); + listPos = listNext; + } + checkTdQueue(ISOTD_QUEUE); +} + +void processDoneQueue_GenTd(HcTD *arg) +{ + IoRequest *req; + IoRequest *firstElem = NULL, *lastElem = NULL; + + u32 hcRes; + + if ((req = memPool.hcTdToIoReqLUT[arg - memPool.hcTdBuf])) { + memPool.hcTdToIoReqLUT[arg - memPool.hcTdBuf] = NULL; + + u32 tdHcArea = arg->HcArea; + + if (arg->bufferEnd && (tdHcArea & 0x180000)) { // dir != SETUP + if (arg->curBufPtr == 0) // transfer successful + req->transferedBytes = req->length; + else + req->transferedBytes = (u8 *)arg->curBufPtr - (u8 *)req->destPtr; + } + hcRes = tdHcArea >> 28; + freeTd(arg); + + if (req->resultCode == USB_RC_OK) + req->resultCode = hcRes; + + if (hcRes || ((tdHcArea & 0xE00000) != 0xE00000)) { // E00000: interrupts disabled + req->prev = lastElem; +#if 0 + // lastElem is NULL, so this condition is always false + if (lastElem) + { + lastElem->next = req; + } + else +#endif + { + firstElem = req; + } + req->next = NULL; + lastElem = req; + } + + HcED *ed = &req->correspEndpoint->hcEd; + if (hcRes && ED_HALTED(req->correspEndpoint->hcEd)) { + HcTD *tdListPos = (HcTD *)((u32)ed->tdHead & ~0xF); + while (tdListPos && (tdListPos != ed->tdTail)) { + HcTD *nextTd = tdListPos->next; + freeTd(tdListPos); + + req = memPool.hcTdToIoReqLUT[tdListPos - memPool.hcTdBuf]; + if (req) { + memPool.hcTdToIoReqLUT[tdListPos - memPool.hcTdBuf] = NULL; + + IoRequest *listPos; + for (listPos = firstElem; listPos != NULL; listPos = listPos->next) + if (listPos == req) + break; + + if (!listPos) { + req->resultCode = USB_RC_ABORTED; + req->prev = lastElem; + if (lastElem) + lastElem->next = req; + else + firstElem = req; + req->next = NULL; + lastElem = req; + } + } + tdListPos = nextTd; + } + ed->tdHead = ed->tdTail; + } + + IoRequest *pos = firstElem; + while (pos) { + pos->busyFlag = 0; + Device *dev = pos->correspEndpoint->correspDevice; + IoRequest *next = pos->next; + if (dev) { + if (pos->callbackProc) + pos->callbackProc(pos); + } else + freeIoRequest(pos); + pos = next; + } + checkTdQueue(GENTD_QUEUE); + } +} + +void handleTimerList(void) +{ + TimerCbStruct *timer = memPool.timerListStart; + if (timer) { + if (timer->delayCount > 0) + timer->delayCount--; + + while (memPool.timerListStart && (memPool.timerListStart->delayCount == 0)) { + dbg_printf("timer expired\n"); + timer = memPool.timerListStart; + + memPool.timerListStart = timer->next; + if (timer->next) + timer->next->prev = NULL; + else + memPool.timerListEnd = NULL; + timer->next = timer->prev = NULL; + timer->isActive = 0; + timer->callbackProc(timer->callbackArg); + } + } + // disable SOF interrupts if there are no timers left + if (memPool.timerListStart == NULL) + memPool.ohciRegs->HcInterruptDisable = OHCI_INT_SF; +} + +struct ArgOption +{ + const char *param; + int *value, *value2; +}; + +static inline void ParseOptionInput(const struct ArgOption *option, const char *arguments) +{ + const char *p; + int value, NewValue; + + p = arguments; + value = 0; + while (*p != '\0') { + if (*p == ',') + break; + + if ((NewValue = *p - '0') > 9) { + return; + } else { + value = (((value << 2) + value) << 1) + NewValue; + p++; + } + } + + if ((option->value2 != NULL && *p == ',') || (option->value2 == NULL && *p != ',')) { + if (arguments < p++) { + *option->value = value; + } + + if (option->value2 != NULL) { + value = 0; + while (*p != '\0') { + if ((NewValue = *p - '0') > 9) { + return; + } else { + value = (((value << 2) + value) << 1) + NewValue; + p++; + } + } + + *option->value2 = value; + } + } +} + +int _start(int argc, char *argv[]) +{ + static const struct ArgOption SupportedArgs[] = { + {"dev=", + &usbConfig.maxDevices, + NULL}, + {"ed=", + &usbConfig.maxEndpoints, + NULL}, + {"gtd=", + &usbConfig.maxTransfDesc, + NULL}, + {"itd=", + &usbConfig.maxIsoTransfDesc, + NULL}, + {"ioreq=", + &usbConfig.maxIoReqs, + NULL}, + {"conf=", + &usbConfig.maxStaticDescSize, + NULL}, + {"hub=", + &usbConfig.maxHubDevices, + NULL}, + {"port=", + &usbConfig.maxPortsPerHub, + NULL}, + {"thpri=", + &usbConfig.hcdThreadPrio, + &usbConfig.cbThreadPrio}, + {NULL, + NULL, + NULL}}; + iop_sema_t sema; + const char *pArgs, *pParam; + int i, option; + + for (i = 1; i < argc; i++) { + for (option = 0; SupportedArgs[option].param != NULL; option++) { + pParam = SupportedArgs[option].param; + pArgs = argv[i]; + while (*pParam != '\0') { + if (*pArgs != *pParam) + break; + + pParam++; + pArgs++; + } + + if (*pParam == '\0') { + ParseOptionInput(&SupportedArgs[option], pArgs); + } + } + } + + + printf(WELCOME_STR); + + dbg_printf("library entries...\n"); + + if (RegisterLibraryEntries(&_exp_usbd) != 0) { + dbg_printf("RegisterLibraryEntries failed\n"); + return MODULE_NO_RESIDENT_END; + } + + sema.attr = 1; + sema.option = 0; + sema.initial = 1; + sema.max = 1; + usbdSema = CreateSema(&sema); + + hcdInit(); + + dbg_printf("Init done\n"); + return MODULE_RESIDENT_END; +} diff --git a/iop/usb/usbd_mini/src/usbd_v12_stubs.c b/iop/usb/usbd_mini/src/usbd_v12_stubs.c new file mode 100644 index 000000000000..fc0cbe2c5a05 --- /dev/null +++ b/iop/usb/usbd_mini/src/usbd_v12_stubs.c @@ -0,0 +1,44 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * usbd 1.2 export stubs for FreeUsbd-based usbd_mini. + * + * Full usbd (SCE rewrite) implements these; mass-storage / OPL only need 1.1. + * Keep the export table at 1.2 so modules that probe the library version still load. + */ + +#include "usbd.h" + +void usbdReboot(int ac) +{ + (void)ac; +} + +int sceUsbdGetReportDescriptor(int devId, int cfgNum, int ifNum, void **desc, u32 *len) +{ + (void)devId; + (void)cfgNum; + (void)ifNum; + (void)desc; + (void)len; + return USB_RC_NOSUPPORT; +} + +int sceUsbdMultiIsochronousTransfer( + int pipeId, sceUsbdMultiIsochronousRequest *request, sceUsbdMultiIsochronousDoneCallback callback, void *cbArg) +{ + (void)pipeId; + (void)request; + (void)callback; + (void)cbArg; + return USB_RC_NOSUPPORT; +} diff --git a/iop/usb/usbd_mini/src/usbdpriv.h b/iop/usb/usbd_mini/src/usbdpriv.h new file mode 100644 index 000000000000..457f0e516f57 --- /dev/null +++ b/iop/usb/usbd_mini/src/usbdpriv.h @@ -0,0 +1,329 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#ifndef __USBDPRIV_H__ +#define __USBDPRIV_H__ + +#include "usbd.h" +#include "types.h" +#include "defs.h" + +#define OHCI_REG_BASE 0xBF801600 + +#define MODNAME "usbd" +#ifdef DEBUG +#define dbg_printf(a...) printf(MODNAME ": " a) +#else +#define dbg_printf(a...) (void)0 +#endif + +#define READ_UINT16(a) (((u8 *)a)[0] | (((u8 *)a)[1] << 8)) + +typedef struct +{ + int maxDevices; + int maxEndpoints; + int maxTransfDesc; + int maxIsoTransfDesc; + int maxIoReqs; + int maxStaticDescSize; + int maxHubDevices; + int maxPortsPerHub; + + int hcdThreadPrio; + int cbThreadPrio; +} UsbdConfig; + +extern UsbdConfig usbConfig; + +struct _device; +struct _ioRequest; +struct _hcTd; +struct _hcIsoTd; +struct _hcEd; +struct _endpoint; +struct _UsbDriver; +struct _ioRequest; + +typedef void (*TimerCallback)(void *arg); +typedef void (*InternCallback)(struct _ioRequest *arg); + +typedef struct _timerCbStruct +{ + u32 isActive; + struct _timerCbStruct *prev, *next; + TimerCallback callbackProc; + void *callbackArg; + u32 delayCount; +} TimerCbStruct; + +typedef struct _ioRequest +{ + u32 busyFlag; + struct _ioRequest *next, *prev; + struct _endpoint *correspEndpoint; + UsbDeviceRequest devReq; + void *destPtr; + u32 length; // length of destPtr buffer + InternCallback callbackProc; + u32 resultCode; + u32 transferedBytes; + u32 waitFrames; // number of frames to wait for isochronous transfers + sceUsbdDoneCallback userCallbackProc; + void *userCallbackArg; +#if USE_GP_REGISTER + void *gpSeg; +#endif +} IoRequest; + +typedef struct _device +{ + u32 id; + struct _device *next, *prev; + struct _endpoint *endpointListStart, *endpointListEnd; + sceUsbdLddOps *devDriver; + u8 deviceStatus; + u8 functionAddress; + u8 isLowSpeedDevice; + u8 resetFlag; + struct _device *childListStart, *childListEnd; + struct _device *parent; + u32 attachedToPortNo; + void *privDataField; + TimerCbStruct timer; + IoRequest ioRequest; + u32 functionDelay; // is this necessary? + void *staticDeviceDescPtr; + void *staticDeviceDescEndPtr; + u32 fetchDescriptorCounter; +} Device; + +typedef struct _hcTd +{ + u32 HcArea; + void *curBufPtr; + struct _hcTd *next; + void *bufferEnd; +} HcTD; + +typedef struct _hcIsoTd +{ + u32 hcArea; + void *bufferPage0; + struct _hcIsoTd *next; + void *bufferEnd; + u16 psw[8]; +} HcIsoTD; + +typedef struct _hcEd +{ + u16 hcArea; + u16 maxPacketSize; + HcTD *tdTail; + HcTD *tdHead; + struct _hcEd *next; +} HcED; + +typedef struct _endpoint +{ + u32 id; + u8 endpointType; + u8 inTdQueue; + u8 alignFlag; + u8 pad; + struct _endpoint *next, *prev; + struct _endpoint *busyNext, *busyPrev; + Device *correspDevice; + IoRequest *ioReqListStart; + IoRequest *ioReqListEnd; + u32 isochronLastFrameNum; // 40 + TimerCbStruct timer; // sizeof(TimerCbStruct) => 24 bytes + HcED hcEd; // HcED has to be aligned to 0x10 bytes! +} Endpoint; + +typedef struct _usbHub +{ + struct _usbHub *next; + Endpoint *controlEp, *statusChangeEp; + IoRequest controlIoReq, statusIoReq; + UsbHubDescriptor desc; + u32 numChildDevices; + u32 portCounter; + u32 hubStatusCounter; + u16 hubStatus; // + u16 hubStatusChange; // unite to u32 to make it match portStatusChange + u32 portStatusChange; + u8 statusChangeInfo[8]; // depends on number of ports +} UsbHub; + +typedef struct +{ + volatile HcED *InterruptTable[32]; + volatile u16 FrameNumber; + volatile u16 pad; + volatile HcTD *DoneHead; + volatile u8 reserved[116]; + volatile u32 pad2; // expand struct to 256 bytes for alignment +} HcCA; + +typedef struct +{ + volatile u32 HcRevision; + volatile u32 HcControl; + volatile u32 HcCommandStatus; + volatile u32 HcInterruptStatus; + volatile u32 HcInterruptEnable; + volatile u32 HcInterruptDisable; + volatile HcCA *HcHCCA; + volatile HcED *HcPeriodCurrentEd; + volatile HcED *HcControlHeadEd; + volatile HcED *HcControlCurrentEd; + volatile HcED *HcBulkHeadEd; + volatile HcED *HcBulkCurrentEd; + volatile u32 HcDoneHead; + volatile u32 HcFmInterval; + volatile u32 HcFmRemaining; + volatile u32 HcFmNumber; + volatile u32 HcPeriodicStart; + volatile u32 HcLsThreshold; + volatile u32 HcRhDescriptorA; + volatile u32 HcRhDescriptorB; + volatile u32 HcRhStatus; + volatile u32 HcRhPortStatus[2]; +} OhciRegs; + +typedef struct _memPool +{ + volatile OhciRegs *ohciRegs; + volatile HcCA *hcHCCA; + + struct _hcEd *hcEdBuf; + + struct _hcTd *freeHcTdList; + struct _hcTd *hcTdBuf; + struct _hcTd *hcTdBufEnd; + + struct _hcIsoTd *freeHcIsoTdList; + struct _hcIsoTd *hcIsoTdBuf; + struct _hcIsoTd *hcIsoTdBufEnd; + + struct _ioRequest **hcTdToIoReqLUT; + struct _ioRequest **hcIsoTdToIoReqLUT; + + struct _ioRequest *ioReqBufPtr; + struct _ioRequest *freeIoReqList; + struct _ioRequest *freeIoReqListEnd; + + struct _device *deviceTreeBuf; + struct _device *freeDeviceListStart; + struct _device *freeDeviceListEnd; + + struct _endpoint *endpointBuf; + struct _endpoint *freeEpListStart; + struct _endpoint *freeEpListEnd; + + struct _endpoint *tdQueueStart[2], *tdQueueEnd[2]; + + struct _timerCbStruct *timerListStart; + struct _timerCbStruct *timerListEnd; + + struct _device *deviceTreeRoot; + + u32 delayResets; +} MemoryPool; + +#define GENTD_QUEUE 1 +#define ISOTD_QUEUE 2 + +#define TYPE_CONTROL 0x3F +#define TYPE_BULK 0x40 +#define TYPE_ISOCHRON 0x41 + +#define DEVICE_NOTCONNECTED 0 +#define DEVICE_CONNECTED 1 +#define DEVICE_RESETDELAYED 2 +#define DEVICE_RESETPENDING 3 +#define DEVICE_RESETCOMPLETE 4 +#define DEVICE_READY 5 + +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVER_CURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define PORT_LOW_SPEED 9 + +#define C_HUB_LOCAL_POWER 0 +#define C_HUB_OVER_CURRENT 1 + +#define C_PORT_CONNECTION 16 +#define C_PORT_ENABLE 17 +#define C_PORT_SUSPEND 18 +#define C_PORT_OVER_CURRENT 19 +#define C_PORT_RESET 20 + +#define BIT(x) (((u32)1) << (x)) + +#define C_PORT_FLAGS (BIT(C_PORT_CONNECTION) | BIT(C_PORT_ENABLE) | BIT(C_PORT_SUSPEND) | BIT(C_PORT_OVER_CURRENT) | BIT(C_PORT_RESET)) + +#define HCED_DIR_OUT BIT(11) // Direction field +#define HCED_DIR_IN BIT(12) // Direction field +#define HCED_SPEED BIT(13) // Speed bit +#define HCED_SKIP BIT(14) // sKip bit +#define HCED_ISOC BIT(15) // Format bit +#define HCED_DIR_MASK (HCED_DIR_OUT | HCED_DIR_IN) + +#define ED_HALTED(a) ((u32)((a).tdHead) & 1) +#define ED_SKIPPED(a) ((u32)((a).hcArea) & HCED_SKIP) + +#define TD_HCAREA(CC, T, DI, DP, R) (((CC) << 12) | ((T) << 8) | ((DI) << 5) | ((DP) << 3) | ((R) << 2)) + +#define TD_SETUP 0 +#define TD_OUT 1 +#define TD_IN 2 + +#define OHCI_INT_SO BIT(0) +#define OHCI_INT_WDH BIT(1) +#define OHCI_INT_SF BIT(2) +#define OHCI_INT_RD BIT(3) +#define OHCI_INT_UE BIT(4) +#define OHCI_INT_FNO BIT(5) +#define OHCI_INT_RHSC BIT(6) +#define OHCI_INT_OC BIT(30) +#define OHCI_INT_MIE BIT(31) + +#define OHCI_COM_HCR BIT(0) +#define OHCI_COM_CLF BIT(1) +#define OHCI_COM_BLF BIT(2) + +#define OHCI_CTR_PLE BIT(2) // Periodic List Enable +#define OHCI_CTR_IE BIT(3) // Isochronous Enable +#define OHCI_CTR_CLE BIT(4) // Control List Enable +#define OHCI_CTR_BLE BIT(5) // Bulk List Enable +#define OHCI_CTR_USB_RESET (0 << 6) +#define OHCI_CTR_USB_RESUME (1 << 6) +#define OHCI_CTR_USB_OPERATIONAL (2 << 6) +#define OHCI_CTR_USB_SUSPEND (3 << 6) + +int usbdLock(void); +int usbdUnlock(void); +int doGetDeviceLocation(Device *dev, u8 *path); +void processDoneQueue_IsoTd(HcIsoTD *arg); +void processDoneQueue_GenTd(HcTD *arg); +void handleTimerList(void); + + +#endif // __USBDPRIV_H__ diff --git a/iop/usb/usbd_mini/src/usbio.c b/iop/usb/usbd_mini/src/usbio.c new file mode 100644 index 000000000000..d46c7c73e260 --- /dev/null +++ b/iop/usb/usbd_mini/src/usbio.c @@ -0,0 +1,319 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#include "usbdpriv.h" +#include "mem.h" +#include "usbio.h" + +#include "stdio.h" + +void removeEndpointFromQueue(Endpoint *ep) +{ + if (!ep->inTdQueue) + return; + + if (ep->busyNext) + ep->busyNext->busyPrev = ep->busyPrev; + else + memPool.tdQueueEnd[ep->inTdQueue - 1] = ep->busyPrev; + + if (ep->busyPrev) + ep->busyPrev->busyNext = ep->busyNext; + else + memPool.tdQueueStart[ep->inTdQueue - 1] = ep->busyNext; + + ep->inTdQueue = 0; +} + +void enqueueEndpoint(Endpoint *ep, u32 listType) +{ + if (ep->inTdQueue) + return; + + ep->busyNext = NULL; + ep->busyPrev = memPool.tdQueueEnd[listType - 1]; + if (memPool.tdQueueEnd[listType - 1]) + memPool.tdQueueEnd[listType - 1]->busyNext = ep; + else + memPool.tdQueueStart[listType - 1] = ep; + memPool.tdQueueEnd[listType - 1] = ep; + + ep->inTdQueue = listType; +} + +void checkTdQueue(int type) +{ + if ((type == GENTD_QUEUE) && !memPool.freeHcTdList) + return; + else if ((type == ISOTD_QUEUE) && !memPool.freeHcIsoTdList) + return; + + if (memPool.tdQueueStart[type - 1]) + handleIoReqList(memPool.tdQueueStart[type - 1]); +} + +int setupControlTransfer(Endpoint *ep) +{ + HcTD *statusTd, *tailTd, *dataTd = NULL; + IoRequest *curIoReq = ep->ioReqListStart; + + if (ep->hcEd.tdTail && !ED_HALTED(ep->hcEd) && !ED_SKIPPED(ep->hcEd) && curIoReq) { + if (curIoReq->destPtr && curIoReq->length) { + dataTd = allocTd(); + if (!dataTd) { + enqueueEndpoint(ep, GENTD_QUEUE); + return 0; + } + } + statusTd = allocTd(); + tailTd = allocTd(); + + if (!statusTd || !tailTd) { + freeTd(statusTd); + freeTd(tailTd); + freeTd(dataTd); + + enqueueEndpoint(ep, GENTD_QUEUE); + return 0; + } + + if (curIoReq->next) + curIoReq->next->prev = curIoReq->prev; + else + ep->ioReqListEnd = curIoReq->prev; + + if (curIoReq->prev) + curIoReq->prev->next = curIoReq->next; + else + ep->ioReqListStart = curIoReq->next; + + // first stage: setup + ep->hcEd.tdTail->HcArea = TD_HCAREA(USB_RC_NOTACCESSED, 2, 7, TD_SETUP, 0) << 16; + ep->hcEd.tdTail->curBufPtr = &curIoReq->devReq; + ep->hcEd.tdTail->bufferEnd = ((u8 *)&curIoReq->devReq) + sizeof(UsbDeviceRequest) - 1; + + memPool.hcTdToIoReqLUT[ep->hcEd.tdTail - memPool.hcTdBuf] = curIoReq; + + // second stage: data + if (dataTd) { + ep->hcEd.tdTail->next = dataTd; + + if (curIoReq->devReq.requesttype & USB_DIR_IN) + dataTd->HcArea = TD_HCAREA(USB_RC_NOTACCESSED, 3, 7, TD_IN, 1) << 16; + else + dataTd->HcArea = TD_HCAREA(USB_RC_NOTACCESSED, 3, 7, TD_OUT, 1) << 16; + + dataTd->curBufPtr = curIoReq->destPtr; + dataTd->bufferEnd = (u8 *)curIoReq->destPtr + curIoReq->length - 1; + dataTd->next = statusTd; + + memPool.hcTdToIoReqLUT[dataTd - memPool.hcTdBuf] = curIoReq; + } else + ep->hcEd.tdTail->next = statusTd; + + // third stage: status + if (curIoReq->devReq.requesttype & USB_DIR_IN) + statusTd->HcArea = TD_HCAREA(USB_RC_NOTACCESSED, 3, 0, TD_OUT, 0) << 16; + else + statusTd->HcArea = TD_HCAREA(USB_RC_NOTACCESSED, 3, 0, TD_IN, 0) << 16; + + statusTd->curBufPtr = NULL; + statusTd->bufferEnd = NULL; + statusTd->next = tailTd; + memPool.hcTdToIoReqLUT[statusTd - memPool.hcTdBuf] = curIoReq; + + ep->hcEd.tdTail = tailTd; + + memPool.ohciRegs->HcCommandStatus |= OHCI_COM_CLF; // control list filled + + // remove endpoint from busy list if there are no IoRequests left + if (!ep->ioReqListStart) + removeEndpointFromQueue(ep); + return 1; + } else { + // endpoint error + removeEndpointFromQueue(ep); + return 0; + } +} + +int setupIsocronTransfer(Endpoint *ep) +{ + IoRequest *curIoReq = ep->ioReqListStart; + + HcED *ed = &ep->hcEd; + HcIsoTD *curTd = (HcIsoTD *)ed->tdTail; + HcIsoTD *newTd; + + u32 frameNo; + + if (ep->hcEd.tdTail && !ED_HALTED(ep->hcEd) && !ED_SKIPPED(ep->hcEd) && curIoReq) { + newTd = allocIsoTd(); + if (!newTd) { + enqueueEndpoint(ep, ISOTD_QUEUE); + return 0; + } + + if (curIoReq->next) + curIoReq->next->prev = curIoReq->prev; + else + ep->ioReqListEnd = curIoReq->prev; + + if (curIoReq->prev) + curIoReq->prev->next = curIoReq->next; + else + ep->ioReqListStart = curIoReq->next; + + if (ed->tdTail == (HcTD *)((u32)ed->tdHead & ~0xF)) + frameNo = memPool.hcHCCA->FrameNumber + 2; + else + frameNo = ep->isochronLastFrameNum; + + frameNo = (frameNo + curIoReq->waitFrames) & 0xFFFF; + + ep->isochronLastFrameNum = frameNo + 1; + + curTd->hcArea = (USB_RC_NOTACCESSED << 28) | frameNo; + curTd->bufferPage0 = (void *)((u32)curIoReq->destPtr & ~0xFFF); + curTd->next = newTd; + + if (curIoReq->destPtr && curIoReq->length) + curTd->bufferEnd = (u8 *)curIoReq->destPtr + curIoReq->length - 1; + else + curTd->bufferEnd = NULL; + + curTd->psw[0] = (USB_RC_NOTACCESSED << 12) | ((u32)curIoReq->destPtr & 0xFFF); + + memPool.hcIsoTdToIoReqLUT[curTd - memPool.hcIsoTdBuf] = curIoReq; + + ed->tdTail = (HcTD *)newTd; + + // remove endpoint from busy list if there are no IoRequests left + if (!ep->ioReqListStart) + removeEndpointFromQueue(ep); + return 1; + } else { + // endpoint error + removeEndpointFromQueue(ep); + return 0; + } +} + +int setupBulkTransfer(Endpoint *ep) +{ + IoRequest *curIoReq = ep->ioReqListStart; + + HcED *ed = &ep->hcEd; + HcTD *curTd = ed->tdTail; + HcTD *newTd; + + if (ep->hcEd.tdTail && !ED_HALTED(ep->hcEd) && !ED_SKIPPED(ep->hcEd) && curIoReq) { + newTd = allocTd(); + if (!newTd) { + enqueueEndpoint(ep, GENTD_QUEUE); + return 0; + } + + if (curIoReq->next) + curIoReq->next->prev = curIoReq->prev; + else + ep->ioReqListEnd = curIoReq->prev; + + if (curIoReq->prev) + curIoReq->prev->next = curIoReq->next; + else + ep->ioReqListStart = curIoReq->next; + + curTd->HcArea = TD_HCAREA(USB_RC_NOTACCESSED, 0, 0, 3, 1) << 16; + curTd->next = newTd; + curTd->curBufPtr = curIoReq->destPtr; + + if (curIoReq->destPtr && curIoReq->length) + curTd->bufferEnd = (u8 *)curIoReq->destPtr + curIoReq->length - 1; + else + curTd->bufferEnd = NULL; + + memPool.hcTdToIoReqLUT[curTd - memPool.hcTdBuf] = curIoReq; + + ed->tdTail = newTd; + + if (ep->endpointType == TYPE_BULK) + memPool.ohciRegs->HcCommandStatus |= OHCI_COM_BLF; // Bulk List Filled + + // remove endpoint from busy list if there are no IoRequests left + if (!ep->ioReqListStart) + removeEndpointFromQueue(ep); + return 1; + } else { + // endpoint error + dbg_printf("ERROR Endpoint error\n"); + removeEndpointFromQueue(ep); + return 0; + } +} + +void handleIoReqList(Endpoint *ep) +{ + if (ep->endpointType == TYPE_CONTROL) + setupControlTransfer(ep); + else if (ep->endpointType == TYPE_ISOCHRON) + setupIsocronTransfer(ep); + else // bulk or interrupt + setupBulkTransfer(ep); +} + +int attachIoReqToEndpoint(Endpoint *ep, IoRequest *req, void *destdata, u16 length, void *callback) +{ + if (!ep->correspDevice) + return USB_RC_BUSY; + if (req->busyFlag) + return USB_RC_BUSY; + + req->busyFlag = 1; + req->correspEndpoint = ep; + req->destPtr = destdata; + req->length = length; + req->resultCode = 0; + req->transferedBytes = 0; + req->callbackProc = callback; + + req->next = NULL; + req->prev = ep->ioReqListEnd; + if (ep->ioReqListEnd) + ep->ioReqListEnd->next = req; + else + ep->ioReqListStart = req; + ep->ioReqListEnd = req; + + handleIoReqList(ep); + return 0; +} + +int doControlTransfer(Endpoint *ep, IoRequest *req, + u8 requestType, u8 request, u16 value, u16 index, u16 length, + void *destdata, void *callback) +{ + if (req->busyFlag) { + dbg_printf("ERROR: doControlTransfer: IoReq busy\n"); + return USB_RC_BUSY; + } + + req->devReq.requesttype = requestType; + req->devReq.request = request; + req->devReq.value = value; + req->devReq.index = index; + req->devReq.length = length; + return attachIoReqToEndpoint(ep, req, destdata, length, callback); +} diff --git a/iop/usb/usbd_mini/src/usbio.h b/iop/usb/usbd_mini/src/usbio.h new file mode 100644 index 000000000000..f767f35607c9 --- /dev/null +++ b/iop/usb/usbd_mini/src/usbio.h @@ -0,0 +1,28 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +/** + * @file + * USB Driver function prototypes and constants. + */ + +#ifndef __USBIO_H__ +#define __USBIO_H__ + +void removeEndpointFromQueue(Endpoint *ep); +void checkTdQueue(int type); +void handleIoReqList(Endpoint *ep); +int doControlTransfer(Endpoint *ep, IoRequest *req, + u8 requestType, u8 request, u16 value, u16 index, u16 length, + void *destdata, void *callback); +int attachIoReqToEndpoint(Endpoint *ep, IoRequest *req, void *destdata, u16 length, void *callback); +void handleIoReqList(Endpoint *ep); + +#endif // __USBIO_H__