mirror of
https://github.com/deadw00d/AROS.git
synced 2026-03-19 11:39:53 +00:00
2296 lines
81 KiB
C
2296 lines
81 KiB
C
/* uhwcmd.c - pciusb.device by Chris Hodges
|
||
*/
|
||
|
||
#include <devices/usb_hub.h>
|
||
#include <proto/utility.h>
|
||
#include <proto/exec.h>
|
||
#include <proto/timer.h>
|
||
|
||
#include <strings.h>
|
||
|
||
#include LC_LIBDEFS_FILE
|
||
|
||
#include "uhwcmd.h"
|
||
#include "ohciproto.h"
|
||
#include "uhciproto.h"
|
||
#include "ehciproto.h"
|
||
#include "xhciproto.h"
|
||
|
||
#define NewList NEWLIST
|
||
|
||
/* we cannot use AROS_WORD2LE in struct initializer */
|
||
#if AROS_BIG_ENDIAN
|
||
#define WORD2LE(w) (UWORD)(((w) >> 8) & 0x00FF) | (((w) << 8) & 0xFF00)
|
||
#else
|
||
#define WORD2LE(w) (w)
|
||
#endif
|
||
|
||
#if defined(AROS_USE_LOGRES)
|
||
#ifdef LogHandle
|
||
#undef LogHandle
|
||
#endif
|
||
#ifdef LogResBase
|
||
#undef LogResBase
|
||
#endif
|
||
#define LogHandle (base->hd_LogRHandle)
|
||
#define LogResBase (base->hd_LogResBase)
|
||
#endif
|
||
|
||
/* Root hub device */
|
||
const struct UsbStdDevDesc RHDevDesc = {
|
||
sizeof(struct UsbStdDevDesc),
|
||
UDT_DEVICE,
|
||
WORD2LE(0x0110), // bcdUSB
|
||
HUB_CLASSCODE, // bDeviceClass
|
||
0, // bDeviceSubClass
|
||
0, // bDeviceProtocol
|
||
8, // bMaxPacketSize0
|
||
WORD2LE(0x0000), // idVendor
|
||
WORD2LE(0x0000), // idProduct
|
||
WORD2LE(0x0100), // bcdDevice
|
||
1, // iManufacturer
|
||
2, // iProduct
|
||
0, // iSerialNumber
|
||
1 // bNumConfigurations
|
||
};
|
||
|
||
/* Root hub config */
|
||
const struct UsbStdCfgDesc RHCfgDesc = {
|
||
sizeof(struct UsbStdCfgDesc),
|
||
UDT_CONFIGURATION,
|
||
WORD2LE(sizeof(struct UsbStdCfgDesc) + sizeof(struct UsbStdIfDesc) + sizeof(struct UsbStdEPDesc)),
|
||
// wTotalLength
|
||
1, // bNumInterfaces
|
||
1, // bConfigurationValue
|
||
3, // iConfiguration
|
||
USCAF_ONE|USCAF_SELF_POWERED, // bmAttributes
|
||
0 // MaxPower
|
||
};
|
||
|
||
/* Root hub interface */
|
||
const struct UsbStdIfDesc RHIfDesc = {
|
||
sizeof(struct UsbStdIfDesc),
|
||
UDT_INTERFACE,
|
||
0, // bInterfaceNumber
|
||
0, // bAlternateSetting
|
||
1, // bNumEndpoints
|
||
HUB_CLASSCODE, // bInterfaceClass
|
||
0, // bInterfaceSubClass
|
||
0, // bInterfaceProtocol
|
||
4 // iInterface
|
||
};
|
||
|
||
/* Root hub endpoint */
|
||
const struct UsbStdEPDesc RHEPDesc = {
|
||
sizeof(struct UsbStdEPDesc),
|
||
UDT_ENDPOINT,
|
||
URTF_IN|1, // bEndpointAddress
|
||
USEAF_INTERRUPT, // bmAttributes
|
||
WORD2LE(8), // wMaxPacketSize
|
||
255 // bInterval
|
||
};
|
||
|
||
/* Root hub descriptors */
|
||
const struct UsbHubDesc RHHubDesc = {
|
||
sizeof(struct UsbHubDesc), // 0 Number of bytes in this descriptor, including this byte
|
||
UDT_HUB, // 1 Descriptor Type, value: 29H for hub descriptor
|
||
0, // 2 Number of downstream facing ports that this hub supports
|
||
0, // 3 wHubCharacteristics
|
||
0, // 5 bPwrOn2PwrGood
|
||
1, // 6 bHubContrCurrent
|
||
1, // 7 DeviceRemovable (size is variable)
|
||
0 // x PortPwrCtrlMask (size is variable)
|
||
};
|
||
|
||
const struct UsbSSHubDesc RHHubSSDesc = {
|
||
sizeof(struct UsbSSHubDesc), // 0 Number of bytes in this descriptor, including this byte
|
||
UDT_SSHUB, // 1 Descriptor Type, value: 29H for hub descriptor
|
||
0, // 2 Number of downstream facing ports that this hub supports
|
||
0, // 3 wHubCharacteristics
|
||
0, // 5 bPwrOn2PwrGood
|
||
1, // 6 bHubContrCurrent
|
||
0, // 7 bHubHdrDecLat
|
||
0, // 8 wHubDelay
|
||
1 // 10 DeviceRemovable
|
||
};
|
||
|
||
|
||
const CONST_STRPTR strStandardConfig = "Standard Config";
|
||
const CONST_STRPTR strHubInterface = "Hub interface";
|
||
const CONST_STRPTR RHStrings[] = { "Chris Hodges", "PCI Root Hub Unit x", strStandardConfig, strHubInterface };
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
const CONST_STRPTR RHxStrings[] = { "The AROS Dev Team", "PCI Superspeed Root Hub Unit x", strStandardConfig, strHubInterface };
|
||
#endif
|
||
|
||
/* /// "SureCause()" */
|
||
void SureCause(struct PCIDevice *base, struct Interrupt *interrupt)
|
||
{
|
||
#if !defined(__AROS__)
|
||
/* this is a workaround for the original Cause() function missing tailed calls */
|
||
Disable();
|
||
|
||
if((interrupt->is_Node.ln_Type == NT_SOFTINT) || (interrupt->is_Node.ln_Type == NT_USER)) {
|
||
// signal tailed call
|
||
interrupt->is_Node.ln_Type = NT_USER;
|
||
} else {
|
||
do {
|
||
interrupt->is_Node.ln_Type = NT_SOFTINT;
|
||
Forbid(); // make sure code is not interrupted by other tasks
|
||
Enable();
|
||
AROS_INTC1(interrupt->is_Code, interrupt->is_Data);
|
||
Disable();
|
||
Permit();
|
||
} while(interrupt->is_Node.ln_Type != NT_SOFTINT);
|
||
interrupt->is_Node.ln_Type = NT_INTERRUPT;
|
||
}
|
||
Enable();
|
||
#else
|
||
Cause(interrupt);
|
||
#endif
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "uhwOpenTimer()" */
|
||
BOOL uhwOpenTimer(struct PCIUnit *unit, struct PCIDevice *base)
|
||
{
|
||
if((unit->hu_MsgPort = CreateMsgPort())) {
|
||
if((unit->hu_TimerReq = (struct timerequest *) CreateIORequest(unit->hu_MsgPort, sizeof(struct timerequest)))) {
|
||
if(!OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest *) unit->hu_TimerReq, 0)) {
|
||
unit->hu_TimerReq->tr_node.io_Message.mn_Node.ln_Name = "PCI hardware";
|
||
unit->hu_TimerReq->tr_node.io_Command = TR_ADDREQUEST;
|
||
KPRINTF(1, "opened timer device\n");
|
||
return(TRUE);
|
||
}
|
||
DeleteIORequest((struct IORequest *) unit->hu_TimerReq);
|
||
unit->hu_TimerReq = NULL;
|
||
}
|
||
DeleteMsgPort(unit->hu_MsgPort);
|
||
unit->hu_MsgPort = NULL;
|
||
}
|
||
KPRINTF(5, "failed to open timer.device\n");
|
||
return(FALSE);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "uhwDelayMS()" */
|
||
void uhwDelayMS(ULONG milli, struct PCIUnit *unit)
|
||
{
|
||
unit->hu_TimerReq->tr_time.tv_secs = 0;
|
||
unit->hu_TimerReq->tr_time.tv_micro = milli * 1000;
|
||
DoIO((struct IORequest *) unit->hu_TimerReq);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "uhwDelayMicro()" */
|
||
void uhwDelayMicro(ULONG micro, struct PCIUnit *unit)
|
||
{
|
||
unit->hu_TimerReq->tr_time.tv_secs = 0;
|
||
unit->hu_TimerReq->tr_time.tv_micro = micro;
|
||
DoIO((struct IORequest *) unit->hu_TimerReq);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "uhwCloseTimer()" */
|
||
void uhwCloseTimer(struct PCIUnit *unit, struct PCIDevice *base)
|
||
{
|
||
if(unit->hu_MsgPort) {
|
||
if(unit->hu_TimerReq) {
|
||
KPRINTF(1, "closing timer.device\n");
|
||
CloseDevice((APTR) unit->hu_TimerReq);
|
||
DeleteIORequest((struct IORequest *) unit->hu_TimerReq);
|
||
unit->hu_TimerReq = NULL;
|
||
}
|
||
DeleteMsgPort(unit->hu_MsgPort);
|
||
unit->hu_MsgPort = NULL;
|
||
}
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "Open_Unit()" */
|
||
struct Unit * Open_Unit(struct IOUsbHWReq *ioreq,
|
||
LONG unitnr,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct PCIUnit *unit = NULL;
|
||
|
||
if(!base->hd_ScanDone) {
|
||
base->hd_ScanDone = TRUE;
|
||
if(!pciInit(base)) {
|
||
return NULL;
|
||
}
|
||
}
|
||
unit = (struct PCIUnit *) base->hd_Units.lh_Head;
|
||
while(((struct Node *) unit)->ln_Succ) {
|
||
if((unit->hu_UnitNo & ~PCIUSBUNIT_MASK) == unitnr) {
|
||
break;
|
||
}
|
||
unit = (struct PCIUnit *) ((struct Node *) unit)->ln_Succ;
|
||
}
|
||
if(!((struct Node *) unit)->ln_Succ) {
|
||
KPRINTF(20, "Unit %ld does not exist!\n", unitnr);
|
||
return NULL;
|
||
}
|
||
if(unit->hu_UnitNo & PCIUSBUNIT_MASK) {
|
||
ioreq->iouh_Req.io_Error = IOERR_UNITBUSY;
|
||
KPRINTF(5, "Unit %ld already open!\n", unitnr);
|
||
return NULL;
|
||
}
|
||
|
||
if(uhwOpenTimer(unit, base)) {
|
||
|
||
if(pciAllocUnit(unit)) { // hardware self test
|
||
unit->hu_UnitNo |= PCIUSBUNIT_MASK; // Mark the unit as allocated/opened
|
||
|
||
unit->hu_NakTimeoutInt.is_Node.ln_Type = NT_INTERRUPT;
|
||
unit->hu_NakTimeoutInt.is_Node.ln_Name = "PCIUSB NakTimeout";
|
||
unit->hu_NakTimeoutInt.is_Node.ln_Pri = -16;
|
||
unit->hu_NakTimeoutInt.is_Data = unit;
|
||
unit->hu_NakTimeoutInt.is_Code = (VOID_FUNC)uhwNakTimeoutInt;
|
||
|
||
CopyMem(unit->hu_TimerReq, &unit->hu_NakTimeoutReq, sizeof(struct timerequest));
|
||
memset( &unit->hu_NakTimeoutMsgPort, 0, sizeof( unit->hu_NakTimeoutMsgPort ) );
|
||
unit->hu_NakTimeoutMsgPort.mp_Node.ln_Type = NT_MSGPORT;
|
||
unit->hu_NakTimeoutMsgPort.mp_Flags = PA_SOFTINT;
|
||
unit->hu_NakTimeoutMsgPort.mp_SigTask = &unit->hu_NakTimeoutInt;
|
||
NewList(&unit->hu_NakTimeoutMsgPort.mp_MsgList);
|
||
unit->hu_NakTimeoutReq.tr_node.io_Message.mn_ReplyPort = &unit->hu_NakTimeoutMsgPort;
|
||
Cause(&unit->hu_NakTimeoutInt);
|
||
|
||
return(&unit->hu_Unit);
|
||
} else {
|
||
ioreq->iouh_Req.io_Error = IOERR_SELFTEST;
|
||
KPRINTF(20, "Hardware allocation failure!\n");
|
||
}
|
||
uhwCloseTimer(unit, base);
|
||
}
|
||
return(NULL);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "Close_Unit()" */
|
||
void Close_Unit(struct PCIDevice *base,
|
||
struct PCIUnit *unit,
|
||
struct IOUsbHWReq *ioreq)
|
||
{
|
||
/* Disable all interrupts */
|
||
unit->hu_NakTimeoutMsgPort.mp_Flags = PA_IGNORE;
|
||
#if !defined(__AROS__)
|
||
unit->hu_NakTimeoutInt.is_Node.ln_Type = NT_SOFTINT;
|
||
#endif
|
||
AbortIO((APTR) &unit->hu_NakTimeoutReq);
|
||
|
||
pciFreeUnit(unit);
|
||
|
||
uhwCloseTimer(unit, base);
|
||
unit->hu_UnitNo &= ~PCIUSBUNIT_MASK; // Mark as de-allocated
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "uhwGetUsbState()" */
|
||
UWORD uhwGetUsbState(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
return(ioreq->iouh_State = UHSF_OPERATIONAL);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdReset()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdReset(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device CMD_RESET routine.
|
||
*
|
||
* Resets the whole USB hardware. Goes into USBOperational mode right
|
||
* after. Must NOT be called from an interrupt.
|
||
*
|
||
*/
|
||
|
||
WORD cmdReset(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
KPRINTF(10, "CMD_RESET ioreq: 0x%p\n", ioreq);
|
||
|
||
uhwDelayMS(1, unit);
|
||
uhwGetUsbState(ioreq, unit, base);
|
||
|
||
if(ioreq->iouh_State & UHSF_OPERATIONAL) {
|
||
return RC_OK;
|
||
}
|
||
return UHIOERR_USBOFFLINE;
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdUsbReset()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdUsbReset(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_USBRESET routine.
|
||
*
|
||
* Resets the USB bus. Goes into USBOperational mode right after. Must
|
||
* NOT be called from an interrupt.
|
||
*
|
||
*/
|
||
|
||
WORD cmdUsbReset(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
KPRINTF(10, "UHCMD_USBRESET ioreq: 0x%p\n", ioreq);
|
||
|
||
/* FIXME */
|
||
uhwGetUsbState(ioreq, unit, base);
|
||
|
||
unit->hu_FrameCounter = 1;
|
||
unit->hu_RootHubAddr = 0;
|
||
|
||
if(ioreq->iouh_State & UHSF_OPERATIONAL) {
|
||
return RC_OK;
|
||
}
|
||
return UHIOERR_USBOFFLINE;
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdUsbResume()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdUsbResume(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_USBRESUME routine.
|
||
*
|
||
* Tries to resume from USBSuspend mode into USBOperational.
|
||
* Must NOT be called from an interrupt.
|
||
*
|
||
*/
|
||
|
||
WORD cmdUsbResume(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
KPRINTF(10, "UHCMD_USBRESUME ioreq: 0x%p\n", ioreq);
|
||
|
||
/* FIXME */
|
||
uhwGetUsbState(ioreq, unit, base);
|
||
if(ioreq->iouh_State & UHSF_OPERATIONAL) {
|
||
return RC_OK;
|
||
}
|
||
return UHIOERR_USBOFFLINE;
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdUsbSuspend()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdUsbSuspend(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_USBSUSPEND routine.
|
||
*
|
||
* Sets the USB into USBSuspend mode.
|
||
* Must NOT be called from an interrupt.
|
||
*
|
||
*/
|
||
|
||
WORD cmdUsbSuspend(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
KPRINTF(10, "UHCMD_USBSUSPEND ioreq: 0x%p\n", ioreq);
|
||
|
||
/* FIXME */
|
||
uhwGetUsbState(ioreq, unit, base);
|
||
if(ioreq->iouh_State & UHSF_SUSPENDED) {
|
||
return RC_OK;
|
||
}
|
||
return UHIOERR_USBOFFLINE;
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdUsbOper()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdUsbOper(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_USBOPER routine.
|
||
*
|
||
* Sets the USB into USBOperational mode.
|
||
* Must NOT be called from an interrupt.
|
||
*
|
||
*/
|
||
|
||
WORD cmdUsbOper(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
KPRINTF(10, "UHCMD_USBOPER ioreq: 0x%p\n", ioreq);
|
||
|
||
/* FIXME */
|
||
uhwGetUsbState(ioreq, unit, base);
|
||
if(ioreq->iouh_State & UHSF_OPERATIONAL) {
|
||
return RC_OK;
|
||
}
|
||
return UHIOERR_USBOFFLINE;
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdQueryDevice()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdQueryDevice(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_QUERYDEVICE routine.
|
||
*
|
||
* Returns information about the hardware.
|
||
*
|
||
*/
|
||
|
||
WORD cmdQueryDevice(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct TagItem *taglist = (struct TagItem *) ioreq->iouh_Data;
|
||
struct TagItem *tag;
|
||
ULONG count = 0;
|
||
|
||
KPRINTF(10, "UHCMD_QUERYDEVICE ioreq: 0x%p, taglist: 0x%p\n", ioreq, taglist);
|
||
|
||
if((tag = FindTagItem(UHA_State, taglist))) {
|
||
*((ULONG *) tag->ti_Data) = (ULONG) uhwGetUsbState(ioreq, unit, base);
|
||
count++;
|
||
}
|
||
if((tag = FindTagItem(UHA_Manufacturer, taglist))) {
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
struct PCIController *hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
if(hc->hc_HCIType == HCITYPE_XHCI) {
|
||
*((STRPTR *) tag->ti_Data) = "The AROS Dev Team";
|
||
}
|
||
else
|
||
#endif
|
||
*((STRPTR *) tag->ti_Data) = "Chris Hodges";
|
||
count++;
|
||
}
|
||
if((tag = FindTagItem(UHA_ProductName, taglist))) {
|
||
*((STRPTR *) tag->ti_Data) = unit->hu_ProductName;
|
||
count++;
|
||
}
|
||
if((tag = FindTagItem(UHA_Description, taglist))) {
|
||
*((STRPTR *) tag->ti_Data) = "Generic adaptive host controller driver for PCI cards";
|
||
count++;
|
||
}
|
||
if((tag = FindTagItem(UHA_Copyright, taglist))) {
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
struct PCIController *hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
if(hc->hc_HCIType == HCITYPE_XHCI) {
|
||
*((STRPTR *) tag->ti_Data) ="\xA9""2023-2025 The AROS Dev Team";
|
||
}
|
||
else
|
||
#endif
|
||
*((STRPTR *) tag->ti_Data) ="\xA9""2007-2009 Chris Hodges";
|
||
count++;
|
||
}
|
||
if((tag = FindTagItem(UHA_Version, taglist))) {
|
||
*((ULONG *) tag->ti_Data) = VERSION_NUMBER;
|
||
count++;
|
||
}
|
||
if((tag = FindTagItem(UHA_Revision, taglist))) {
|
||
*((ULONG *) tag->ti_Data) = REVISION_NUMBER;
|
||
count++;
|
||
}
|
||
if((tag = FindTagItem(UHA_DriverVersion, taglist))) {
|
||
*((ULONG *) tag->ti_Data) = 0x220;
|
||
count++;
|
||
}
|
||
if((tag = FindTagItem(UHA_Capabilities, taglist))) {
|
||
ULONG caps = 0;
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
if(unit->hu_RootHubXPorts > 0) {
|
||
caps |= UHCF_USB30;
|
||
}
|
||
#endif
|
||
if (unit->hu_RootHub20Ports > 0)
|
||
caps |= UHCF_USB20;
|
||
#if defined(PCIUSB_WIP_ISO)
|
||
caps |= UHCF_ISO|UHCF_RT_ISO;
|
||
#endif
|
||
#if defined(PCIUSB_QUICKIO)
|
||
caps |= UHCF_QUICKIO;
|
||
#endif
|
||
*((ULONG *) tag->ti_Data) = caps;
|
||
count++;
|
||
}
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
if((tag = FindTagItem(UHA_PrepareEndpoint, taglist))) {
|
||
struct PCIController *hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
if(hc->hc_HCIType == HCITYPE_XHCI) {
|
||
*((APTR *) tag->ti_Data) = xhciPrepareEndpoint;
|
||
count++;
|
||
}
|
||
}
|
||
if((tag = FindTagItem(UHA_DestroyEndpoint, taglist))) {
|
||
struct PCIController *hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
if(hc->hc_HCIType == HCITYPE_XHCI) {
|
||
*((APTR *) tag->ti_Data) = xhciDestroyEndpoint;
|
||
count++;
|
||
}
|
||
}
|
||
#endif
|
||
ioreq->iouh_Actual = count;
|
||
return RC_OK;
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdControlXFerRootHub()" */
|
||
WORD cmdControlXFerRootHub(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct PCIController *hc;
|
||
struct PCIController *chc;
|
||
UWORD rt = ioreq->iouh_SetupData.bmRequestType;
|
||
UWORD req = ioreq->iouh_SetupData.bRequest;
|
||
UWORD idx = AROS_WORD2LE(ioreq->iouh_SetupData.wIndex);
|
||
UWORD val = AROS_WORD2LE(ioreq->iouh_SetupData.wValue);
|
||
UWORD len = AROS_WORD2LE(ioreq->iouh_SetupData.wLength);
|
||
UWORD hciport;
|
||
ULONG numports = unit->hu_RootHubPorts;
|
||
|
||
KPRINTF(10, "cmdControlXFerRootHub(0x%p, 0x%p, 0x%p)\n", ioreq, unit, base);
|
||
|
||
if(ioreq->iouh_Endpoint) {
|
||
return(UHIOERR_STALL);
|
||
}
|
||
|
||
if(len != ioreq->iouh_Length) {
|
||
KPRINTF(20, "RH: Len (%ld != %ld) mismatch!\n", len != ioreq->iouh_Length);
|
||
return(UHIOERR_STALL);
|
||
}
|
||
switch(rt) {
|
||
case (URTF_STANDARD|URTF_DEVICE):
|
||
switch(req) {
|
||
case USR_SET_ADDRESS:
|
||
KPRINTF(1, "RH: SetAddress = %ld\n", val);
|
||
unit->hu_RootHubAddr = val;
|
||
ioreq->iouh_Actual = len;
|
||
return(0);
|
||
|
||
case USR_SET_CONFIGURATION:
|
||
KPRINTF(1, "RH: SetConfiguration=%ld\n", val);
|
||
ioreq->iouh_Actual = len;
|
||
return(0);
|
||
}
|
||
break;
|
||
|
||
case (URTF_IN|URTF_STANDARD|URTF_DEVICE):
|
||
switch(req) {
|
||
case USR_GET_DESCRIPTOR:
|
||
switch(val>>8) {
|
||
case UDT_DEVICE:
|
||
KPRINTF(1, "RH: GetDeviceDescriptor (%ld)\n", len);
|
||
ioreq->iouh_Actual = (len > sizeof(struct UsbStdDevDesc)) ? sizeof(struct UsbStdDevDesc) : len;
|
||
CopyMem((APTR) &RHDevDesc, ioreq->iouh_Data, ioreq->iouh_Actual);
|
||
if(ioreq->iouh_Length >= sizeof(struct UsbStdDevDesc)) {
|
||
struct UsbStdDevDesc *usdd = (struct UsbStdDevDesc *) ioreq->iouh_Data;
|
||
hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
usdd->idVendor = WORD2LE(hc->hc_VendID);
|
||
usdd->idProduct = WORD2LE(hc->hc_ProdID);
|
||
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
if((hc->hc_HCIType == HCITYPE_XHCI) && (unit->hu_RootHubXPorts)) {
|
||
KPRINTF(1, "RH: XHCI (USB3) Hub Descriptor\n");
|
||
usdd->bcdUSB = AROS_WORD2LE(0x0300); /* USB 3.0 */
|
||
usdd->bDeviceClass = HUB_CLASSCODE; /* 9 */
|
||
usdd->bDeviceSubClass= 0;
|
||
usdd->bDeviceProtocol= 3; /* USB3 hub */
|
||
usdd->bMaxPacketSize0 = 64; /* 512 bytes (2^9) encoded as 64 here */
|
||
} else
|
||
#endif
|
||
if(unit->hu_RootHub20Ports) {
|
||
KPRINTF(1, "RH: USB2 Hub Descriptor\n");
|
||
usdd->bcdUSB = AROS_WORD2LE(0x0200); // signal a highspeed root hub
|
||
usdd->bDeviceProtocol = 1; // single TT
|
||
usdd->bMaxPacketSize0 = 64;
|
||
}
|
||
|
||
}
|
||
return(0);
|
||
|
||
case UDT_CONFIGURATION: {
|
||
UBYTE tmpbuf[sizeof(struct UsbStdCfgDesc) + sizeof(struct UsbStdIfDesc) + sizeof(struct UsbStdEPDesc)];
|
||
KPRINTF(1, "RH: GetConfigDescriptor (%ld)\n", len);
|
||
CopyMem((APTR) &RHCfgDesc,
|
||
tmpbuf,
|
||
sizeof(struct UsbStdCfgDesc));
|
||
CopyMem((APTR) &RHIfDesc,
|
||
&tmpbuf[sizeof(struct UsbStdCfgDesc)],
|
||
sizeof(struct UsbStdIfDesc));
|
||
CopyMem((APTR) &RHEPDesc,
|
||
&tmpbuf[sizeof(struct UsbStdCfgDesc) + sizeof(struct UsbStdIfDesc)],
|
||
sizeof(struct UsbStdEPDesc));
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
if(hc->hc_HCIType == HCITYPE_XHCI) {
|
||
struct UsbStdEPDesc *usepd = (struct UsbStdEPDesc *) &tmpbuf[sizeof(struct UsbStdCfgDesc) + sizeof(struct UsbStdIfDesc)];
|
||
KPRINTF(1, "RH: XHCI EndPoint Config\n");
|
||
usepd->bInterval = 12; // * 1ms, or 125<32>s, = 2048 <20>Frames
|
||
usepd->wMaxPacketSize = AROS_WORD2LE(64);
|
||
} else
|
||
#endif
|
||
if(unit->hu_RootHub20Ports) {
|
||
struct UsbStdEPDesc *usepd = (struct UsbStdEPDesc *) &tmpbuf[sizeof(struct UsbStdCfgDesc) + sizeof(struct UsbStdIfDesc)];
|
||
usepd->bInterval = 12; // 2048 <20>Frames
|
||
usepd->wMaxPacketSize = AROS_WORD2LE(64);
|
||
}
|
||
ioreq->iouh_Actual = (len > sizeof(struct UsbStdCfgDesc) + sizeof(struct UsbStdIfDesc) + sizeof(struct UsbStdEPDesc)) ? (sizeof(struct UsbStdCfgDesc) + sizeof(struct UsbStdIfDesc) + sizeof(struct UsbStdEPDesc)) : len;
|
||
CopyMem(tmpbuf, ioreq->iouh_Data, ioreq->iouh_Actual);
|
||
return(0);
|
||
}
|
||
|
||
case UDT_STRING:
|
||
if(val & 0xff) { /* get lang array */
|
||
CONST_STRPTR source = NULL, rhstring;
|
||
UWORD *mptr = ioreq->iouh_Data;
|
||
UWORD slen = 1;
|
||
KPRINTF(1, "RH: GetString %04lx (%ld)\n", val, len);
|
||
if((val & 0xff) > 4) { /* index too high? */
|
||
return(UHIOERR_STALL);
|
||
}
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
if(hc->hc_HCIType == HCITYPE_XHCI) {
|
||
rhstring = RHxStrings[(val & 0xff)-1];
|
||
}
|
||
else
|
||
#endif
|
||
rhstring = RHStrings[(val & 0xff)-1];
|
||
source = rhstring;
|
||
if(len > 1) {
|
||
ioreq->iouh_Actual = 2;
|
||
while(*source++) {
|
||
slen++;
|
||
}
|
||
source = rhstring;
|
||
*mptr++ = AROS_WORD2BE((slen<<9)|UDT_STRING);
|
||
while(ioreq->iouh_Actual+1 < len) {
|
||
// special hack for unit number in root hub string
|
||
if(((val & 0xff) == 2) && (source[1] == 0)) {
|
||
*mptr++ = AROS_WORD2LE('0' + (unit->hu_UnitNo & ~PCIUSBUNIT_MASK));
|
||
} else {
|
||
*mptr++ = AROS_WORD2LE(*source);
|
||
}
|
||
source++;
|
||
ioreq->iouh_Actual += 2;
|
||
if(!(*source)) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
UWORD *mptr = ioreq->iouh_Data;
|
||
KPRINTF(1, "RH: GetLangArray %04lx (%ld)\n", val, len);
|
||
if(len > 1) {
|
||
ioreq->iouh_Actual = 2;
|
||
mptr[0] = AROS_WORD2BE((4<<8)|UDT_STRING);
|
||
if(len > 3) {
|
||
ioreq->iouh_Actual += 2;
|
||
mptr[1] = AROS_WORD2LE(0x0409);
|
||
}
|
||
}
|
||
}
|
||
return(0);
|
||
|
||
default:
|
||
KPRINTF(1, "RH: Unsupported Descriptor %04lx\n", idx);
|
||
}
|
||
break;
|
||
|
||
case USR_GET_CONFIGURATION:
|
||
if(len == 1) {
|
||
KPRINTF(1, "RH: GetConfiguration\n");
|
||
((UBYTE *) ioreq->iouh_Data)[0] = 1;
|
||
ioreq->iouh_Actual = len;
|
||
return(0);
|
||
}
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case (URTF_CLASS|URTF_OTHER):
|
||
switch(req) {
|
||
case USR_SET_FEATURE: {
|
||
WORD retval = 0;
|
||
if((!idx) || (idx > numports)) {
|
||
KPRINTF(20, "Port %ld out of range\n", idx);
|
||
return(UHIOERR_STALL);
|
||
}
|
||
chc = unit->hu_PortMap11[idx - 1];
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
if(unit->hu_PortOwner[idx - 1] == HCITYPE_XHCI) {
|
||
hc = unit->hu_PortMapX[idx - 1];
|
||
hciport = idx - 1;
|
||
} else
|
||
#endif
|
||
if(unit->hu_PortOwner[idx - 1] == HCITYPE_EHCI) {
|
||
hc = unit->hu_PortMap20[idx - 1];
|
||
hciport = idx - 1;
|
||
} else {
|
||
hc = chc;
|
||
hciport = unit->hu_PortNum11[idx - 1];
|
||
}
|
||
KPRINTF(10, "Set Feature %ld maps from glob. Port %ld to local Port %ld (%s)\n", val, idx, hciport,
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
(unit->hu_PortOwner[idx - 1] == HCITYPE_XHCI) ? "XHCI" :
|
||
#endif
|
||
(unit->hu_PortOwner[idx - 1] == HCITYPE_EHCI) ? "EHCI" : "U/OHCI");
|
||
switch(hc->hc_HCIType) {
|
||
case HCITYPE_UHCI: {
|
||
if (uhciSetFeature(unit, hc, hciport, idx, val, &retval)) {
|
||
KPRINTF(5, "uhciSetFeature returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case HCITYPE_OHCI: {
|
||
if (ohciSetFeature(unit, hc, hciport, idx, val, &retval)) {
|
||
KPRINTF(5, "ohciSetFeature returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case HCITYPE_EHCI: {
|
||
if (ehciSetFeature(unit, hc, hciport, idx, val, &retval)) {
|
||
KPRINTF(5, "ehciSetFeature returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
case HCITYPE_XHCI: {
|
||
if (xhciSetFeature(unit, hc, hciport, idx, val, &retval)) {
|
||
KPRINTF(5, "xhciSetFeature returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
#endif
|
||
}
|
||
}
|
||
break;
|
||
|
||
case USR_CLEAR_FEATURE: {
|
||
WORD retval = 0;
|
||
if((!idx) || (idx > numports)) {
|
||
KPRINTF(20, "Port %ld out of range\n", idx);
|
||
return(UHIOERR_STALL);
|
||
}
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
if(unit->hu_PortOwner[idx - 1] == HCITYPE_XHCI) {
|
||
hc = unit->hu_PortMapX[idx - 1];
|
||
hciport = idx - 1;
|
||
} else
|
||
#endif
|
||
if(unit->hu_PortOwner[idx - 1] == HCITYPE_EHCI) {
|
||
hc = unit->hu_PortMap20[idx - 1];
|
||
hciport = idx - 1;
|
||
} else {
|
||
hc = unit->hu_PortMap11[idx - 1];
|
||
hciport = unit->hu_PortNum11[idx - 1];
|
||
}
|
||
KPRINTF(10, "Clear Feature %ld maps from glob. Port %ld to local Port %ld (%s)\n", val, idx, hciport,
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
(unit->hu_PortOwner[idx - 1] == HCITYPE_XHCI) ? "XHCI" :
|
||
#endif
|
||
(unit->hu_PortOwner[idx - 1] == HCITYPE_EHCI) ? "EHCI" : "U/OHCI");
|
||
switch(hc->hc_HCIType) {
|
||
case HCITYPE_UHCI: {
|
||
if (uhciClearFeature(unit, hc, hciport, idx, val, &retval)) {
|
||
KPRINTF(5, "uhciClearFeature returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case HCITYPE_OHCI: {
|
||
if (ohciClearFeature(unit, hc, hciport, idx, val, &retval)) {
|
||
KPRINTF(5, "ohciClearFeature returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case HCITYPE_EHCI: {
|
||
if (ehciClearFeature(unit, hc, hciport, idx, val, &retval)) {
|
||
KPRINTF(5, "ehciClearFeature returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
case HCITYPE_XHCI: {
|
||
if (xhciClearFeature(unit, hc, hciport, idx, val, &retval)) {
|
||
KPRINTF(5, "xhciClearFeature returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
#endif
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case (URTF_IN|URTF_CLASS|URTF_OTHER):
|
||
switch(req) {
|
||
case USR_GET_STATUS: {
|
||
UWORD *mptr = ioreq->iouh_Data;
|
||
WORD retval = 0;
|
||
|
||
if(len != sizeof(struct UsbPortStatus)) {
|
||
return(UHIOERR_STALL);
|
||
}
|
||
if((!idx) || (idx > numports)) {
|
||
KPRINTF(20, "Port %ld out of range\n", idx);
|
||
return(UHIOERR_STALL);
|
||
}
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
if(unit->hu_PortOwner[idx - 1] == HCITYPE_XHCI) {
|
||
hc = unit->hu_PortMapX[idx - 1];
|
||
hciport = idx - 1;
|
||
} else
|
||
#endif
|
||
if(unit->hu_PortOwner[idx - 1] == HCITYPE_EHCI) {
|
||
hc = unit->hu_PortMap20[idx - 1];
|
||
hciport = idx - 1;
|
||
} else {
|
||
hc = unit->hu_PortMap11[idx - 1];
|
||
hciport = unit->hu_PortNum11[idx - 1];
|
||
}
|
||
switch(hc->hc_HCIType) {
|
||
case HCITYPE_UHCI: {
|
||
if (uhciGetStatus(hc, mptr, hciport, idx, &retval)) {
|
||
KPRINTF(5, "uhciGetStatus returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case HCITYPE_OHCI: {
|
||
if (ohciGetStatus(hc, mptr, hciport, idx, &retval)) {
|
||
KPRINTF(5, "ohciGetStatus returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case HCITYPE_EHCI: {
|
||
if (ehciGetStatus(hc, mptr, hciport, idx, &retval)) {
|
||
KPRINTF(5, "ehciGetStatus returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
case HCITYPE_XHCI: {
|
||
if (xhciGetStatus(hc, mptr, hciport, idx, &retval)) {
|
||
KPRINTF(5, "xhciGetStatus returned (retval %04x)\n", retval);
|
||
return(retval);
|
||
}
|
||
}
|
||
break;
|
||
#endif
|
||
}
|
||
return(0);
|
||
}
|
||
|
||
}
|
||
break;
|
||
|
||
case (URTF_IN|URTF_CLASS|URTF_DEVICE):
|
||
switch(req) {
|
||
case USR_GET_STATUS: {
|
||
UWORD *mptr = ioreq->iouh_Data;
|
||
|
||
KPRINTF(1, "RH: GetHubStatus (%ld)\n", len);
|
||
|
||
if(len < sizeof(struct UsbHubStatus)) {
|
||
return(UHIOERR_STALL);
|
||
}
|
||
*mptr++ = 0;
|
||
*mptr++ = 0;
|
||
ioreq->iouh_Actual = 4;
|
||
return(0);
|
||
}
|
||
|
||
case USR_GET_DESCRIPTOR:
|
||
switch(val>>8) {
|
||
case UDT_SSHUB: {
|
||
struct UsbSSHubDesc *shd;
|
||
UWORD sslen = sizeof(struct UsbSSHubDesc);
|
||
|
||
KPRINTF(1, "RH: GetSuperSpeedHubDescriptor (%ld)\n", len);
|
||
|
||
#if !defined(PCIUSB_ENABLEXHCI)
|
||
/* No xHCI -> no SuperSpeed hub */
|
||
return UHIOERR_STALL;
|
||
#else
|
||
/* Only make sense if we actually have an xHCI root */
|
||
hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
if (!hc || hc->hc_HCIType != HCITYPE_XHCI)
|
||
return UHIOERR_STALL;
|
||
|
||
|
||
ioreq->iouh_Actual = (len > sslen) ? sslen : len;
|
||
CopyMem((APTR)&RHHubSSDesc, ioreq->iouh_Data, ioreq->iouh_Actual);
|
||
|
||
if (ioreq->iouh_Length >= sslen) {
|
||
shd = (struct UsbSSHubDesc *)ioreq->iouh_Data;
|
||
|
||
/* Number of SS ports */
|
||
shd->bNbrPorts = unit->hu_RootHubXPorts;
|
||
|
||
/* wHubCharacteristics: at least indicate per-port power if PPC set */
|
||
{
|
||
UWORD characteristics = 0;
|
||
|
||
if (hc->hc_Flags & HCF_PPC)
|
||
characteristics |= UHCF_INDIVID_POWER;
|
||
|
||
shd->wHubCharacteristics = WORD2LE(characteristics);
|
||
}
|
||
|
||
/* bPwrOn2PwrGood <20> USB3 spec suggests up to 20ms typical for xHCI */
|
||
shd->bPwrOn2PwrGood = 10; /* 10 * 2ms = 20ms */
|
||
|
||
/* wHubDelay / bHubHdrDecLat left at 0 for now */
|
||
}
|
||
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
case UDT_HUB: {
|
||
UWORD hubdesclen = 9;
|
||
UWORD characteristics = UHCF_INDIVID_OVP;
|
||
UBYTE powergood = 1;
|
||
|
||
struct UsbHubDesc *uhd = (struct UsbHubDesc *) ioreq->iouh_Data;
|
||
KPRINTF(1, "RH: GetHubDescriptor (%ld)\n", len);
|
||
|
||
if(unit->hu_RootHubPorts > 7) { // needs two bytes for port masks
|
||
hubdesclen += 2;
|
||
}
|
||
|
||
ioreq->iouh_Actual = (len > hubdesclen) ? hubdesclen : len;
|
||
CopyMem((APTR) &RHHubDesc, ioreq->iouh_Data, ioreq->iouh_Actual);
|
||
|
||
if(ioreq->iouh_Length) {
|
||
uhd->bLength = hubdesclen;
|
||
}
|
||
|
||
if(ioreq->iouh_Length >= 5) {
|
||
hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
while(hc->hc_Node.ln_Succ) {
|
||
if(hc->hc_HCIType == HCITYPE_OHCI) {
|
||
ULONG localpwgood = (READREG32_LE(hc->hc_RegBase, OHCI_HUBDESCA) & OHAM_POWERGOOD) >> OHAS_POWERGOOD;
|
||
characteristics |= UHCF_INDIVID_POWER;
|
||
if(localpwgood > powergood)
|
||
powergood = localpwgood;
|
||
}
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
else if (hc->hc_HCIType == HCITYPE_XHCI) {
|
||
powergood = 10; /* 20ms max (Section 5.4.9) */
|
||
if (hc->hc_Flags & HCF_PPC)
|
||
characteristics |= UHCF_INDIVID_POWER;
|
||
}
|
||
#endif
|
||
else characteristics |= UHCF_INDIVID_POWER;
|
||
|
||
hc = (struct PCIController *) hc->hc_Node.ln_Succ;
|
||
}
|
||
uhd->wHubCharacteristics = WORD2LE(characteristics);
|
||
|
||
if(ioreq->iouh_Length >= 6) {
|
||
if (powergood > 1) {
|
||
KPRINTF(10, "Increasing power good time to %ld\n", powergood);
|
||
}
|
||
uhd->bPwrOn2PwrGood = powergood;
|
||
}
|
||
}
|
||
|
||
if(ioreq->iouh_Length >= hubdesclen) {
|
||
uhd->bNbrPorts = unit->hu_RootHubPorts;
|
||
if(hubdesclen == 9) {
|
||
uhd->DeviceRemovable = 0;
|
||
uhd->PortPwrCtrlMask = (1<<(unit->hu_RootHubPorts+2))-2;
|
||
} else {
|
||
// each field is now 16 bits wide
|
||
uhd->DeviceRemovable = 0;
|
||
uhd->PortPwrCtrlMask = 0;
|
||
((UBYTE *) ioreq->iouh_Data)[9] = (1<<(unit->hu_RootHubPorts+2))-2;
|
||
((UBYTE *) ioreq->iouh_Data)[10] = ((1<<(unit->hu_RootHubPorts+2))-2)>>8;
|
||
}
|
||
}
|
||
return(0);
|
||
}
|
||
|
||
default:
|
||
KPRINTF(20, "RH: Unsupported Descriptor %04lx\n", idx);
|
||
}
|
||
break;
|
||
}
|
||
|
||
}
|
||
KPRINTF(20, DEBUGWARNCOLOR_SET "RH: Unsupported command %02lx %02lx %04lx %04lx %04lx!" DEBUGCOLOR_RESET "\n", rt, req, idx, val, len);
|
||
return(UHIOERR_STALL);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdIntXFerRootHub()" */
|
||
WORD cmdIntXFerRootHub(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
KPRINTF(10, "cmdIntXFerRootHub(0x%p, 0x%p, 0x%p)\n", ioreq, unit, base);
|
||
|
||
if((ioreq->iouh_Endpoint != 1) || (!ioreq->iouh_Length)) {
|
||
return(UHIOERR_STALL);
|
||
}
|
||
|
||
if(unit->hu_RootPortChanges) {
|
||
KPRINTF(1, "Immediate Portchange map %04lx\n", unit->hu_RootPortChanges);
|
||
if((unit->hu_RootHubPorts < 8) || (ioreq->iouh_Length == 1)) {
|
||
*((UBYTE *) ioreq->iouh_Data) = unit->hu_RootPortChanges;
|
||
ioreq->iouh_Actual = 1;
|
||
} else {
|
||
((UBYTE *) ioreq->iouh_Data)[0] = unit->hu_RootPortChanges;
|
||
((UBYTE *) ioreq->iouh_Data)[1] = unit->hu_RootPortChanges>>8;
|
||
ioreq->iouh_Actual = 2;
|
||
}
|
||
unit->hu_RootPortChanges = 0;
|
||
return(0);
|
||
}
|
||
ioreq->iouh_Req.io_Flags &= ~IOF_QUICK;
|
||
Disable();
|
||
AddTail(&unit->hu_RHIOQueue, (struct Node *) ioreq);
|
||
Enable();
|
||
return(RC_DONTREPLY);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdControlXFer()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdControlXFer(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_CONTROLXFER routine.
|
||
*
|
||
* First it check if the usb is in proper state and if user passed arguments
|
||
* are valid. If everything is ok, the request is linked to queue of
|
||
* pending transfer requests.
|
||
*
|
||
*/
|
||
|
||
WORD cmdControlXFer(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct PCIController *hc;
|
||
|
||
KPRINTF(10, "UHCMD_CONTROLXFER ioreq: 0x%p\n", ioreq);
|
||
uhwGetUsbState(ioreq, unit, base);
|
||
if(!(ioreq->iouh_State & UHSF_OPERATIONAL)) {
|
||
return(UHIOERR_USBOFFLINE);
|
||
}
|
||
/* Root hub emulation */
|
||
if(ioreq->iouh_DevAddr == unit->hu_RootHubAddr) {
|
||
return(cmdControlXFerRootHub(ioreq, unit, base));
|
||
}
|
||
|
||
hc = unit->hu_DevControllers[ioreq->iouh_DevAddr];
|
||
if(!hc) {
|
||
KPRINTF(20, "No Host controller assigned to device address %ld\n", ioreq->iouh_DevAddr);
|
||
return(UHIOERR_HOSTERROR);
|
||
}
|
||
|
||
ioreq->iouh_Req.io_Flags &= ~IOF_QUICK;
|
||
ioreq->iouh_Actual = 0;
|
||
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
if (hc->hc_HCIType == HCITYPE_XHCI)
|
||
xhciPrepareTransfer(ioreq, unit, base);
|
||
#endif
|
||
|
||
Disable();
|
||
AddTail(&hc->hc_CtrlXFerQueue, (struct Node *) ioreq);
|
||
Enable();
|
||
SureCause(base, &hc->hc_CompleteInt);
|
||
|
||
KPRINTF(10, "UHCMD_CONTROLXFER processed ioreq: 0x%p\n", ioreq);
|
||
return(RC_DONTREPLY);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdBulkXFer()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdBulkXFer(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_BULKXFER routine.
|
||
*
|
||
* First it check if the usb is in proper state and if user passed arguments
|
||
* are valid. If everything is ok, the request is linked to queue of
|
||
* pending transfer requests.
|
||
*
|
||
*/
|
||
|
||
WORD cmdBulkXFer(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct PCIController *hc;
|
||
|
||
KPRINTF(10, "UHCMD_BULKXFER ioreq: 0x%p\n", ioreq);
|
||
uhwGetUsbState(ioreq, unit, base);
|
||
if(!(ioreq->iouh_State & UHSF_OPERATIONAL)) {
|
||
return(UHIOERR_USBOFFLINE);
|
||
}
|
||
|
||
if(ioreq->iouh_Flags & UHFF_LOWSPEED) {
|
||
return(UHIOERR_BADPARAMS);
|
||
}
|
||
|
||
hc = unit->hu_DevControllers[ioreq->iouh_DevAddr];
|
||
if(!hc) {
|
||
KPRINTF(20, "No Host controller assigned to device address %ld\n", ioreq->iouh_DevAddr);
|
||
return(UHIOERR_HOSTERROR);
|
||
}
|
||
|
||
ioreq->iouh_Req.io_Flags &= ~IOF_QUICK;
|
||
ioreq->iouh_Actual = 0;
|
||
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
if (hc->hc_HCIType == HCITYPE_XHCI)
|
||
xhciPrepareTransfer(ioreq, unit, base);
|
||
#endif
|
||
|
||
Disable();
|
||
AddTail(&hc->hc_BulkXFerQueue, (struct Node *) ioreq);
|
||
Enable();
|
||
SureCause(base, &hc->hc_CompleteInt);
|
||
|
||
KPRINTF(10, "UHCMD_BULKXFER processed ioreq: 0x%p\n", ioreq);
|
||
return(RC_DONTREPLY);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdIsoXFer()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdIsoXFer(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_ISOXFER routine.
|
||
*
|
||
* First it check if the usb is in proper state and if user passed arguments
|
||
* are valid. If everything is ok, the request is linked to queue of
|
||
* pending transfer requests.
|
||
*
|
||
*/
|
||
|
||
WORD cmdIsoXFer(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct PCIController *hc;
|
||
|
||
KPRINTF(10, "UHCMD_ISOXFER ioreq: 0x%p\n", ioreq);
|
||
uhwGetUsbState(ioreq, unit, base);
|
||
if(!(ioreq->iouh_State & UHSF_OPERATIONAL)) {
|
||
return(UHIOERR_USBOFFLINE);
|
||
}
|
||
|
||
if(ioreq->iouh_Flags & UHFF_LOWSPEED) {
|
||
return(UHIOERR_BADPARAMS);
|
||
}
|
||
|
||
hc = unit->hu_DevControllers[ioreq->iouh_DevAddr];
|
||
if(!hc) {
|
||
KPRINTF(20, "No Host controller assigned to device address %ld\n", ioreq->iouh_DevAddr);
|
||
return(UHIOERR_HOSTERROR);
|
||
}
|
||
|
||
ioreq->iouh_Req.io_Flags &= ~IOF_QUICK;
|
||
ioreq->iouh_Actual = 0;
|
||
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
if (hc->hc_HCIType == HCITYPE_XHCI)
|
||
xhciPrepareTransfer(ioreq, unit, base);
|
||
#endif
|
||
|
||
Disable();
|
||
AddTail(&hc->hc_IsoXFerQueue, (struct Node *) ioreq);
|
||
Enable();
|
||
SureCause(base, &hc->hc_CompleteInt);
|
||
|
||
KPRINTF(10, "UHCMD_ISOXFER processed ioreq: 0x%p\n", ioreq);
|
||
return(RC_DONTREPLY);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdIntXFer()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdIntXFer(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_INTXFER routine.
|
||
*
|
||
* First it check if the usb is in proper state and if user passed arguments
|
||
* are valid. If everything is ok, the request is linked to queue of
|
||
* pending transfer requests.
|
||
*
|
||
*/
|
||
|
||
WORD cmdIntXFer(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct PCIController *hc;
|
||
|
||
KPRINTF(10, "UHCMD_INTXFER ioreq: 0x%p\n", ioreq);
|
||
//uhwDelayMS(1000, unit); /* Wait 200 ms */
|
||
uhwGetUsbState(ioreq, unit, base);
|
||
if(!(ioreq->iouh_State & UHSF_OPERATIONAL)) {
|
||
return(UHIOERR_USBOFFLINE);
|
||
}
|
||
|
||
/* Root Hub Emulation */
|
||
if(ioreq->iouh_DevAddr == unit->hu_RootHubAddr) {
|
||
return(cmdIntXFerRootHub(ioreq, unit, base));
|
||
}
|
||
|
||
hc = unit->hu_DevControllers[ioreq->iouh_DevAddr];
|
||
if(!hc) {
|
||
KPRINTF(20, "No Host controller assigned to device address %ld\n", ioreq->iouh_DevAddr);
|
||
return(UHIOERR_HOSTERROR);
|
||
}
|
||
|
||
ioreq->iouh_Req.io_Flags &= ~IOF_QUICK;
|
||
ioreq->iouh_Actual = 0;
|
||
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
if (hc->hc_HCIType == HCITYPE_XHCI)
|
||
xhciPrepareTransfer(ioreq, unit, base);
|
||
#endif
|
||
|
||
Disable();
|
||
AddTail(&hc->hc_IntXFerQueue, (struct Node *) ioreq);
|
||
Enable();
|
||
SureCause(base, &hc->hc_CompleteInt);
|
||
|
||
KPRINTF(10, "UHCMD_INTXFER processed ioreq: 0x%p\n", ioreq);
|
||
return(RC_DONTREPLY);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdFlush()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdFlush(ioreq, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device CMD_FLUSH routine.
|
||
*
|
||
* This routine abort all pending transfer requests.
|
||
*
|
||
*/
|
||
|
||
WORD cmdFlush(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct IOUsbHWReq *cmpioreq;
|
||
struct PCIController *hc;
|
||
UWORD devadrep;
|
||
|
||
KPRINTF(10, "CMD_FLUSH ioreq: 0x%p\n", ioreq);
|
||
|
||
Disable();
|
||
cmpioreq = (struct IOUsbHWReq *) unit->hu_RHIOQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
Remove(&cmpioreq->iouh_Req.io_Message.mn_Node);
|
||
cmpioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
ReplyMsg(&cmpioreq->iouh_Req.io_Message);
|
||
cmpioreq = (struct IOUsbHWReq *) unit->hu_RHIOQueue.lh_Head;
|
||
}
|
||
hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
while(hc->hc_Node.ln_Succ) {
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_CtrlXFerQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
Remove(&cmpioreq->iouh_Req.io_Message.mn_Node);
|
||
cmpioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
ReplyMsg(&cmpioreq->iouh_Req.io_Message);
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_CtrlXFerQueue.lh_Head;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_IntXFerQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
Remove(&cmpioreq->iouh_Req.io_Message.mn_Node);
|
||
cmpioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
ReplyMsg(&cmpioreq->iouh_Req.io_Message);
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_IntXFerQueue.lh_Head;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_IsoXFerQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
Remove(&cmpioreq->iouh_Req.io_Message.mn_Node);
|
||
cmpioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
ReplyMsg(&cmpioreq->iouh_Req.io_Message);
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_IsoXFerQueue.lh_Head;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_BulkXFerQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
Remove(&cmpioreq->iouh_Req.io_Message.mn_Node);
|
||
cmpioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
ReplyMsg(&cmpioreq->iouh_Req.io_Message);
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_BulkXFerQueue.lh_Head;
|
||
}
|
||
switch(hc->hc_HCIType) {
|
||
case HCITYPE_UHCI:
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_TDQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
Remove(&cmpioreq->iouh_Req.io_Message.mn_Node);
|
||
devadrep = (cmpioreq->iouh_DevAddr<<5) + cmpioreq->iouh_Endpoint + ((cmpioreq->iouh_Dir == UHDIR_IN) ? 0x10 : 0);
|
||
unit->hu_DevBusyReq[devadrep] = NULL;
|
||
uhciFreeQContext(hc, (struct UhciQH *) cmpioreq->iouh_DriverPrivate1);
|
||
cmpioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
ReplyMsg(&cmpioreq->iouh_Req.io_Message);
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_TDQueue.lh_Head;
|
||
}
|
||
break;
|
||
|
||
case HCITYPE_EHCI:
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_TDQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
ehciFreeAsyncContext(hc, cmpioreq);
|
||
cmpioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
ReplyMsg(&cmpioreq->iouh_Req.io_Message);
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_TDQueue.lh_Head;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_PeriodicTDQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
ehciFreePeriodicContext(hc, cmpioreq);
|
||
cmpioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
ReplyMsg(&cmpioreq->iouh_Req.io_Message);
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_PeriodicTDQueue.lh_Head;
|
||
}
|
||
break;
|
||
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
case HCITYPE_XHCI:
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
xhciFreeAsyncContext(hc, unit, cmpioreq);
|
||
cmpioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
ReplyMsg(&cmpioreq->iouh_Req.io_Message);
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_TDQueue.lh_Head;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_PeriodicTDQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
xhciFreePeriodicContext(hc, unit, cmpioreq);
|
||
cmpioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
ReplyMsg(&cmpioreq->iouh_Req.io_Message);
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_PeriodicTDQueue.lh_Head;
|
||
}
|
||
break;
|
||
#endif
|
||
}
|
||
hc = (struct PCIController *) hc->hc_Node.ln_Succ;
|
||
}
|
||
Enable();
|
||
/* Return success
|
||
*/
|
||
return RC_OK;
|
||
}
|
||
/* \\\ */
|
||
|
||
#if defined(PCIUSB_WIP_ISO)
|
||
/* /// "cmdAddIsoHandler()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdAddIsoHandler(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_ADDISOHANDLER routine.
|
||
*
|
||
* First it check if the usb is in proper state and if user passed arguments
|
||
* are valid. If everything is ok, the request is linked to the list of
|
||
* realtime iso handlers.
|
||
*
|
||
*/
|
||
|
||
WORD cmdAddIsoHandler(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct PCIController *hc;
|
||
struct RTIsoNode *rtn;
|
||
|
||
WORD retval;
|
||
UWORD requested_ptds;
|
||
|
||
KPRINTF(10, "UHCMD_ADDISOHANDLER ioreq: 0x%08lx\n", ioreq);
|
||
|
||
//uhwDelayMS(1000, unit); /* Wait 200 ms */
|
||
uhwGetUsbState(ioreq, unit, base);
|
||
if(!(ioreq->iouh_State & UHSF_OPERATIONAL)) {
|
||
return(UHIOERR_USBOFFLINE);
|
||
}
|
||
|
||
/* Root Hub Emulation */
|
||
if(ioreq->iouh_DevAddr == unit->hu_RootHubAddr) {
|
||
return(UHIOERR_BADPARAMS);
|
||
}
|
||
|
||
hc = unit->hu_DevControllers[ioreq->iouh_DevAddr];
|
||
if(!hc) {
|
||
return(UHIOERR_HOSTERROR);
|
||
}
|
||
if(!ioreq->iouh_Data) {
|
||
return(UHIOERR_BADPARAMS);
|
||
}
|
||
|
||
ioreq->iouh_Actual = 0;
|
||
|
||
Disable();
|
||
rtn = (struct RTIsoNode *) RemHead((struct List *) &unit->hu_FreeRTIsoNodes);
|
||
Enable();
|
||
|
||
requested_ptds = hc->hc_IsoPTDCount ? hc->hc_IsoPTDCount : PCIUSB_ISO_PTD_COUNT;
|
||
if(requested_ptds < 2)
|
||
requested_ptds = 2;
|
||
|
||
if(rtn->rtn_PTDs && rtn->rtn_PTDCount != requested_ptds) {
|
||
FreeMem(rtn->rtn_PTDs, rtn->rtn_PTDCount * sizeof(struct PTDNode *));
|
||
rtn->rtn_PTDs = NULL;
|
||
rtn->rtn_PTDCount = 0;
|
||
}
|
||
|
||
if(!rtn->rtn_PTDs) {
|
||
rtn->rtn_PTDs = AllocMem(requested_ptds * sizeof(struct PTDNode *), MEMF_CLEAR);
|
||
rtn->rtn_PTDCount = rtn->rtn_PTDs ? requested_ptds : 0;
|
||
}
|
||
|
||
if(!rtn->rtn_PTDs) {
|
||
AddTail((struct List *) &unit->hu_FreeRTIsoNodes, (struct Node *) &rtn->rtn_Node);
|
||
return UHIOERR_OUTOFMEMORY;
|
||
}
|
||
|
||
/* copy some variables */
|
||
rtn->rtn_IOReq.iouh_Flags = ioreq->iouh_Flags;
|
||
rtn->rtn_IOReq.iouh_Dir = ioreq->iouh_Dir;
|
||
rtn->rtn_IOReq.iouh_DevAddr = ioreq->iouh_DevAddr;
|
||
rtn->rtn_IOReq.iouh_Endpoint = ioreq->iouh_Endpoint;
|
||
rtn->rtn_IOReq.iouh_MaxPktSize = ioreq->iouh_MaxPktSize;
|
||
rtn->rtn_IOReq.iouh_Interval = ioreq->iouh_Interval;
|
||
rtn->rtn_IOReq.iouh_SplitHubAddr = ioreq->iouh_SplitHubAddr;
|
||
rtn->rtn_IOReq.iouh_SplitHubPort = ioreq->iouh_SplitHubPort;
|
||
|
||
rtn->rtn_RTIso = (struct IOUsbHWRTIso *) ioreq->iouh_Data;
|
||
|
||
switch(hc->hc_HCIType) {
|
||
case HCITYPE_XHCI:
|
||
retval = xhciInitIsochIO(hc, rtn);
|
||
break;
|
||
case HCITYPE_EHCI:
|
||
retval = ehciInitIsochIO(hc, rtn);
|
||
break;
|
||
case HCITYPE_UHCI:
|
||
retval = uhciInitIsochIO(hc, rtn);
|
||
break;
|
||
default:
|
||
retval = ohciInitIsochIO(hc, rtn);
|
||
break;
|
||
};
|
||
|
||
if (retval != RC_OK) {
|
||
Disable();
|
||
AddTail((struct List *) &unit->hu_FreeRTIsoNodes, (struct Node *) &rtn->rtn_Node);
|
||
Enable();
|
||
return retval;
|
||
}
|
||
rtn->rtn_RTIso->urti_DriverPrivate1 = rtn; // backlink
|
||
|
||
Disable();
|
||
switch(hc->hc_HCIType) {
|
||
case HCITYPE_XHCI:
|
||
retval = xhciQueueIsochIO(hc, rtn);
|
||
break;
|
||
case HCITYPE_EHCI:
|
||
retval = ehciQueueIsochIO(hc, rtn);
|
||
break;
|
||
case HCITYPE_UHCI:
|
||
retval = uhciQueueIsochIO(hc, rtn);
|
||
break;
|
||
default:
|
||
retval = ohciQueueIsochIO(hc, rtn);
|
||
break;
|
||
};
|
||
|
||
if (retval == RC_OK)
|
||
AddTail((struct List *) &hc->hc_RTIsoHandlers, (struct Node *) &rtn->rtn_Node);
|
||
else
|
||
AddTail((struct List *) &unit->hu_FreeRTIsoNodes, (struct Node *) &rtn->rtn_Node);
|
||
Enable();
|
||
|
||
KPRINTF(10, "UHCMD_ADDISOHANDLER processed ioreq: 0x%08lx\n", ioreq);
|
||
return(retval);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdRemIsoHandler()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdRemIsoHandler(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_REMISOHANDLER routine.
|
||
*
|
||
* Removes a previously added real time ISO handler.
|
||
*
|
||
*/
|
||
|
||
WORD cmdRemIsoHandler(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct PCIController *hc;
|
||
struct RTIsoNode *rtn;
|
||
|
||
KPRINTF(10, "UHCMD_REMISOHANDLER ioreq: 0x%08lx\n", ioreq);
|
||
|
||
hc = unit->hu_DevControllers[ioreq->iouh_DevAddr];
|
||
if(!hc) {
|
||
return(UHIOERR_HOSTERROR);
|
||
}
|
||
|
||
Disable();
|
||
rtn = (struct RTIsoNode *) hc->hc_RTIsoHandlers.mlh_Head;
|
||
while(rtn->rtn_Node.mln_Succ) {
|
||
if(rtn->rtn_RTIso == ioreq->iouh_Data) {
|
||
break;
|
||
}
|
||
rtn = (struct RTIsoNode *) rtn->rtn_Node.mln_Succ;
|
||
}
|
||
if(!rtn->rtn_Node.mln_Succ) {
|
||
Enable();
|
||
KPRINTF(200, "UHCMD_REMISOHANDLER unable to find RTIso handler\n", ioreq);
|
||
return(UHIOERR_BADPARAMS);
|
||
}
|
||
Remove((struct Node *) rtn);
|
||
|
||
rtn->rtn_RTIso->urti_DriverPrivate1 = NULL;
|
||
rtn->rtn_RTIso = NULL;
|
||
|
||
switch(hc->hc_HCIType) {
|
||
case HCITYPE_XHCI:
|
||
xhciFreeIsochIO(hc, rtn);
|
||
break;
|
||
case HCITYPE_EHCI:
|
||
ehciFreeIsochIO(hc, rtn);
|
||
break;
|
||
case HCITYPE_UHCI:
|
||
uhciFreeIsochIO(hc, rtn);
|
||
break;
|
||
default:
|
||
ohciFreeIsochIO(hc, rtn);
|
||
break;
|
||
};
|
||
|
||
if(rtn->rtn_PTDs) {
|
||
FreeMem(rtn->rtn_PTDs, rtn->rtn_PTDCount * sizeof(struct PTDNode *));
|
||
rtn->rtn_PTDs = NULL;
|
||
rtn->rtn_PTDCount = 0;
|
||
}
|
||
|
||
AddHead((struct List *) &unit->hu_FreeRTIsoNodes, (struct Node *) &rtn->rtn_Node);
|
||
Enable();
|
||
|
||
KPRINTF(10, "UHCMD_REMISOHANDLER processed ioreq: 0x%08lx\n", ioreq);
|
||
return(RC_OK);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdStartRTIso()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdStartRTIso(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_STARTRTISO routine.
|
||
*
|
||
* Enables a previously added realtime iso handler.
|
||
*
|
||
*/
|
||
|
||
WORD cmdStartRTIso(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct PCIController *hc;
|
||
struct RTIsoNode *rtn;
|
||
KPRINTF(10, "UHCMD_STARTRTISO ioreq: 0x%08lx\n", ioreq);
|
||
|
||
hc = unit->hu_DevControllers[ioreq->iouh_DevAddr];
|
||
if(!hc) {
|
||
return(UHIOERR_HOSTERROR);
|
||
}
|
||
|
||
Disable();
|
||
rtn = (struct RTIsoNode *) hc->hc_RTIsoHandlers.mlh_Head;
|
||
while(rtn->rtn_Node.mln_Succ) {
|
||
if(rtn->rtn_RTIso == ioreq->iouh_Data) {
|
||
break;
|
||
}
|
||
rtn = (struct RTIsoNode *) rtn->rtn_Node.mln_Succ;
|
||
}
|
||
if(!rtn->rtn_Node.mln_Succ) {
|
||
Enable();
|
||
return(UHIOERR_BADPARAMS);
|
||
}
|
||
|
||
switch(hc->hc_HCIType) {
|
||
case HCITYPE_XHCI:
|
||
xhciStartIsochIO(hc, rtn);
|
||
break;
|
||
case HCITYPE_EHCI:
|
||
ehciStartIsochIO(hc, rtn);
|
||
break;
|
||
case HCITYPE_UHCI:
|
||
uhciStartIsochIO(hc, rtn);
|
||
break;
|
||
default:
|
||
ohciStartIsochIO(hc, rtn);
|
||
break;
|
||
};
|
||
|
||
Enable();
|
||
|
||
return(RC_OK);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdStopRTIso()" */
|
||
/*
|
||
*======================================================================
|
||
* cmdStopRTIso(ioreq, unit, base)
|
||
*======================================================================
|
||
*
|
||
* This is the device UHCMD_STOPRTISO routine.
|
||
*
|
||
* Disables a previously added realtime iso handler.
|
||
*
|
||
*/
|
||
|
||
WORD cmdStopRTIso(struct IOUsbHWReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct PCIController *hc;
|
||
struct RTIsoNode *rtn;
|
||
|
||
KPRINTF(10, "UHCMD_STOPRTISO ioreq: 0x%08lx\n", ioreq);
|
||
|
||
hc = unit->hu_DevControllers[ioreq->iouh_DevAddr];
|
||
if(!hc) {
|
||
return(UHIOERR_HOSTERROR);
|
||
}
|
||
|
||
Disable();
|
||
rtn = (struct RTIsoNode *) hc->hc_RTIsoHandlers.mlh_Head;
|
||
while(rtn->rtn_Node.mln_Succ) {
|
||
if(rtn->rtn_RTIso == ioreq->iouh_Data) {
|
||
break;
|
||
}
|
||
rtn = (struct RTIsoNode *) rtn->rtn_Node.mln_Succ;
|
||
}
|
||
if(!rtn->rtn_Node.mln_Succ) {
|
||
Enable();
|
||
return(UHIOERR_BADPARAMS);
|
||
}
|
||
|
||
switch(hc->hc_HCIType) {
|
||
case HCITYPE_XHCI:
|
||
xhciStopIsochIO(hc, rtn);
|
||
break;
|
||
case HCITYPE_EHCI:
|
||
ehciStopIsochIO(hc, rtn);
|
||
break;
|
||
case HCITYPE_UHCI:
|
||
uhciStopIsochIO(hc, rtn);
|
||
break;
|
||
default:
|
||
ohciStopIsochIO(hc, rtn);
|
||
break;
|
||
};
|
||
|
||
Enable();
|
||
|
||
return(RC_OK);
|
||
}
|
||
/* \\\ */
|
||
#endif /* PCIUSB_WIP_ISO */
|
||
|
||
/* /// "NSD stuff" */
|
||
|
||
static
|
||
const UWORD NSDSupported[] = {
|
||
CMD_FLUSH, CMD_RESET,
|
||
UHCMD_QUERYDEVICE, UHCMD_USBRESET,
|
||
UHCMD_USBRESUME, UHCMD_USBSUSPEND,
|
||
UHCMD_USBOPER, UHCMD_CONTROLXFER,
|
||
UHCMD_ISOXFER, UHCMD_INTXFER,
|
||
UHCMD_BULKXFER,
|
||
#if defined(PCIUSB_WIP_ISO)
|
||
UHCMD_ADDISOHANDLER, UHCMD_REMISOHANDLER,
|
||
UHCMD_STARTRTISO, UHCMD_STOPRTISO,
|
||
#endif
|
||
NSCMD_DEVICEQUERY, 0
|
||
};
|
||
|
||
WORD cmdNSDeviceQuery(struct IOStdReq *ioreq,
|
||
struct PCIUnit *unit,
|
||
struct PCIDevice *base)
|
||
{
|
||
struct NSDeviceQueryResult *query;
|
||
|
||
query = (struct NSDeviceQueryResult *) ioreq->io_Data;
|
||
|
||
KPRINTF(10, "NSCMD_DEVICEQUERY ioreq: 0x%p query: 0x%p\n", ioreq, query);
|
||
|
||
/* NULL ptr?
|
||
Enough data?
|
||
Valid request?
|
||
*/
|
||
if((!query) ||
|
||
(ioreq->io_Length < sizeof(struct NSDeviceQueryResult)) ||
|
||
(query->DevQueryFormat != 0) ||
|
||
(query->SizeAvailable != 0)) {
|
||
/* Return error. This is special handling, since iorequest is only
|
||
guaranteed to be sizeof(struct IOStdReq). If we'd let our
|
||
devBeginIO dispatcher return the error, it would trash some
|
||
memory past end of the iorequest (ios2_WireError field).
|
||
*/
|
||
ioreq->io_Error = IOERR_NOCMD;
|
||
TermIO((struct IOUsbHWReq *) ioreq, base);
|
||
|
||
/* Don't reply, we already did.
|
||
*/
|
||
return RC_DONTREPLY;
|
||
}
|
||
|
||
ioreq->io_Actual = query->SizeAvailable
|
||
= sizeof(struct NSDeviceQueryResult);
|
||
query->DeviceType = NSDEVTYPE_USBHARDWARE;
|
||
query->DeviceSubType = 0;
|
||
query->SupportedCommands = (UWORD *)NSDSupported;
|
||
|
||
/* Return success (note that this will NOT poke ios2_WireError).
|
||
*/
|
||
return RC_OK;
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "TermIO()" */
|
||
/*
|
||
*===========================================================
|
||
* TermIO(ioreq, base)
|
||
*===========================================================
|
||
*
|
||
* Return completed ioreq to sender.
|
||
*
|
||
*/
|
||
|
||
void TermIO(struct IOUsbHWReq *ioreq,
|
||
struct PCIDevice *base)
|
||
{
|
||
ioreq->iouh_Req.io_Message.mn_Node.ln_Type = NT_FREEMSG;
|
||
|
||
/* If not quick I/O, reply the message
|
||
*/
|
||
if(!(ioreq->iouh_Req.io_Flags & IOF_QUICK)) {
|
||
ReplyMsg(&ioreq->iouh_Req.io_Message);
|
||
}
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "cmdAbortIO()" */
|
||
BOOL cmdAbortIO(struct IOUsbHWReq *ioreq, struct PCIDevice *base)
|
||
{
|
||
struct PCIUnit *unit = (struct PCIUnit *) ioreq->iouh_Req.io_Unit;
|
||
struct IOUsbHWReq *cmpioreq;
|
||
struct PCIController *hc;
|
||
UWORD devadrep;
|
||
BOOL foundit = FALSE;
|
||
|
||
KPRINTF(10, "cmdAbort(%p)\n", ioreq);
|
||
|
||
Disable();
|
||
cmpioreq = (struct IOUsbHWReq *) unit->hu_RHIOQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
if(ioreq == cmpioreq) {
|
||
Remove(&ioreq->iouh_Req.io_Message.mn_Node);
|
||
Enable();
|
||
return TRUE;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) cmpioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
}
|
||
|
||
hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
while(hc->hc_Node.ln_Succ) {
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_CtrlXFerQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
if(ioreq == cmpioreq) {
|
||
foundit = TRUE;
|
||
break;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) cmpioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
}
|
||
if(!foundit) {
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_IntXFerQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
if(ioreq == cmpioreq) {
|
||
foundit = TRUE;
|
||
break;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) cmpioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
}
|
||
}
|
||
if(!foundit) {
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_IsoXFerQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
if(ioreq == cmpioreq) {
|
||
foundit = TRUE;
|
||
break;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) cmpioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
}
|
||
}
|
||
if(!foundit) {
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_BulkXFerQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
if(ioreq == cmpioreq) {
|
||
foundit = TRUE;
|
||
break;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) cmpioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
}
|
||
}
|
||
if(!foundit) {
|
||
// IOReq is probably pending in some transfer structure
|
||
devadrep = (ioreq->iouh_DevAddr<<5) + ioreq->iouh_Endpoint + ((ioreq->iouh_Dir == UHDIR_IN) ? 0x10 : 0);
|
||
switch(hc->hc_HCIType) {
|
||
case HCITYPE_UHCI:
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_TDQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
if(ioreq == cmpioreq) {
|
||
foundit = TRUE;
|
||
unit->hu_DevBusyReq[devadrep] = NULL;
|
||
uhciFreeQContext(hc, (struct UhciQH *) ioreq->iouh_DriverPrivate1);
|
||
break;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) cmpioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
}
|
||
break;
|
||
|
||
case HCITYPE_OHCI:
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_TDQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
if(ioreq == cmpioreq) {
|
||
/*
|
||
* Request's ED is in use by the HC, as well as its TDs and
|
||
* data buffers.
|
||
* Schedule abort on the HC driver and reply the request
|
||
* only when done. However return success.
|
||
*/
|
||
ioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
ohciAbortRequest(hc, ioreq);
|
||
Enable();
|
||
return TRUE;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) cmpioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
}
|
||
break;
|
||
|
||
case HCITYPE_EHCI:
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_TDQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
if(ioreq == cmpioreq) {
|
||
/*
|
||
* CHECKME: Perhaps immediate freeing can cause issues similar to OHCI.
|
||
* Should synchronized abort routine be implemented here too ?
|
||
*/
|
||
ehciFreeAsyncContext(hc, ioreq);
|
||
Enable();
|
||
ioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
TermIO(ioreq, base);
|
||
return TRUE;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) cmpioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_PeriodicTDQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
if(ioreq == cmpioreq) {
|
||
ehciFreePeriodicContext(hc, ioreq);
|
||
Enable();
|
||
ioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
TermIO(ioreq, base);
|
||
return TRUE;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) cmpioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
}
|
||
break;
|
||
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
case HCITYPE_XHCI:
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_TDQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
if(ioreq == cmpioreq) {
|
||
/*
|
||
* CHECKME: Perhaps immediate freeing can cause issues similar to OHCI.
|
||
* Should synchronized abort routine be implemented here too ?
|
||
*/
|
||
xhciFreeAsyncContext(hc, unit, ioreq);
|
||
Enable();
|
||
ioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
TermIO(ioreq, base);
|
||
return TRUE;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) cmpioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) hc->hc_PeriodicTDQueue.lh_Head;
|
||
while(((struct Node *) cmpioreq)->ln_Succ) {
|
||
if(ioreq == cmpioreq) {
|
||
xhciFreePeriodicContext(hc, unit, ioreq);
|
||
Enable();
|
||
ioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
TermIO(ioreq, base);
|
||
return TRUE;
|
||
}
|
||
cmpioreq = (struct IOUsbHWReq *) cmpioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
}
|
||
break;
|
||
#endif
|
||
}
|
||
}
|
||
if(foundit) {
|
||
Remove(&ioreq->iouh_Req.io_Message.mn_Node);
|
||
break;
|
||
}
|
||
hc = (struct PCIController *) hc->hc_Node.ln_Succ;
|
||
}
|
||
Enable();
|
||
|
||
if (foundit) {
|
||
ioreq->iouh_Req.io_Error = IOERR_ABORTED;
|
||
TermIO(ioreq, base);
|
||
} else {
|
||
KPRINTF(20, "WARNING, could not abort unknown IOReq %p\n", ioreq);
|
||
}
|
||
return(foundit);
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "uhwCheckRootHubChanges()" */
|
||
void uhwCheckRootHubChanges(struct PCIUnit *unit)
|
||
{
|
||
struct PCIDevice *base = unit->hu_Device;
|
||
struct IOUsbHWReq *ioreq;
|
||
|
||
if(unit->hu_RootPortChanges && unit->hu_RHIOQueue.lh_Head->ln_Succ) {
|
||
KPRINTF(1, "Portchange map %04lx\n", unit->hu_RootPortChanges);
|
||
Disable();
|
||
ioreq = (struct IOUsbHWReq *) unit->hu_RHIOQueue.lh_Head;
|
||
while(((struct Node *) ioreq)->ln_Succ) {
|
||
Remove(&ioreq->iouh_Req.io_Message.mn_Node);
|
||
if((unit->hu_RootHubPorts < 8) || (ioreq->iouh_Length == 1)) {
|
||
*((UBYTE *) ioreq->iouh_Data) = unit->hu_RootPortChanges;
|
||
ioreq->iouh_Actual = 1;
|
||
} else {
|
||
((UBYTE *) ioreq->iouh_Data)[0] = unit->hu_RootPortChanges;
|
||
((UBYTE *) ioreq->iouh_Data)[1] = unit->hu_RootPortChanges>>8;
|
||
ioreq->iouh_Actual = 2;
|
||
}
|
||
ReplyMsg(&ioreq->iouh_Req.io_Message);
|
||
ioreq = (struct IOUsbHWReq *) unit->hu_RHIOQueue.lh_Head;
|
||
}
|
||
unit->hu_RootPortChanges = 0;
|
||
Enable();
|
||
}
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "uhwCheckSpecialCtrlTransfers()" */
|
||
void uhwCheckSpecialCtrlTransfers(struct PCIController *hc, struct IOUsbHWReq *ioreq)
|
||
{
|
||
struct PCIUnit *unit = hc->hc_Unit;
|
||
struct PCIDevice *base = unit->hu_Device;
|
||
|
||
/* Clear Feature(Endpoint halt) */
|
||
if((ioreq->iouh_SetupData.bmRequestType == (URTF_STANDARD|URTF_ENDPOINT)) &&
|
||
(ioreq->iouh_SetupData.bRequest == USR_CLEAR_FEATURE) &&
|
||
(ioreq->iouh_SetupData.wValue == AROS_WORD2LE(UFS_ENDPOINT_HALT))) {
|
||
KPRINTF(10, "Resetting toggle bit for endpoint %ld\n", AROS_WORD2LE(ioreq->iouh_SetupData.wIndex) & 0xf);
|
||
unit->hu_DevDataToggle[(ioreq->iouh_DevAddr<<5)|(AROS_WORD2LE(ioreq->iouh_SetupData.wIndex) & 0xf)|((AROS_WORD2LE(ioreq->iouh_SetupData.wIndex) & 0x80)>>3)] = 0;
|
||
} else if((ioreq->iouh_SetupData.bmRequestType == (URTF_STANDARD|URTF_DEVICE)) &&
|
||
(ioreq->iouh_SetupData.bRequest == USR_SET_ADDRESS)) {
|
||
/* Set Address -> clear all endpoints */
|
||
ULONG epnum;
|
||
ULONG adr = AROS_WORD2BE(ioreq->iouh_SetupData.wValue)>>3;
|
||
KPRINTF(10, "Resetting toggle bits for device address %ld\n", adr>>5);
|
||
for(epnum = 0; epnum < 31; epnum++) {
|
||
unit->hu_DevDataToggle[adr+epnum] = 0;
|
||
}
|
||
// transfer host controller ownership
|
||
unit->hu_DevControllers[ioreq->iouh_DevAddr] = NULL;
|
||
unit->hu_DevControllers[adr>>5] = hc;
|
||
} else if((ioreq->iouh_SetupData.bmRequestType == (URTF_CLASS|URTF_OTHER)) &&
|
||
(ioreq->iouh_SetupData.bRequest == USR_SET_FEATURE) &&
|
||
(ioreq->iouh_SetupData.wValue == AROS_WORD2LE(UFS_PORT_RESET))) {
|
||
// a hub will be enumerating a device on this host controller soon!
|
||
KPRINTF(10, "Hub RESET caught, assigning Dev0 to %p!\n", hc);
|
||
unit->hu_DevControllers[0] = hc;
|
||
}
|
||
}
|
||
/* \\\ */
|
||
|
||
/* /// "uhwNakTimeoutInt()" */
|
||
AROS_INTH1(uhwNakTimeoutInt, struct PCIUnit *, unit)
|
||
{
|
||
AROS_INTFUNC_INIT
|
||
|
||
struct PCIDevice *base = unit->hu_Device;
|
||
struct PCIController *hc;
|
||
struct IOUsbHWReq *ioreq;
|
||
struct UhciQH *uqh;
|
||
struct UhciTD *utd;
|
||
struct EhciQH *eqh;
|
||
UWORD devadrep;
|
||
UWORD cnt;
|
||
ULONG linkelem;
|
||
ULONG ctrlstatus;
|
||
BOOL causeint;
|
||
|
||
// KPRINTF(1, "Enter NakTimeoutInt(0x%p)\n", unit);
|
||
|
||
// check for port status change for UHCI and frame rollovers and NAK Timeouts
|
||
hc = (struct PCIController *) unit->hu_Controllers.lh_Head;
|
||
while(hc->hc_Node.ln_Succ) {
|
||
if (!(hc->hc_Flags & HCF_ONLINE)) {
|
||
hc = (struct PCIController *) hc->hc_Node.ln_Succ;
|
||
continue;
|
||
}
|
||
ULONG framecnt;
|
||
causeint = FALSE;
|
||
switch(hc->hc_HCIType) {
|
||
case HCITYPE_UHCI: {
|
||
uhciUpdateFrameCounter(hc);
|
||
framecnt = hc->hc_FrameCounter;
|
||
|
||
// NakTimeout
|
||
ioreq = (struct IOUsbHWReq *) hc->hc_TDQueue.lh_Head;
|
||
while(((struct Node *) ioreq)->ln_Succ) {
|
||
if(ioreq->iouh_Flags & UHFF_NAKTIMEOUT) {
|
||
uqh = (struct UhciQH *) ioreq->iouh_DriverPrivate1;
|
||
if(uqh) {
|
||
KPRINTF(1, "UHCI: Examining IOReq=%p with UQH=%p\n", ioreq, uqh);
|
||
devadrep = (ioreq->iouh_DevAddr<<5) + ioreq->iouh_Endpoint + ((ioreq->iouh_Dir == UHDIR_IN) ? 0x10 : 0);
|
||
linkelem = READMEM32_LE(&uqh->uqh_Element);
|
||
if(linkelem & UHCI_TERMINATE) {
|
||
KPRINTF(1, "UHCI: UQH terminated %08lx\n", linkelem);
|
||
if(framecnt > unit->hu_NakTimeoutFrame[devadrep]) {
|
||
// give the thing the chance to exit gracefully
|
||
KPRINTF(20, "UHCI: Terminated? NAK timeout %ld > %ld, IOReq=%p\n", framecnt, unit->hu_NakTimeoutFrame[devadrep], ioreq);
|
||
causeint = TRUE;
|
||
}
|
||
} else {
|
||
utd = (struct UhciTD *) (((IPTR)linkelem & UHCI_PTRMASK) - hc->hc_PCIVirtualAdjust - 16); // struct UhciTD starts 16 before physical TD
|
||
ctrlstatus = READMEM32_LE(&utd->utd_CtrlStatus);
|
||
if(ctrlstatus & UTCF_ACTIVE) {
|
||
if(framecnt > unit->hu_NakTimeoutFrame[devadrep]) {
|
||
// give the thing the chance to exit gracefully
|
||
KPRINTF(20, "UHCI: NAK timeout %ld > %ld, IOReq=%p\n", framecnt, unit->hu_NakTimeoutFrame[devadrep], ioreq);
|
||
ctrlstatus &= ~UTCF_ACTIVE;
|
||
WRITEMEM32_LE(&utd->utd_CtrlStatus, ctrlstatus);
|
||
causeint = TRUE;
|
||
}
|
||
} else {
|
||
if(framecnt > unit->hu_NakTimeoutFrame[devadrep]) {
|
||
// give the thing the chance to exit gracefully
|
||
KPRINTF(20, "UHCI: Terminated? NAK timeout %ld > %ld, IOReq=%p\n", framecnt, unit->hu_NakTimeoutFrame[devadrep], ioreq);
|
||
causeint = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
ioreq = (struct IOUsbHWReq *) ((struct Node *) ioreq)->ln_Succ;
|
||
}
|
||
|
||
uhciCheckPortStatusChange(hc);
|
||
break;
|
||
}
|
||
|
||
case HCITYPE_OHCI: {
|
||
struct OhciHCPrivate *ohcihcp = (struct OhciHCPrivate *)hc->hc_CPrivate;
|
||
ohciUpdateFrameCounter(hc);
|
||
framecnt = hc->hc_FrameCounter;
|
||
// NakTimeout
|
||
ioreq = (struct IOUsbHWReq *) hc->hc_TDQueue.lh_Head;
|
||
while(((struct Node *) ioreq)->ln_Succ) {
|
||
// Remember the successor because ohciAbortRequest() will move the request to another list
|
||
struct IOUsbHWReq *succ = (struct IOUsbHWReq *)ioreq->iouh_Req.io_Message.mn_Node.ln_Succ;
|
||
|
||
if(ioreq->iouh_Flags & UHFF_NAKTIMEOUT) {
|
||
KPRINTF(1, "OHCI: Examining IOReq=%p with OED=%p\n", ioreq, ioreq->iouh_DriverPrivate1);
|
||
if (ioreq->iouh_DriverPrivate1) {
|
||
KPRINTF(1, "OHCI: CTRL=%04lx, CMD=%01lx, F=%ld, hccaDH=%08lx, hcDH=%08lx, CH=%08lx, CCH=%08lx, IntEn=%08lx\n",
|
||
READREG32_LE(hc->hc_RegBase, OHCI_CONTROL),
|
||
READREG32_LE(hc->hc_RegBase, OHCI_CMDSTATUS),
|
||
READREG32_LE(hc->hc_RegBase, OHCI_FRAMECOUNT),
|
||
READMEM32_LE(&ohcihcp->ohc_OhciHCCA->oha_DoneHead),
|
||
READREG32_LE(hc->hc_RegBase, OHCI_DONEHEAD),
|
||
READREG32_LE(hc->hc_RegBase, OHCI_CTRL_HEAD_ED),
|
||
READREG32_LE(hc->hc_RegBase, OHCI_CTRL_ED),
|
||
READREG32_LE(hc->hc_RegBase, OHCI_INTEN));
|
||
|
||
devadrep = (ioreq->iouh_DevAddr<<5) + ioreq->iouh_Endpoint + ((ioreq->iouh_Dir == UHDIR_IN) ? 0x10 : 0);
|
||
if(framecnt > unit->hu_NakTimeoutFrame[devadrep]) {
|
||
// give the thing the chance to exit gracefully
|
||
KPRINTF(200, "OHCI: HC 0x%p NAK timeout %ld > %ld, IOReq=%p\n", hc, framecnt, unit->hu_NakTimeoutFrame[devadrep], ioreq);
|
||
ioreq->iouh_Req.io_Error = UHIOERR_NAKTIMEOUT;
|
||
ohciAbortRequest(hc, ioreq);
|
||
}
|
||
}
|
||
}
|
||
ioreq = succ;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case HCITYPE_EHCI: {
|
||
ehciUpdateFrameCounter(hc);
|
||
framecnt = hc->hc_FrameCounter;
|
||
// NakTimeout
|
||
for(cnt = 0; cnt < 2; cnt++) {
|
||
ioreq = (struct IOUsbHWReq *) (cnt ? hc->hc_PeriodicTDQueue.lh_Head : hc->hc_TDQueue.lh_Head);
|
||
while(((struct Node *) ioreq)->ln_Succ) {
|
||
if(ioreq->iouh_Flags & UHFF_NAKTIMEOUT) {
|
||
eqh = (struct EhciQH *) ioreq->iouh_DriverPrivate1;
|
||
if(eqh) {
|
||
KPRINTF(1, "EHCI: Examining IOReq=%p with EQH=%p\n", ioreq, eqh);
|
||
devadrep = (ioreq->iouh_DevAddr<<5) + ioreq->iouh_Endpoint + ((ioreq->iouh_Dir == UHDIR_IN) ? 0x10 : 0);
|
||
ctrlstatus = READMEM32_LE(&eqh->eqh_CtrlStatus);
|
||
if(ctrlstatus & ETCF_ACTIVE) {
|
||
if(framecnt > unit->hu_NakTimeoutFrame[devadrep]) {
|
||
// give the thing the chance to exit gracefully
|
||
KPRINTF(20, "EHCI: NAK timeout %ld > %ld, IOReq=%p\n", framecnt, unit->hu_NakTimeoutFrame[devadrep], ioreq);
|
||
ctrlstatus &= ~ETCF_ACTIVE;
|
||
ctrlstatus |= ETSF_HALTED;
|
||
WRITEMEM32_LE(&eqh->eqh_CtrlStatus, ctrlstatus);
|
||
causeint = TRUE;
|
||
}
|
||
} else {
|
||
if(ctrlstatus & ETCF_READYINTEN) {
|
||
KPRINTF(10, "EHCI: INT missed?!? Manually causing it! %08lx, IOReq=%p\n",
|
||
ctrlstatus, ioreq);
|
||
causeint = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
ioreq = (struct IOUsbHWReq *) ((struct Node *) ioreq)->ln_Succ;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
#if defined(PCIUSB_ENABLEXHCI)
|
||
case HCITYPE_XHCI: {
|
||
struct List *TOList;
|
||
xhciUpdateFrameCounter(hc);
|
||
framecnt = hc->hc_FrameCounter;
|
||
|
||
for(cnt = 0; cnt < 3; cnt++) {
|
||
switch(cnt) {
|
||
case 2:
|
||
TOList = &hc->hc_CtrlXFerQueue;
|
||
break;
|
||
case 1:
|
||
TOList = &hc->hc_TDQueue;
|
||
break;
|
||
default:
|
||
TOList = &hc->hc_PeriodicTDQueue;
|
||
break;
|
||
}
|
||
// Timeout active transfers
|
||
ForeachNode(TOList, ioreq) {
|
||
//KPRINTF(1, "XHCI: Examining IOReq=%p with (PrivateData @ %p)\n", ioreq, ioreq->iouh_DriverPrivate1);
|
||
if (cnt < 1) {
|
||
if(ioreq->iouh_Flags & UHFF_NAKTIMEOUT) {
|
||
if (ioreq->iouh_DriverPrivate1) {
|
||
devadrep = (ioreq->iouh_DevAddr<<5) + ioreq->iouh_Endpoint + ((ioreq->iouh_Dir == UHDIR_IN) ? 0x10 : 0);
|
||
if(framecnt > unit->hu_NakTimeoutFrame[devadrep]) {
|
||
// give the thing the chance to exit gracefully
|
||
KPRINTF(200, "XHCI: HC 0x%p NAK timeout %ld, IOReq=%p\n", hc, unit->hu_NakTimeoutFrame[devadrep], ioreq);
|
||
causeint = TRUE;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
// Timeout failed pending transfers
|
||
devadrep = (ioreq->iouh_DevAddr<<5) + ioreq->iouh_Endpoint + ((ioreq->iouh_Dir == UHDIR_IN) ? 0x10 : 0);
|
||
if ((unit->hu_NakTimeoutFrame[devadrep]) && (framecnt > unit->hu_NakTimeoutFrame[devadrep])) {
|
||
KPRINTF(200, "XHCI: HC 0x%p NAK timeout %ld, IOReq=%p\n", hc, unit->hu_NakTimeoutFrame[devadrep], ioreq);
|
||
ioreq->iouh_Req.io_Error = UHIOERR_NAKTIMEOUT;
|
||
xhciAbortRequest(hc, ioreq);
|
||
} else if (unit->hu_NakTimeoutFrame[devadrep])
|
||
causeint = TRUE;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
#endif
|
||
}
|
||
if(causeint) {
|
||
SureCause(base, &hc->hc_CompleteInt);
|
||
}
|
||
|
||
hc = (struct PCIController *) hc->hc_Node.ln_Succ;
|
||
}
|
||
|
||
uhwCheckRootHubChanges(unit);
|
||
|
||
unit->hu_NakTimeoutReq.tr_time.tv_micro = 150*1000;
|
||
SendIO((APTR) &unit->hu_NakTimeoutReq);
|
||
|
||
// KPRINTF(1, "Exit NakTimeoutInt(0x%p)\n", unit);
|
||
|
||
return FALSE;
|
||
|
||
AROS_INTFUNC_EXIT
|
||
}
|
||
/* \\\ */
|
||
|