mirror of https://github.com/deadw00d/AROS.git
2338 lines
102 KiB
C
2338 lines
102 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 "uhwcmd.h"
|
|
#include "ohciproto.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
|
|
|
|
/* Root hub data */
|
|
const struct UsbStdDevDesc RHDevDesc = { sizeof(struct UsbStdDevDesc), UDT_DEVICE, WORD2LE(0x0110), HUB_CLASSCODE, 0, 0, 8, WORD2LE(0x0000), WORD2LE(0x0000), WORD2LE(0x0100), 1, 2, 0, 1 };
|
|
|
|
const struct UsbStdCfgDesc RHCfgDesc = { 9, UDT_CONFIGURATION, WORD2LE(9+9+7), 1, 1, 3, USCAF_ONE|USCAF_SELF_POWERED, 0 };
|
|
const struct UsbStdIfDesc RHIfDesc = { 9, UDT_INTERFACE, 0, 0, 1, HUB_CLASSCODE, 0, 0, 4 };
|
|
const struct UsbStdEPDesc RHEPDesc = { 7, UDT_ENDPOINT, URTF_IN|1, USEAF_INTERRUPT, WORD2LE(8), 255 };
|
|
const struct UsbHubDesc RHHubDesc = { 9, // 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
|
|
WORD2LE(UHCF_INDIVID_POWER|UHCF_INDIVID_OVP), // 3 wHubCharacteristics
|
|
0, // 5 bPwrOn2PwrGood
|
|
1, // 6 bHubContrCurrent
|
|
1, // 7 DeviceRemovable (size is variable)
|
|
0 // x PortPwrCtrlMask (size is variable)
|
|
};
|
|
|
|
const CONST_STRPTR RHStrings[] = { "Chris Hodges", "PCI Root Hub Unit x", "Standard Config", "Hub interface" };
|
|
|
|
/* /// "SureCause()" */
|
|
void SureCause(struct PCIDevice *base, struct Interrupt *interrupt)
|
|
{
|
|
/* 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();
|
|
}
|
|
/* \\\ */
|
|
|
|
/* /// "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 == 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_UnitAllocated)
|
|
{
|
|
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_UnitAllocated = TRUE;
|
|
unit->hu_NakTimeoutInt.is_Node.ln_Type = NT_INTERRUPT;
|
|
unit->hu_NakTimeoutInt.is_Node.ln_Name = "PCI 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;
|
|
unit->hu_NakTimeoutInt.is_Node.ln_Type = NT_SOFTINT;
|
|
AbortIO((APTR) &unit->hu_NakTimeoutReq);
|
|
|
|
pciFreeUnit(unit);
|
|
|
|
uhwCloseTimer(unit, base);
|
|
unit->hu_UnitAllocated = FALSE;
|
|
}
|
|
/* \\\ */
|
|
|
|
/* /// "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)))
|
|
{
|
|
*((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)))
|
|
{
|
|
*((STRPTR *) tag->ti_Data) ="©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 *) tag->ti_Data) = UHCF_USB20;
|
|
count++;
|
|
}
|
|
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;
|
|
BOOL cmdgood;
|
|
ULONG cnt;
|
|
|
|
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))
|
|
{
|
|
if(unit->hu_RootHub20Ports)
|
|
{
|
|
struct UsbStdDevDesc *usdd = (struct UsbStdDevDesc *) ioreq->iouh_Data;
|
|
usdd->bcdUSB = AROS_WORD2LE(0x0200); // signal a highspeed root hub
|
|
usdd->bDeviceProtocol = 1; // single TT
|
|
}
|
|
|
|
}
|
|
return(0);
|
|
|
|
case UDT_CONFIGURATION:
|
|
{
|
|
UBYTE tmpbuf[9+9+7];
|
|
KPRINTF(1, ("RH: GetConfigDescriptor (%ld)\n", len));
|
|
CopyMem((APTR) &RHCfgDesc, tmpbuf, 9);
|
|
CopyMem((APTR) &RHIfDesc, &tmpbuf[9], 9);
|
|
CopyMem((APTR) &RHEPDesc, &tmpbuf[9+9], 7);
|
|
if(unit->hu_RootHub20Ports)
|
|
{
|
|
struct UsbStdEPDesc *usepd = (struct UsbStdEPDesc *) &tmpbuf[9+9];
|
|
usepd->bInterval = 12; // 2048 µFrames
|
|
}
|
|
ioreq->iouh_Actual = (len > 9+9+7) ? 9+9+7 : len;
|
|
CopyMem(tmpbuf, ioreq->iouh_Data, ioreq->iouh_Actual);
|
|
return(0);
|
|
}
|
|
|
|
case UDT_STRING:
|
|
if(val & 0xff) /* get lang array */
|
|
{
|
|
CONST_STRPTR source = NULL;
|
|
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);
|
|
}
|
|
source = RHStrings[(val & 0xff)-1];
|
|
if(len > 1)
|
|
{
|
|
ioreq->iouh_Actual = 2;
|
|
while(*source++)
|
|
{
|
|
slen++;
|
|
}
|
|
source = RHStrings[(val & 0xff)-1];
|
|
*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);
|
|
} 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:
|
|
if((!idx) || (idx > numports))
|
|
{
|
|
KPRINTF(20, ("Port %ld out of range\n", idx));
|
|
return(UHIOERR_STALL);
|
|
}
|
|
chc = unit->hu_PortMap11[idx - 1];
|
|
if(unit->hu_EhciOwned[idx - 1])
|
|
{
|
|
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, unit->hu_EhciOwned[idx - 1] ? "EHCI" : "U/OHCI"));
|
|
cmdgood = FALSE;
|
|
switch(hc->hc_HCIType)
|
|
{
|
|
case HCITYPE_UHCI:
|
|
{
|
|
UWORD portreg = hciport ? UHCI_PORT2STSCTRL : UHCI_PORT1STSCTRL;
|
|
ULONG oldval = READIO16_LE(hc->hc_RegBase, portreg) & ~(UHPF_ENABLECHANGE|UHPF_CONNECTCHANGE); // these are clear-on-write!
|
|
ULONG newval = oldval;
|
|
switch(val)
|
|
{
|
|
/* case UFS_PORT_CONNECTION: not possible */
|
|
case UFS_PORT_ENABLE:
|
|
KPRINTF(10, ("UHCI: Enabling Port (%s)\n", newval & UHPF_PORTENABLE ? "already" : "ok"));
|
|
newval |= UHPF_PORTENABLE;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_PORT_SUSPEND:
|
|
newval |= UHPF_PORTSUSPEND;
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_SUSPEND; // manually fake suspend change
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
/* case UFS_PORT_OVER_CURRENT: not possible */
|
|
case UFS_PORT_RESET:
|
|
KPRINTF(10, ("UHCI: Resetting Port (%s)\n", newval & UHPF_PORTRESET ? "already" : "ok"));
|
|
|
|
// this is an ugly blocking workaround to the inability of UHCI to clear reset automatically
|
|
newval &= ~(UHPF_PORTSUSPEND|UHPF_PORTENABLE);
|
|
newval |= UHPF_PORTRESET;
|
|
WRITEIO16_LE(hc->hc_RegBase, portreg, newval);
|
|
uhwDelayMS(25, unit);
|
|
newval = READIO16_LE(hc->hc_RegBase, portreg) & ~(UHPF_ENABLECHANGE|UHPF_CONNECTCHANGE|UHPF_PORTSUSPEND|UHPF_PORTENABLE);
|
|
KPRINTF(10, ("UHCI: Reset=%s\n", newval & UHPF_PORTRESET ? "GOOD" : "BAD!"));
|
|
// like windows does it
|
|
newval &= ~UHPF_PORTRESET;
|
|
WRITEIO16_LE(hc->hc_RegBase, portreg, newval);
|
|
uhwDelayMicro(50, unit);
|
|
newval = READIO16_LE(hc->hc_RegBase, portreg) & ~(UHPF_ENABLECHANGE|UHPF_CONNECTCHANGE|UHPF_PORTSUSPEND);
|
|
KPRINTF(10, ("UHCI: Reset=%s\n", newval & UHPF_PORTRESET ? "BAD!" : "GOOD"));
|
|
newval &= ~(UHPF_PORTSUSPEND|UHPF_PORTRESET);
|
|
newval |= UHPF_PORTENABLE;
|
|
WRITEIO16_LE(hc->hc_RegBase, portreg, newval);
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_RESET|UPSF_PORT_ENABLE; // manually fake reset change
|
|
|
|
cnt = 100;
|
|
do
|
|
{
|
|
uhwDelayMS(1, unit);
|
|
newval = READIO16_LE(hc->hc_RegBase, portreg);
|
|
} while(--cnt && (!(newval & UHPF_PORTENABLE)));
|
|
if(cnt)
|
|
{
|
|
KPRINTF(10, ("UHCI: Enabled after %ld ticks\n", 100-cnt));
|
|
} else {
|
|
KPRINTF(20, ("UHCI: Port refuses to be enabled!\n"));
|
|
return(UHIOERR_HOSTERROR);
|
|
}
|
|
// make enumeration possible
|
|
unit->hu_DevControllers[0] = hc;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_PORT_POWER:
|
|
KPRINTF(10, ("UHCI: Powering Port\n"));
|
|
// ignore for UHCI, is always powered
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
/* case UFS_PORT_LOW_SPEED: not possible */
|
|
/* case UFS_C_PORT_CONNECTION:
|
|
case UFS_C_PORT_ENABLE:
|
|
case UFS_C_PORT_SUSPEND:
|
|
case UFS_C_PORT_OVER_CURRENT:
|
|
case UFS_C_PORT_RESET: */
|
|
}
|
|
if(cmdgood)
|
|
{
|
|
KPRINTF(5, ("UHCI: Port %ld SET_FEATURE %04lx->%04lx\n", idx, oldval, newval));
|
|
WRITEIO16_LE(hc->hc_RegBase, portreg, newval);
|
|
return(0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HCITYPE_OHCI:
|
|
{
|
|
UWORD portreg = OHCI_PORTSTATUS + (hciport<<2);
|
|
ULONG oldval = READREG32_LE(hc->hc_RegBase, portreg);
|
|
|
|
switch(val)
|
|
{
|
|
/* case UFS_PORT_CONNECTION: not possible */
|
|
case UFS_PORT_ENABLE:
|
|
KPRINTF(10, ("OHCI: Enabling Port (%s)\n", oldval & OHPF_PORTENABLE ? "already" : "ok"));
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTENABLE);
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_PORT_SUSPEND:
|
|
KPRINTF(10, ("OHCI: Suspending Port (%s)\n", oldval & OHPF_PORTSUSPEND ? "already" : "ok"));
|
|
//hc->hc_PortChangeMap[hciport] |= UPSF_PORT_SUSPEND; // manually fake suspend change
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTSUSPEND);
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
/* case UFS_PORT_OVER_CURRENT: not possible */
|
|
case UFS_PORT_RESET:
|
|
KPRINTF(10, ("OHCI: Resetting Port (%s)\n", oldval & OHPF_PORTRESET ? "already" : "ok"));
|
|
// make sure we have at least 50ms of reset time here, as required for a root hub port
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTRESET);
|
|
uhwDelayMS(10, unit);
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTRESET);
|
|
uhwDelayMS(10, unit);
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTRESET);
|
|
uhwDelayMS(10, unit);
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTRESET);
|
|
uhwDelayMS(10, unit);
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTRESET);
|
|
uhwDelayMS(15, unit);
|
|
oldval = READREG32_LE(hc->hc_RegBase, portreg);
|
|
KPRINTF(10, ("OHCI: Reset release (%s %s)\n", oldval & OHPF_PORTRESET ? "didn't turn off" : "okay",
|
|
oldval & OHPF_PORTENABLE ? "enabled" : "not enabled"));
|
|
if(oldval & OHPF_PORTRESET)
|
|
{
|
|
uhwDelayMS(40, unit);
|
|
oldval = READREG32_LE(hc->hc_RegBase, portreg);
|
|
KPRINTF(10, ("OHCI: Reset 2nd release (%s %s)\n", oldval & OHPF_PORTRESET ? "didn't turn off" : "okay",
|
|
oldval & OHPF_PORTENABLE ? "enabled" : "still not enabled"));
|
|
}
|
|
// make enumeration possible
|
|
unit->hu_DevControllers[0] = hc;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_PORT_POWER:
|
|
KPRINTF(10, ("OHCI: Powering Port (%s)\n", oldval & OHPF_PORTPOWER ? "already" : "ok"));
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTPOWER);
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
/* case UFS_PORT_LOW_SPEED: not possible */
|
|
/* case UFS_C_PORT_CONNECTION:
|
|
case UFS_C_PORT_ENABLE:
|
|
case UFS_C_PORT_SUSPEND:
|
|
case UFS_C_PORT_OVER_CURRENT:
|
|
case UFS_C_PORT_RESET: */
|
|
}
|
|
if(cmdgood)
|
|
{
|
|
return(0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HCITYPE_EHCI:
|
|
{
|
|
UWORD portreg = EHCI_PORTSC1 + (hciport<<2);
|
|
ULONG oldval = READREG32_LE(hc->hc_RegBase, portreg) & ~(EHPF_OVERCURRENTCHG|EHPF_ENABLECHANGE|EHPF_CONNECTCHANGE); // these are clear-on-write!
|
|
ULONG newval = oldval;
|
|
switch(val)
|
|
{
|
|
/* case UFS_PORT_CONNECTION: not possible */
|
|
case UFS_PORT_ENABLE:
|
|
KPRINTF(10, ("EHCI: Enabling Port (%s)\n", newval & EHPF_PORTENABLE ? "already" : "ok"));
|
|
newval |= EHPF_PORTENABLE;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_PORT_SUSPEND:
|
|
newval |= EHPF_PORTSUSPEND;
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_SUSPEND; // manually fake suspend change
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
/* case UFS_PORT_OVER_CURRENT: not possible */
|
|
case UFS_PORT_RESET:
|
|
KPRINTF(10, ("EHCI: Resetting Port (%s)\n", newval & EHPF_PORTRESET ? "already" : "ok"));
|
|
|
|
// this is an ugly blocking workaround to the inability of EHCI to clear reset automatically
|
|
newval &= ~(EHPF_PORTSUSPEND|EHPF_PORTENABLE);
|
|
newval |= EHPF_PORTRESET;
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, newval);
|
|
|
|
// Wait for reset to complete (spec is 50ms, FreeBSD source suggests 200ms, but
|
|
// we compromise to help USB volumes become available in time to be chosen as
|
|
// the boot device)
|
|
uhwDelayMS(125, unit);
|
|
|
|
newval = READREG32_LE(hc->hc_RegBase, portreg) & ~(EHPF_OVERCURRENTCHG|EHPF_ENABLECHANGE|EHPF_CONNECTCHANGE|EHPF_PORTSUSPEND|EHPF_PORTENABLE);
|
|
KPRINTF(10, ("EHCI: Reset=%s\n", newval & EHPF_PORTRESET ? "BAD!" : "GOOD"));
|
|
if (newval & EHPF_PORTRESET)
|
|
{
|
|
newval &= ~EHPF_PORTRESET;
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, newval);
|
|
}
|
|
uhwDelayMS(10, unit);
|
|
newval = READREG32_LE(hc->hc_RegBase, portreg) & ~(EHPF_OVERCURRENTCHG|EHPF_ENABLECHANGE|EHPF_CONNECTCHANGE|EHPF_PORTSUSPEND);
|
|
KPRINTF(10, ("EHCI: Reset=%s\n", newval & EHPF_PORTRESET ? "BAD!" : "GOOD"));
|
|
KPRINTF(10, ("EHCI: Highspeed=%s\n", newval & EHPF_PORTENABLE ? "YES!" : "NO"));
|
|
KPRINTF(10, ("EHCI: Port status=%08lx\n", newval));
|
|
if(!(newval & EHPF_PORTENABLE))
|
|
{
|
|
// if not highspeed, release ownership
|
|
KPRINTF(20, ("EHCI: Transferring ownership to UHCI/OHCI port %ld\n", unit->hu_PortNum11[idx - 1]));
|
|
KPRINTF(10, ("EHCI: Device is %s\n", newval & EHPF_LINESTATUS_DM ? "LOWSPEED" : "FULLSPEED"));
|
|
newval |= EHPF_NOTPORTOWNER;
|
|
if(!chc)
|
|
{
|
|
KPRINTF(20, ("EHCI has no companion controller, can't transfer ownership!\n"));
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, newval);
|
|
return(UHIOERR_HOSTERROR);
|
|
}
|
|
switch(chc->hc_HCIType)
|
|
{
|
|
case HCITYPE_UHCI:
|
|
{
|
|
UWORD uhcihciport = unit->hu_PortNum11[idx - 1];
|
|
UWORD uhciportreg = uhcihciport ? UHCI_PORT2STSCTRL : UHCI_PORT1STSCTRL;
|
|
ULONG __unused uhcinewval = READREG16_LE(chc->hc_RegBase, uhciportreg);
|
|
|
|
KPRINTF(10, ("UHCI Port status before handover=%04lx\n", uhcinewval));
|
|
break;
|
|
}
|
|
|
|
case HCITYPE_OHCI:
|
|
{
|
|
UWORD ohcihciport = unit->hu_PortNum11[idx - 1];
|
|
UWORD ohciportreg = OHCI_PORTSTATUS + (ohcihciport<<2);
|
|
ULONG __unused ohcioldval = READREG32_LE(chc->hc_RegBase, ohciportreg);
|
|
|
|
KPRINTF(10, ("OHCI: Port status before handover=%08lx\n", ohcioldval));
|
|
KPRINTF(10, ("OHCI: Powering Port (%s)\n", ohcioldval & OHPF_PORTPOWER ? "already" : "ok"));
|
|
WRITEREG32_LE(chc->hc_RegBase, ohciportreg, OHPF_PORTPOWER);
|
|
uhwDelayMS(10, unit);
|
|
KPRINTF(10, ("OHCI: Port status after handover=%08lx\n", READREG32_LE(chc->hc_RegBase, ohciportreg)));
|
|
break;
|
|
}
|
|
}
|
|
newval = READREG32_LE(hc->hc_RegBase, portreg) & ~(EHPF_OVERCURRENTCHG|EHPF_ENABLECHANGE|EHPF_CONNECTCHANGE|EHPF_PORTSUSPEND);
|
|
KPRINTF(10, ("EHCI: Port status (reread)=%08lx\n", newval));
|
|
newval |= EHPF_NOTPORTOWNER;
|
|
unit->hu_EhciOwned[idx - 1] = FALSE;
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, newval);
|
|
uhwDelayMS(90, unit);
|
|
KPRINTF(10, ("EHCI: Port status (after handover)=%08lx\n", READREG32_LE(hc->hc_RegBase, portreg) & ~(EHPF_OVERCURRENTCHG|EHPF_ENABLECHANGE|EHPF_CONNECTCHANGE|EHPF_PORTSUSPEND)));
|
|
// enable companion controller port
|
|
switch(chc->hc_HCIType)
|
|
{
|
|
case HCITYPE_UHCI:
|
|
{
|
|
UWORD uhcihciport = unit->hu_PortNum11[idx - 1];
|
|
UWORD uhciportreg = uhcihciport ? UHCI_PORT2STSCTRL : UHCI_PORT1STSCTRL;
|
|
ULONG uhcinewval;
|
|
|
|
uhcinewval = READIO16_LE(chc->hc_RegBase, uhciportreg) & ~(UHPF_ENABLECHANGE|UHPF_CONNECTCHANGE|UHPF_PORTSUSPEND);
|
|
KPRINTF(10, ("UHCI: Reset=%s\n", uhcinewval & UHPF_PORTRESET ? "BAD!" : "GOOD"));
|
|
if((uhcinewval & UHPF_PORTRESET))//|| (newval & EHPF_LINESTATUS_DM))
|
|
{
|
|
// this is an ugly blocking workaround to the inability of UHCI to clear reset automatically
|
|
KPRINTF(20, ("UHCI: Uhm, reset was bad!\n"));
|
|
uhcinewval &= ~(UHPF_PORTSUSPEND|UHPF_PORTENABLE);
|
|
uhcinewval |= UHPF_PORTRESET;
|
|
WRITEIO16_LE(chc->hc_RegBase, uhciportreg, uhcinewval);
|
|
uhwDelayMS(50, unit);
|
|
uhcinewval = READIO16_LE(chc->hc_RegBase, uhciportreg) & ~(UHPF_ENABLECHANGE|UHPF_CONNECTCHANGE|UHPF_PORTSUSPEND|UHPF_PORTENABLE);
|
|
KPRINTF(10, ("UHCI: ReReset=%s\n", uhcinewval & UHPF_PORTRESET ? "GOOD" : "BAD!"));
|
|
uhcinewval &= ~UHPF_PORTRESET;
|
|
WRITEIO16_LE(chc->hc_RegBase, uhciportreg, uhcinewval);
|
|
uhwDelayMicro(50, unit);
|
|
uhcinewval = READIO16_LE(chc->hc_RegBase, uhciportreg) & ~(UHPF_ENABLECHANGE|UHPF_CONNECTCHANGE|UHPF_PORTSUSPEND);
|
|
KPRINTF(10, ("UHCI: ReReset=%s\n", uhcinewval & UHPF_PORTRESET ? "STILL BAD!" : "GOOD"));
|
|
}
|
|
uhcinewval &= ~UHPF_PORTRESET;
|
|
uhcinewval |= UHPF_PORTENABLE;
|
|
WRITEIO16_LE(chc->hc_RegBase, uhciportreg, uhcinewval);
|
|
chc->hc_PortChangeMap[uhcihciport] |= UPSF_PORT_RESET|UPSF_PORT_ENABLE; // manually fake reset change
|
|
uhwDelayMS(5, unit);
|
|
cnt = 100;
|
|
do
|
|
{
|
|
uhwDelayMS(1, unit);
|
|
uhcinewval = READIO16_LE(chc->hc_RegBase, uhciportreg);
|
|
} while(--cnt && (!(uhcinewval & UHPF_PORTENABLE)));
|
|
if(cnt)
|
|
{
|
|
KPRINTF(10, ("UHCI: Enabled after %ld ticks\n", 100-cnt));
|
|
} else {
|
|
KPRINTF(20, ("UHCI: Port refuses to be enabled!\n"));
|
|
return(UHIOERR_HOSTERROR);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HCITYPE_OHCI:
|
|
{
|
|
UWORD ohcihciport = unit->hu_PortNum11[idx - 1];
|
|
UWORD ohciportreg = OHCI_PORTSTATUS + (ohcihciport<<2);
|
|
ULONG ohcioldval = READREG32_LE(chc->hc_RegBase, ohciportreg);
|
|
KPRINTF(10, ("OHCI: Resetting Port (%s)\n", ohcioldval & OHPF_PORTRESET ? "already" : "ok"));
|
|
// make sure we have at least 50ms of reset time here, as required for a root hub port
|
|
WRITEREG32_LE(chc->hc_RegBase, ohciportreg, OHPF_PORTRESET);
|
|
uhwDelayMS(10, unit);
|
|
WRITEREG32_LE(chc->hc_RegBase, ohciportreg, OHPF_PORTRESET);
|
|
uhwDelayMS(10, unit);
|
|
WRITEREG32_LE(chc->hc_RegBase, ohciportreg, OHPF_PORTRESET);
|
|
uhwDelayMS(10, unit);
|
|
WRITEREG32_LE(chc->hc_RegBase, ohciportreg, OHPF_PORTRESET);
|
|
uhwDelayMS(10, unit);
|
|
WRITEREG32_LE(chc->hc_RegBase, ohciportreg, OHPF_PORTRESET);
|
|
uhwDelayMS(15, unit);
|
|
ohcioldval = READREG32_LE(chc->hc_RegBase, ohciportreg);
|
|
KPRINTF(10, ("OHCI: Reset release (%s %s)\n", ohcioldval & OHPF_PORTRESET ? "didn't turn off" : "okay",
|
|
ohcioldval & OHPF_PORTENABLE ? "enabled" : "not enabled"));
|
|
if(ohcioldval & OHPF_PORTRESET)
|
|
{
|
|
uhwDelayMS(40, unit);
|
|
ohcioldval = READREG32_LE(chc->hc_RegBase, ohciportreg);
|
|
KPRINTF(10, ("OHCI: Reset 2nd release (%s %s)\n", ohcioldval & OHPF_PORTRESET ? "didn't turn off" : "okay",
|
|
ohcioldval & OHPF_PORTENABLE ? "enabled" : "still not enabled"));
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
// make enumeration possible
|
|
unit->hu_DevControllers[0] = chc;
|
|
return(0);
|
|
} else {
|
|
newval &= ~EHPF_PORTRESET;
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, newval);
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_RESET; // manually fake reset change
|
|
uhwDelayMS(10, unit);
|
|
cnt = 100;
|
|
do
|
|
{
|
|
uhwDelayMS(1, unit);
|
|
newval = READREG32_LE(hc->hc_RegBase, portreg);
|
|
} while(--cnt && (!(newval & EHPF_PORTENABLE)));
|
|
if(cnt)
|
|
{
|
|
KPRINTF(10, ("EHCI: Enabled after %ld ticks\n", 100-cnt));
|
|
} else {
|
|
KPRINTF(20, ("EHCI: Port refuses to be enabled!\n"));
|
|
return(UHIOERR_HOSTERROR);
|
|
}
|
|
// make enumeration possible
|
|
unit->hu_DevControllers[0] = hc;
|
|
}
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_PORT_POWER:
|
|
KPRINTF(10, ("EHCI: Powering Port\n"));
|
|
newval |= EHPF_PORTPOWER;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
/* case UFS_PORT_LOW_SPEED: not possible */
|
|
/* case UFS_C_PORT_CONNECTION:
|
|
case UFS_C_PORT_ENABLE:
|
|
case UFS_C_PORT_SUSPEND:
|
|
case UFS_C_PORT_OVER_CURRENT:
|
|
case UFS_C_PORT_RESET: */
|
|
}
|
|
if(cmdgood)
|
|
{
|
|
KPRINTF(5, ("EHCI: Port %ld SET_FEATURE %04lx->%04lx\n", idx, oldval, newval));
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, newval);
|
|
return(0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case USR_CLEAR_FEATURE:
|
|
if((!idx) || (idx > numports))
|
|
{
|
|
KPRINTF(20, ("Port %ld out of range\n", idx));
|
|
return(UHIOERR_STALL);
|
|
}
|
|
if(unit->hu_EhciOwned[idx - 1])
|
|
{
|
|
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, unit->hu_EhciOwned[idx - 1] ? "EHCI" : "U/OHCI"));
|
|
cmdgood = FALSE;
|
|
switch(hc->hc_HCIType)
|
|
{
|
|
case HCITYPE_UHCI:
|
|
{
|
|
UWORD portreg = hciport ? UHCI_PORT2STSCTRL : UHCI_PORT1STSCTRL;
|
|
ULONG oldval = READIO16_LE(hc->hc_RegBase, portreg) & ~(UHPF_ENABLECHANGE|UHPF_CONNECTCHANGE); // these are clear-on-write!
|
|
ULONG newval = oldval;
|
|
switch(val)
|
|
{
|
|
case UFS_PORT_ENABLE:
|
|
KPRINTF(10, ("Disabling Port (%s)\n", newval & UHPF_PORTENABLE ? "ok" : "already"));
|
|
newval &= ~UHPF_PORTENABLE;
|
|
cmdgood = TRUE;
|
|
// disable enumeration
|
|
unit->hu_DevControllers[0] = NULL;
|
|
break;
|
|
|
|
case UFS_PORT_SUSPEND:
|
|
newval &= ~UHPF_PORTSUSPEND;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_PORT_POWER: // ignore for UHCI, there's no power control here
|
|
KPRINTF(10, ("Disabling Power\n"));
|
|
KPRINTF(10, ("Disabling Port (%s)\n", newval & UHPF_PORTENABLE ? "ok" : "already"));
|
|
newval &= ~UHPF_PORTENABLE;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_CONNECTION:
|
|
newval |= UHPF_CONNECTCHANGE; // clear-on-write!
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_CONNECTION;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_ENABLE:
|
|
newval |= UHPF_ENABLECHANGE; // clear-on-write!
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_ENABLE;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_SUSPEND: // ignore for UHCI, there's no bit indicating this
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_SUSPEND; // manually fake suspend change clearing
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_OVER_CURRENT: // ignore for UHCI, there's no bit indicating this
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_OVER_CURRENT; // manually fake over current clearing
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_RESET: // ignore for UHCI, there's no bit indicating this
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_RESET; // manually fake reset change clearing
|
|
cmdgood = TRUE;
|
|
break;
|
|
}
|
|
if(cmdgood)
|
|
{
|
|
KPRINTF(5, ("Port %ld CLEAR_FEATURE %04lx->%04lx\n", idx, oldval, newval));
|
|
WRITEIO16_LE(hc->hc_RegBase, portreg, newval);
|
|
if(hc->hc_PortChangeMap[hciport])
|
|
{
|
|
unit->hu_RootPortChanges |= 1UL<<idx;
|
|
} else {
|
|
unit->hu_RootPortChanges &= ~(1UL<<idx);
|
|
}
|
|
return(0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HCITYPE_OHCI:
|
|
{
|
|
UWORD portreg = OHCI_PORTSTATUS + (hciport<<2);
|
|
ULONG __unused oldval = READREG32_LE(hc->hc_RegBase, portreg);
|
|
|
|
switch(val)
|
|
{
|
|
case UFS_PORT_ENABLE:
|
|
KPRINTF(10, ("Disabling Port (%s)\n", oldval & OHPF_PORTENABLE ? "ok" : "already"));
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTDISABLE);
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_PORT_SUSPEND:
|
|
KPRINTF(10, ("Resuming Port (%s)\n", oldval & OHPF_PORTSUSPEND ? "ok" : "already"));
|
|
//hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_SUSPEND; // manually fake suspend change
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_RESUME);
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_PORT_POWER:
|
|
KPRINTF(10, ("Unpowering Port (%s)\n", oldval & OHPF_PORTPOWER ? "ok" : "already"));
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTUNPOWER);
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_CONNECTION:
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_CONNECTCHANGE);
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_CONNECTION;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_ENABLE:
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_ENABLECHANGE);
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_ENABLE;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_SUSPEND:
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_RESUMEDTX);
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_SUSPEND;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_OVER_CURRENT:
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_OVERCURRENTCHG);
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_OVER_CURRENT;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_RESET:
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_RESETCHANGE);
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_RESET;
|
|
cmdgood = TRUE;
|
|
break;
|
|
}
|
|
if(cmdgood)
|
|
{
|
|
return(0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HCITYPE_EHCI:
|
|
{
|
|
UWORD portreg = EHCI_PORTSC1 + (hciport<<2);
|
|
ULONG oldval = READREG32_LE(hc->hc_RegBase, portreg) & ~(EHPF_OVERCURRENTCHG|EHPF_ENABLECHANGE|EHPF_CONNECTCHANGE); // these are clear-on-write!
|
|
ULONG newval = oldval;
|
|
switch(val)
|
|
{
|
|
case UFS_PORT_ENABLE:
|
|
KPRINTF(10, ("Disabling Port (%s)\n", newval & EHPF_PORTENABLE ? "ok" : "already"));
|
|
newval &= ~EHPF_PORTENABLE;
|
|
cmdgood = TRUE;
|
|
// disable enumeration
|
|
unit->hu_DevControllers[0] = NULL;
|
|
break;
|
|
|
|
case UFS_PORT_SUSPEND:
|
|
newval &= ~EHPF_PORTSUSPEND;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_PORT_POWER: // ignore for UHCI, there's no power control here
|
|
KPRINTF(10, ("Disabling Power (%s)\n", newval & EHPF_PORTPOWER ? "ok" : "already"));
|
|
KPRINTF(10, ("Disabling Port (%s)\n", newval & EHPF_PORTENABLE ? "ok" : "already"));
|
|
newval &= ~(EHPF_PORTENABLE|EHPF_PORTPOWER);
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_CONNECTION:
|
|
newval |= EHPF_CONNECTCHANGE; // clear-on-write!
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_CONNECTION;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_ENABLE:
|
|
newval |= EHPF_ENABLECHANGE; // clear-on-write!
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_ENABLE;
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_SUSPEND: // ignore for EHCI, there's no bit indicating this
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_SUSPEND; // manually fake suspend change clearing
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_OVER_CURRENT:
|
|
newval |= EHPF_OVERCURRENTCHG; // clear-on-write!
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_OVER_CURRENT; // manually fake over current clearing
|
|
cmdgood = TRUE;
|
|
break;
|
|
|
|
case UFS_C_PORT_RESET: // ignore for EHCI, there's no bit indicating this
|
|
hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_RESET; // manually fake reset change clearing
|
|
cmdgood = TRUE;
|
|
break;
|
|
}
|
|
if(cmdgood)
|
|
{
|
|
KPRINTF(5, ("Port %ld CLEAR_FEATURE %08lx->%08lx\n", idx, oldval, newval));
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, newval);
|
|
if(hc->hc_PortChangeMap[hciport])
|
|
{
|
|
unit->hu_RootPortChanges |= 1UL<<idx;
|
|
} else {
|
|
unit->hu_RootPortChanges &= ~(1UL<<idx);
|
|
}
|
|
return(0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case (URTF_IN|URTF_CLASS|URTF_OTHER):
|
|
switch(req)
|
|
{
|
|
case USR_GET_STATUS:
|
|
{
|
|
UWORD *mptr = ioreq->iouh_Data;
|
|
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(unit->hu_EhciOwned[idx - 1])
|
|
{
|
|
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:
|
|
{
|
|
UWORD portreg = hciport ? UHCI_PORT2STSCTRL : UHCI_PORT1STSCTRL;
|
|
UWORD oldval = READIO16_LE(hc->hc_RegBase, portreg);
|
|
*mptr = AROS_WORD2LE(UPSF_PORT_POWER);
|
|
if(oldval & UHPF_PORTCONNECTED) *mptr |= AROS_WORD2LE(UPSF_PORT_CONNECTION);
|
|
if(oldval & UHPF_PORTENABLE) *mptr |= AROS_WORD2LE(UPSF_PORT_ENABLE);
|
|
if(oldval & UHPF_LOWSPEED) *mptr |= AROS_WORD2LE(UPSF_PORT_LOW_SPEED);
|
|
if(oldval & UHPF_PORTRESET) *mptr |= AROS_WORD2LE(UPSF_PORT_RESET);
|
|
if(oldval & UHPF_PORTSUSPEND) *mptr |= AROS_WORD2LE(UPSF_PORT_SUSPEND);
|
|
|
|
KPRINTF(5, ("UHCI Port %ld is %s\n", idx, oldval & UHPF_LOWSPEED ? "LOWSPEED" : "FULLSPEED"));
|
|
KPRINTF(5, ("UHCI Port %ld Status %08lx\n", idx, *mptr));
|
|
|
|
mptr++;
|
|
if(oldval & UHPF_ENABLECHANGE)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_ENABLE;
|
|
}
|
|
if(oldval & UHPF_CONNECTCHANGE)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_CONNECTION;
|
|
}
|
|
if(oldval & UHPF_RESUMEDTX)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_SUSPEND|UPSF_PORT_ENABLE;
|
|
}
|
|
*mptr = AROS_WORD2LE(hc->hc_PortChangeMap[hciport]);
|
|
WRITEIO16_LE(hc->hc_RegBase, portreg, oldval);
|
|
KPRINTF(5, ("UHCI Port %ld Change %08lx\n", idx, *mptr));
|
|
return(0);
|
|
}
|
|
|
|
case HCITYPE_OHCI:
|
|
{
|
|
UWORD portreg = OHCI_PORTSTATUS + (hciport<<2);
|
|
ULONG oldval = READREG32_LE(hc->hc_RegBase, portreg);
|
|
|
|
*mptr = 0;
|
|
if(oldval & OHPF_PORTPOWER) *mptr |= AROS_WORD2LE(UPSF_PORT_POWER);
|
|
if(oldval & OHPF_OVERCURRENT) *mptr |= AROS_WORD2LE(UPSF_PORT_OVER_CURRENT);
|
|
if(oldval & OHPF_PORTCONNECTED) *mptr |= AROS_WORD2LE(UPSF_PORT_CONNECTION);
|
|
if(oldval & OHPF_PORTENABLE) *mptr |= AROS_WORD2LE(UPSF_PORT_ENABLE);
|
|
if(oldval & OHPF_LOWSPEED) *mptr |= AROS_WORD2LE(UPSF_PORT_LOW_SPEED);
|
|
if(oldval & OHPF_PORTRESET) *mptr |= AROS_WORD2LE(UPSF_PORT_RESET);
|
|
if(oldval & OHPF_PORTSUSPEND) *mptr |= AROS_WORD2LE(UPSF_PORT_SUSPEND);
|
|
|
|
KPRINTF(5, ("OHCI Port %ld (glob. %ld) is %s\n", hciport, idx, oldval & OHPF_LOWSPEED ? "LOWSPEED" : "FULLSPEED"));
|
|
KPRINTF(5, ("OHCI Port %ld Status %08lx (%08lx)\n", idx, *mptr, oldval));
|
|
|
|
mptr++;
|
|
if(oldval & OHPF_OVERCURRENTCHG)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_OVER_CURRENT;
|
|
}
|
|
if(oldval & OHPF_RESETCHANGE)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_RESET;
|
|
}
|
|
if(oldval & OHPF_ENABLECHANGE)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_ENABLE;
|
|
}
|
|
if(oldval & OHPF_CONNECTCHANGE)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_CONNECTION;
|
|
}
|
|
if(oldval & OHPF_RESUMEDTX)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_SUSPEND;
|
|
}
|
|
*mptr = AROS_WORD2LE(hc->hc_PortChangeMap[hciport]);
|
|
KPRINTF(5, ("OHCI Port %ld Change %08lx\n", idx, *mptr));
|
|
return(0);
|
|
}
|
|
|
|
case HCITYPE_EHCI:
|
|
{
|
|
UWORD portreg = EHCI_PORTSC1 + (hciport<<2);
|
|
ULONG oldval = READREG32_LE(hc->hc_RegBase, portreg);
|
|
|
|
*mptr = 0;
|
|
if(oldval & EHPF_PORTCONNECTED) *mptr |= AROS_WORD2LE(UPSF_PORT_CONNECTION);
|
|
if(oldval & EHPF_PORTENABLE) *mptr |= AROS_WORD2LE(UPSF_PORT_ENABLE|UPSF_PORT_HIGH_SPEED);
|
|
if((oldval & (EHPF_LINESTATUS_DM|EHPF_PORTCONNECTED|EHPF_PORTENABLE)) ==
|
|
(EHPF_LINESTATUS_DM|EHPF_PORTCONNECTED))
|
|
{
|
|
KPRINTF(10, ("EHCI Port %ld is LOWSPEED\n", idx));
|
|
// we need to detect low speed devices prior to reset
|
|
*mptr |= AROS_WORD2LE(UPSF_PORT_LOW_SPEED);
|
|
}
|
|
|
|
if(oldval & EHPF_PORTRESET) *mptr |= AROS_WORD2LE(UPSF_PORT_RESET);
|
|
if(oldval & EHPF_PORTSUSPEND) *mptr |= AROS_WORD2LE(UPSF_PORT_SUSPEND);
|
|
if(oldval & EHPF_PORTPOWER) *mptr |= AROS_WORD2LE(UPSF_PORT_POWER);
|
|
if(oldval & EHPM_PORTINDICATOR) *mptr |= AROS_WORD2LE(UPSF_PORT_INDICATOR);
|
|
|
|
KPRINTF(5, ("EHCI Port %ld Status %08lx\n", idx, *mptr));
|
|
|
|
mptr++;
|
|
if(oldval & EHPF_ENABLECHANGE)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_ENABLE;
|
|
}
|
|
if(oldval & EHPF_CONNECTCHANGE)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_CONNECTION;
|
|
}
|
|
if(oldval & EHPF_RESUMEDTX)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_SUSPEND|UPSF_PORT_ENABLE;
|
|
}
|
|
if(oldval & EHPF_OVERCURRENTCHG)
|
|
{
|
|
hc->hc_PortChangeMap[hciport] |= UPSF_PORT_OVER_CURRENT;
|
|
}
|
|
*mptr = AROS_WORD2LE(hc->hc_PortChangeMap[hciport]);
|
|
WRITEREG32_LE(hc->hc_RegBase, portreg, oldval);
|
|
KPRINTF(5, ("EHCI Port %ld Change %08lx\n", idx, *mptr));
|
|
return(0);
|
|
}
|
|
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case (URTF_IN|URTF_CLASS|URTF_DEVICE):
|
|
switch(req)
|
|
{
|
|
case USR_GET_STATUS:
|
|
{
|
|
UWORD *mptr = ioreq->iouh_Data;
|
|
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_HUB:
|
|
{
|
|
ULONG hubdesclen = 9;
|
|
ULONG 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 >= 6)
|
|
{
|
|
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;
|
|
if(localpwgood > powergood)
|
|
{
|
|
powergood = localpwgood;
|
|
KPRINTF(10, ("Increasing power good time to %ld\n", powergood));
|
|
}
|
|
}
|
|
hc = (struct PCIController *) hc->hc_Node.ln_Succ;
|
|
}
|
|
|
|
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, ("RH: Unsupported command %02lx %02lx %04lx %04lx %04lx!\n", rt, req, idx, val, len));
|
|
return(UHIOERR_STALL);
|
|
}
|
|
/* \\\ */
|
|
|
|
/* /// "cmdIntXFerRootHub()" */
|
|
WORD cmdIntXFerRootHub(struct IOUsbHWReq *ioreq,
|
|
struct PCIUnit *unit,
|
|
struct PCIDevice *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;
|
|
|
|
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)
|
|
{
|
|
return(UHIOERR_HOSTERROR);
|
|
}
|
|
|
|
ioreq->iouh_Req.io_Flags &= ~IOF_QUICK;
|
|
ioreq->iouh_Actual = 0;
|
|
|
|
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)
|
|
{
|
|
return(UHIOERR_HOSTERROR);
|
|
}
|
|
|
|
ioreq->iouh_Req.io_Flags &= ~IOF_QUICK;
|
|
ioreq->iouh_Actual = 0;
|
|
|
|
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)
|
|
{
|
|
return(UHIOERR_HOSTERROR);
|
|
}
|
|
|
|
ioreq->iouh_Req.io_Flags &= ~IOF_QUICK;
|
|
ioreq->iouh_Actual = 0;
|
|
|
|
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;
|
|
}
|
|
hc = (struct PCIController *) hc->hc_Node.ln_Succ;
|
|
}
|
|
Enable();
|
|
/* Return success
|
|
*/
|
|
return RC_OK;
|
|
}
|
|
/* \\\ */
|
|
|
|
/* /// "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,
|
|
NSCMD_DEVICEQUERY, 0
|
|
};
|
|
|
|
WORD cmdNSDeviceQuery(struct IOStdReq *ioreq,
|
|
struct PCIUnit *unit,
|
|
struct PCIDevice *base)
|
|
{
|
|
struct my_NSDeviceQueryResult *query;
|
|
|
|
query = (struct my_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 my_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 my_NSDeviceQueryResult);
|
|
query->DeviceType = NSDEVTYPE_USBHARDWARE;
|
|
query->DeviceSubType = 0;
|
|
query->SupportedCommands = 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(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 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;
|
|
|
|
/* 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;
|
|
}
|
|
causeint = FALSE;
|
|
switch(hc->hc_HCIType)
|
|
{
|
|
case HCITYPE_UHCI:
|
|
{
|
|
ULONG framecnt;
|
|
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, ("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, ("UQH terminated %08lx\n", linkelem));
|
|
if(framecnt > unit->hu_NakTimeoutFrame[devadrep])
|
|
{
|
|
// give the thing the chance to exit gracefully
|
|
KPRINTF(20, ("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, ("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, ("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:
|
|
{
|
|
ULONG framecnt;
|
|
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, ("Examining IOReq=%p with OED=%p\n", ioreq, ioreq->iouh_DriverPrivate1));
|
|
if (ioreq->iouh_DriverPrivate1)
|
|
{
|
|
KPRINTF(1, ("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(&hc->hc_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, ("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:
|
|
{
|
|
ULONG framecnt;
|
|
ehciUpdateFrameCounter(hc);
|
|
framecnt = hc->hc_FrameCounter;
|
|
// NakTimeout
|
|
for(cnt = 0; cnt < 1; 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, ("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, ("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, ("INT missed?!? Manually causing it! %08lx, IOReq=%p\n",
|
|
ctrlstatus, ioreq));
|
|
causeint = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ioreq = (struct IOUsbHWReq *) ((struct Node *) ioreq)->ln_Succ;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
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
|
|
}
|
|
/* \\\ */
|
|
|