Patch by Jian Chiang as part of his GSoc Project (coding style fixes by myself):
* xhci controller start operation * command ring and event ring initialization * No-Op Command test and real xhci irq handle * xhci root hub support * add Super Speed enumeration and xhci_rh.cpp into jamfile git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42511 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
3b1fd3270e
commit
d6e4f54f2d
@ -90,7 +90,8 @@ typedef enum {
|
||||
USB_SPEED_LOWSPEED = 0,
|
||||
USB_SPEED_FULLSPEED,
|
||||
USB_SPEED_HIGHSPEED,
|
||||
USB_SPEED_MAX = USB_SPEED_HIGHSPEED
|
||||
USB_SPEED_SUPER,
|
||||
USB_SPEED_MAX = USB_SPEED_SUPER
|
||||
} usb_speed;
|
||||
|
||||
|
||||
|
@ -28,6 +28,7 @@ KernelAddon <usb>ehci :
|
||||
|
||||
KernelAddon <usb>xhci :
|
||||
xhci.cpp
|
||||
xhci_rh.cpp
|
||||
: libusb.a
|
||||
: xhci.rdef
|
||||
;
|
||||
|
@ -63,7 +63,22 @@ XHCI::XHCI(pci_info *info, Stack *stack)
|
||||
fRegisterArea(-1),
|
||||
fPCIInfo(info),
|
||||
fStack(stack),
|
||||
fPortCount(0)
|
||||
fErstArea(-1),
|
||||
fDcbaArea(-1),
|
||||
fSpinlock(B_SPINLOCK_INITIALIZER),
|
||||
fCmdCompSem(-1),
|
||||
fCmdCompThread(-1),
|
||||
fFinishTransfersSem(-1),
|
||||
fFinishThread(-1),
|
||||
fStopThreads(false),
|
||||
fRootHub(NULL),
|
||||
fRootHubAddress(0),
|
||||
fPortCount(0),
|
||||
fSlotCount(0),
|
||||
fEventIdx(0),
|
||||
fCmdIdx(0),
|
||||
fEventCcs(1),
|
||||
fCmdCcs(1)
|
||||
{
|
||||
if (BusManager::InitCheck() < B_OK) {
|
||||
TRACE_ERROR("bus manager failed to init\n");
|
||||
@ -76,7 +91,7 @@ XHCI::XHCI(pci_info *info, Stack *stack)
|
||||
// enable busmaster and memory mapped access
|
||||
uint16 command = sPCIModule->read_pci_config(fPCIInfo->bus,
|
||||
fPCIInfo->device, fPCIInfo->function, PCI_command, 2);
|
||||
command &= ~PCI_command_io;
|
||||
command &= ~(PCI_command_io | PCI_command_int_disable);
|
||||
command |= PCI_command_master | PCI_command_memory;
|
||||
|
||||
sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device,
|
||||
@ -92,7 +107,7 @@ XHCI::XHCI(pci_info *info, Stack *stack)
|
||||
fPCIInfo->u.h0.base_registers[0], physicalAddress, offset,
|
||||
fPCIInfo->u.h0.base_register_sizes[0]);
|
||||
|
||||
fRegisterArea = map_physical_memory("EHCI memory mapped registers",
|
||||
fRegisterArea = map_physical_memory("XHCI memory mapped registers",
|
||||
physicalAddress, mapSize, B_ANY_KERNEL_BLOCK_ADDRESS,
|
||||
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA,
|
||||
(void **)&fCapabilityRegisters);
|
||||
@ -103,62 +118,55 @@ XHCI::XHCI(pci_info *info, Stack *stack)
|
||||
|
||||
fCapabilityRegisters += offset;
|
||||
fOperationalRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_CAPLENGTH);
|
||||
fRuntimeRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_RTSOFF);
|
||||
fRuntimeRegisters = fCapabilityRegisters + ReadCapReg32(XHCI_RTSOFF);
|
||||
fDoorbellRegisters = fCapabilityRegisters + ReadCapReg32(XHCI_DBOFF);
|
||||
TRACE("mapped capability registers: 0x%08lx\n", (uint32)fCapabilityRegisters);
|
||||
TRACE("mapped operational registers: 0x%08lx\n", (uint32)fOperationalRegisters);
|
||||
TRACE("mapped rumtime registers: 0x%08lx\n", (uint32)fRuntimeRegisters);
|
||||
TRACE("mapped doorbell registers: 0x%08lx\n", (uint32)fDoorbellRegisters);
|
||||
|
||||
TRACE("structural parameters1: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS1));
|
||||
TRACE("structural parameters2: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS2));
|
||||
TRACE("structural parameters3: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS3));
|
||||
TRACE("capability parameters: 0x%08lx\n", ReadCapReg32(XHCI_HCCPARAMS));
|
||||
|
||||
// read port count from capability register
|
||||
fPortCount = (ReadCapReg32(XHCI_HCSPARAMS1) >> 24) & 0x7f;
|
||||
|
||||
uint32 extendedCapPointer = ((ReadCapReg32(XHCI_HCCPARAMS) >> 16) & 0xffff)
|
||||
<< 2;
|
||||
if (extendedCapPointer > 0) {
|
||||
TRACE("extended capabilities register at %ld\n", extendedCapPointer);
|
||||
|
||||
uint32 legacySupport = ReadCapReg32(extendedCapPointer);
|
||||
if ((legacySupport & XHCI_LEGSUP_CAPID_MASK) == XHCI_LEGSUP_CAPID) {
|
||||
if ((legacySupport & XHCI_LEGSUP_BIOSOWNED) != 0) {
|
||||
TRACE_ALWAYS("the host controller is bios owned, claiming"
|
||||
" ownership\n");
|
||||
WriteCapReg32(extendedCapPointer, legacySupport
|
||||
| XHCI_LEGSUP_OSOWNED);
|
||||
for (int32 i = 0; i < 20; i++) {
|
||||
legacySupport = ReadCapReg32(extendedCapPointer);
|
||||
|
||||
if ((legacySupport & XHCI_LEGSUP_BIOSOWNED) == 0)
|
||||
break;
|
||||
|
||||
TRACE_ALWAYS("controller is still bios owned, waiting\n");
|
||||
snooze(50000);
|
||||
}
|
||||
}
|
||||
|
||||
if (legacySupport & XHCI_LEGSUP_BIOSOWNED) {
|
||||
TRACE_ERROR("bios won't give up control over the host "
|
||||
"controller (ignoring)\n");
|
||||
} else if (legacySupport & XHCI_LEGSUP_OSOWNED) {
|
||||
TRACE_ALWAYS("successfully took ownership of the host "
|
||||
"controller\n");
|
||||
}
|
||||
|
||||
// Force off the BIOS owned flag, and clear all SMIs. Some BIOSes
|
||||
// do indicate a successful handover but do not remove their SMIs
|
||||
// and then freeze the system when interrupts are generated.
|
||||
WriteCapReg32(extendedCapPointer, legacySupport & ~XHCI_LEGSUP_BIOSOWNED);
|
||||
WriteCapReg32(extendedCapPointer + XHCI_LEGCTLSTS,
|
||||
XHCI_LEGCTLSTS_DISABLE_SMI);
|
||||
} else {
|
||||
TRACE("extended capability is not a legacy support register\n");
|
||||
}
|
||||
} else {
|
||||
TRACE("no extended capabilities register\n");
|
||||
uint32 cparams = ReadCapReg32(XHCI_HCCPARAMS);
|
||||
uint32 eec = 0xffffffff;
|
||||
uint32 eecp = HCS0_XECP(cparams) << 2;
|
||||
for (; eecp != 0 && XECP_NEXT(eec); eecp += XECP_NEXT(eec) << 2) {
|
||||
eec = ReadCapReg32(eecp);
|
||||
if (XECP_ID(eec) != XHCI_LEGSUP_CAPID)
|
||||
continue;
|
||||
}
|
||||
if (eec & XHCI_LEGSUP_BIOSOWNED) {
|
||||
TRACE_ALWAYS("the host controller is bios owned, claiming"
|
||||
" ownership\n");
|
||||
WriteCapReg32(eecp, eec | XHCI_LEGSUP_OSOWNED);
|
||||
|
||||
for (int32 i = 0; i < 20; i++) {
|
||||
eec = ReadCapReg32(eecp);
|
||||
|
||||
if ((eec & XHCI_LEGSUP_BIOSOWNED) == 0)
|
||||
break;
|
||||
|
||||
TRACE_ALWAYS("controller is still bios owned, waiting\n");
|
||||
snooze(50000);
|
||||
}
|
||||
|
||||
if (eec & XHCI_LEGSUP_BIOSOWNED) {
|
||||
TRACE_ERROR("bios won't give up control over the host "
|
||||
"controller (ignoring)\n");
|
||||
} else if (eec & XHCI_LEGSUP_OSOWNED) {
|
||||
TRACE_ALWAYS("successfully took ownership of the host "
|
||||
"controller\n");
|
||||
}
|
||||
|
||||
// Force off the BIOS owned flag, and clear all SMIs. Some BIOSes
|
||||
// do indicate a successful handover but do not remove their SMIs
|
||||
// and then freeze the system when interrupts are generated.
|
||||
WriteCapReg32(eecp, eec & ~XHCI_LEGSUP_BIOSOWNED);
|
||||
}
|
||||
WriteCapReg32(eecp + XHCI_LEGCTLSTS, XHCI_LEGCTLSTS_DISABLE_SMI);
|
||||
|
||||
// halt the host controller
|
||||
if (ControllerHalt() < B_OK) {
|
||||
@ -171,6 +179,28 @@ XHCI::XHCI(pci_info *info, Stack *stack)
|
||||
return;
|
||||
}
|
||||
|
||||
fCmdCompSem = create_sem(0, "XHCI Command Complete");
|
||||
fFinishTransfersSem = create_sem(0, "XHCI Finish Transfers");
|
||||
if (fFinishTransfersSem < B_OK || fCmdCompSem < B_OK) {
|
||||
TRACE_ERROR("failed to create semaphores\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// create finisher service thread
|
||||
fFinishThread = spawn_kernel_thread(FinishThread, "xhci finish thread",
|
||||
B_NORMAL_PRIORITY, (void *)this);
|
||||
resume_thread(fFinishThread);
|
||||
|
||||
// create command complete service thread
|
||||
fCmdCompThread = spawn_kernel_thread(CmdCompThread, "xhci cmd complete thread",
|
||||
B_NORMAL_PRIORITY, (void *)this);
|
||||
resume_thread(fCmdCompThread);
|
||||
|
||||
// Install the interrupt handler
|
||||
TRACE("installing interrupt handler\n");
|
||||
install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line,
|
||||
InterruptHandler, (void *)this, 0);
|
||||
|
||||
fInitOK = true;
|
||||
TRACE("XHCI host controller driver constructed\n");
|
||||
}
|
||||
@ -182,7 +212,15 @@ XHCI::~XHCI()
|
||||
|
||||
WriteOpReg(XHCI_CMD, 0);
|
||||
|
||||
int32 result = 0;
|
||||
fStopThreads = true;
|
||||
delete_sem(fCmdCompSem);
|
||||
delete_sem(fFinishTransfersSem);
|
||||
delete_area(fRegisterArea);
|
||||
delete_area(fErstArea);
|
||||
delete_area(fDcbaArea);
|
||||
wait_for_thread(fCmdCompThread, &result);
|
||||
wait_for_thread(fFinishThread, &result);
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
}
|
||||
|
||||
@ -194,7 +232,100 @@ XHCI::Start()
|
||||
TRACE("usbcmd: 0x%08lx; usbsts: 0x%08lx\n", ReadOpReg(XHCI_CMD),
|
||||
ReadOpReg(XHCI_STS));
|
||||
|
||||
if ((ReadOpReg(XHCI_PAGESIZE) & (1 << 0)) == 0) {
|
||||
TRACE_ERROR("Controller does not support 4K page size.\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// read port count from capability register
|
||||
uint32 capabilities = ReadCapReg32(XHCI_HCSPARAMS1);
|
||||
|
||||
uint8 portsCount = HCS_MAX_PORTS(capabilities);
|
||||
if (portsCount == 0) {
|
||||
TRACE_ERROR("Invalid number of ports: %u\n", portsCount);
|
||||
return B_ERROR;
|
||||
}
|
||||
fPortCount = portsCount;
|
||||
fSlotCount = HCS_MAX_SLOTS(capabilities);
|
||||
WriteOpReg(XHCI_CONFIG, fSlotCount);
|
||||
|
||||
void *dmaAddress;
|
||||
fDcbaArea = fStack->AllocateArea((void **)&fDcba, &dmaAddress,
|
||||
sizeof(uint64) * XHCI_MAX_SLOTS, "DCBA Area");
|
||||
if (fDcbaArea < B_OK) {
|
||||
TRACE_ERROR("unable to create the DCBA area\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
memset(fDcba, 0, sizeof(uint64) * XHCI_MAX_SLOTS);
|
||||
TRACE("setting DCBAAP\n");
|
||||
WriteOpReg(XHCI_DCBAAP_LO, (uint32)dmaAddress);
|
||||
WriteOpReg(XHCI_DCBAAP_HI, 0);
|
||||
|
||||
fErstArea = fStack->AllocateArea((void **)&fErst, &dmaAddress,
|
||||
(MAX_COMMANDS + MAX_EVENTS) * sizeof(xhci_trb)
|
||||
+ sizeof(xhci_erst_element),
|
||||
"USB XHCI ERST CMD_RING and EVENT_RING Area");
|
||||
|
||||
if (fErstArea < B_OK) {
|
||||
TRACE_ERROR("unable to create the ERST AND RING area\n");
|
||||
delete_area(fDcbaArea);
|
||||
return B_ERROR;
|
||||
}
|
||||
memset(fErst, 0, (MAX_COMMANDS + MAX_EVENTS) * sizeof(xhci_trb)
|
||||
+ sizeof(xhci_erst_element));
|
||||
|
||||
fErst->rs_addr = (uint32)dmaAddress + sizeof(xhci_erst_element);
|
||||
fErst->rs_size = MAX_EVENTS;
|
||||
fErst->rsvdz = 0;
|
||||
|
||||
uint32 addr = (uint32)fErst + sizeof(xhci_erst_element);
|
||||
fEventRing = (xhci_trb *)addr;
|
||||
addr += MAX_EVENTS * sizeof(xhci_trb);
|
||||
fCmdRing = (xhci_trb *)addr;
|
||||
|
||||
TRACE("setting ERST size\n");
|
||||
WriteRunReg32(XHCI_ERSTSZ(0), XHCI_ERSTS_SET(1));
|
||||
|
||||
TRACE("setting ERDP addr = 0x%llx\n", fErst->rs_addr);
|
||||
WriteRunReg32(XHCI_ERDP_LO(0), (uint32)fErst->rs_addr);
|
||||
WriteRunReg32(XHCI_ERDP_HI(0), (uint32)(fErst->rs_addr >> 32));
|
||||
|
||||
TRACE("setting ERST base addr = 0x%llx\n", (uint64)dmaAddress);
|
||||
WriteRunReg32(XHCI_ERSTBA_LO(0), (uint32)dmaAddress);
|
||||
WriteRunReg32(XHCI_ERSTBA_HI(0), 0);
|
||||
|
||||
addr = fErst->rs_addr + MAX_EVENTS * sizeof(xhci_trb);
|
||||
TRACE("setting CRCR addr = 0x%llx\n", (uint64)addr);
|
||||
WriteOpReg(XHCI_CRCR_LO, addr | CRCR_RCS);
|
||||
WriteOpReg(XHCI_CRCR_HI, 0);
|
||||
//link trb
|
||||
fCmdRing[MAX_COMMANDS - 1].qwtrb0 = addr;
|
||||
|
||||
TRACE("setting interrupt rate\n");
|
||||
WriteRunReg32(XHCI_IMOD(0), 160);//4000 irq/s
|
||||
|
||||
TRACE("enabling interrupt\n");
|
||||
WriteRunReg32(XHCI_IMAN(0), ReadRunReg32(XHCI_IMAN(0)) | IMAN_INTR_ENA);
|
||||
|
||||
WriteOpReg(XHCI_CMD, CMD_RUN | CMD_EIE | CMD_HSEIE);
|
||||
|
||||
fRootHubAddress = AllocateAddress();
|
||||
fRootHub = new(std::nothrow) XHCIRootHub(RootObject(), fRootHubAddress);
|
||||
if (!fRootHub) {
|
||||
TRACE_ERROR("no memory to allocate root hub\n");
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (fRootHub->InitCheck() < B_OK) {
|
||||
TRACE_ERROR("root hub failed init check\n");
|
||||
return fRootHub->InitCheck();
|
||||
}
|
||||
|
||||
SetRootHub(fRootHub);
|
||||
|
||||
TRACE_ALWAYS("successfully started the controller\n");
|
||||
TRACE("No-Op test\n");
|
||||
QueueNoop();
|
||||
return BusManager::Start();
|
||||
}
|
||||
|
||||
@ -202,6 +333,10 @@ XHCI::Start()
|
||||
status_t
|
||||
XHCI::SubmitTransfer(Transfer *transfer)
|
||||
{
|
||||
// short circuit the root hub
|
||||
if (transfer->TransferPipe()->DeviceAddress() == fRootHubAddress)
|
||||
return fRootHub->ProcessTransfer(this, transfer);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@ -315,6 +450,46 @@ XHCI::AddTo(Stack *stack)
|
||||
status_t
|
||||
XHCI::GetPortStatus(uint8 index, usb_port_status *status)
|
||||
{
|
||||
if (index >= fPortCount)
|
||||
return B_BAD_INDEX;
|
||||
|
||||
status->status = status->change = 0;
|
||||
uint32 portStatus = ReadOpReg(XHCI_PORTSC(index));
|
||||
TRACE("port status=0x%08lx\n", portStatus);
|
||||
|
||||
// build the status
|
||||
switch(PS_SPEED_GET(portStatus)) {
|
||||
case 3:
|
||||
status->status |= PORT_STATUS_HIGH_SPEED;
|
||||
break;
|
||||
case 2:
|
||||
status->status |= PORT_STATUS_LOW_SPEED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (portStatus & PS_CCS)
|
||||
status->status |= PORT_STATUS_CONNECTION;
|
||||
if (portStatus & PS_PED)
|
||||
status->status |= PORT_STATUS_ENABLE;
|
||||
if (portStatus & PS_OCA)
|
||||
status->status |= PORT_STATUS_OVER_CURRENT;
|
||||
if (portStatus & PS_PR)
|
||||
status->status |= PORT_STATUS_RESET;
|
||||
if (portStatus & PS_PP)
|
||||
status->status |= PORT_STATUS_POWER;
|
||||
|
||||
// build the change
|
||||
if (portStatus & PS_CSC)
|
||||
status->change |= PORT_STATUS_CONNECTION;
|
||||
if (portStatus & PS_PEC)
|
||||
status->change |= PORT_STATUS_ENABLE;
|
||||
if (portStatus & PS_OCC)
|
||||
status->change |= PORT_STATUS_OVER_CURRENT;
|
||||
if (portStatus & PS_PRC)
|
||||
status->change |= PORT_STATUS_RESET;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@ -322,6 +497,36 @@ XHCI::GetPortStatus(uint8 index, usb_port_status *status)
|
||||
status_t
|
||||
XHCI::SetPortFeature(uint8 index, uint16 feature)
|
||||
{
|
||||
TRACE("set port feature index %u feature %u\n", index, feature);
|
||||
if (index >= fPortCount)
|
||||
return B_BAD_INDEX;
|
||||
|
||||
uint32 portRegister = XHCI_PORTSC(index);
|
||||
uint32 portStatus = ReadOpReg(portRegister);
|
||||
|
||||
switch (feature) {
|
||||
case PORT_SUSPEND:
|
||||
if ((portStatus & PS_PED ) == 0 || (portStatus & PS_PR)
|
||||
|| (portStatus & PS_PLS_MASK) >= PS_XDEV_U3) {
|
||||
TRACE_ERROR("USB core suspending device not in U0/U1/U2.\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
portStatus &= ~PS_CLEAR;
|
||||
portStatus &= ~PS_PLS_MASK;
|
||||
portStatus |= PS_LWS | PS_XDEV_U3;
|
||||
WriteOpReg(portRegister, portStatus);
|
||||
return B_OK;
|
||||
|
||||
case PORT_RESET:
|
||||
portStatus &= ~PS_CLEAR;
|
||||
WriteOpReg(portRegister, portStatus | PS_PR);
|
||||
return B_OK;
|
||||
|
||||
case PORT_POWER:
|
||||
portStatus &= ~PS_CLEAR;
|
||||
WriteOpReg(portRegister, portStatus | PS_PP);
|
||||
return B_OK;
|
||||
}
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
@ -329,26 +534,51 @@ XHCI::SetPortFeature(uint8 index, uint16 feature)
|
||||
status_t
|
||||
XHCI::ClearPortFeature(uint8 index, uint16 feature)
|
||||
{
|
||||
TRACE("clear port feature index %u feature %u\n", index, feature);
|
||||
if (index >= fPortCount)
|
||||
return B_BAD_INDEX;
|
||||
|
||||
uint32 portRegister = XHCI_PORTSC(index);
|
||||
uint32 portStatus = ReadOpReg(portRegister);
|
||||
portStatus &= ~PS_CLEAR;
|
||||
|
||||
switch (feature) {
|
||||
case PORT_SUSPEND:
|
||||
portStatus = ReadOpReg(portRegister);
|
||||
if (portStatus & PS_PR)
|
||||
return B_BAD_VALUE;
|
||||
if (portStatus & PS_XDEV_U3) {
|
||||
if ((portStatus & PS_PED) == 0)
|
||||
return B_BAD_VALUE;
|
||||
portStatus &= ~PS_CLEAR;
|
||||
portStatus &= ~PS_PLS_MASK;
|
||||
WriteOpReg(portRegister, portStatus | PS_XDEV_U0 | PS_LWS);
|
||||
}
|
||||
return B_OK;
|
||||
case PORT_ENABLE:
|
||||
WriteOpReg(portRegister, portStatus | PS_PED);
|
||||
return B_OK;
|
||||
case PORT_POWER:
|
||||
WriteOpReg(portRegister, portStatus & ~PS_PP);
|
||||
return B_OK;
|
||||
case C_PORT_CONNECTION:
|
||||
WriteOpReg(portRegister, portStatus | PS_CSC);
|
||||
return B_OK;
|
||||
case C_PORT_ENABLE:
|
||||
WriteOpReg(portRegister, portStatus | PS_PEC);
|
||||
return B_OK;
|
||||
case C_PORT_OVER_CURRENT:
|
||||
WriteOpReg(portRegister, portStatus | PS_OCC);
|
||||
return B_OK;
|
||||
case C_PORT_RESET:
|
||||
WriteOpReg(portRegister, portStatus | PS_PRC);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::ResetPort(uint8 index)
|
||||
{
|
||||
TRACE("reset port %d\n", index);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::SuspendPort(uint8 index)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::ControllerHalt()
|
||||
{
|
||||
@ -388,13 +618,6 @@ XHCI::ControllerReset()
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCI::LightReset()
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
XHCI::InterruptHandler(void *data)
|
||||
{
|
||||
@ -405,11 +628,215 @@ XHCI::InterruptHandler(void *data)
|
||||
int32
|
||||
XHCI::Interrupt()
|
||||
{
|
||||
acquire_spinlock(&fSpinlock);
|
||||
|
||||
uint32 status = ReadOpReg(XHCI_STS);
|
||||
uint32 temp = ReadRunReg32(XHCI_IMAN(0));
|
||||
WriteOpReg(XHCI_STS, status);
|
||||
WriteRunReg32(XHCI_IMAN(0), temp);
|
||||
TRACE("STS: %lx IRQ_PENDING: %lx\n", status, temp);
|
||||
|
||||
int32 result = B_HANDLED_INTERRUPT;
|
||||
|
||||
if (status & STS_HSE) {
|
||||
TRACE_ERROR("Host System Error\n");
|
||||
return result;
|
||||
}
|
||||
if (status & STS_HCE) {
|
||||
TRACE_ERROR("Host Controller Error\n");
|
||||
return result;
|
||||
}
|
||||
uint16 i = fEventIdx;
|
||||
uint8 j = fEventCcs;
|
||||
uint8 t = 2;
|
||||
|
||||
while (1) {
|
||||
temp = fEventRing[i].dwtrb3;
|
||||
uint8 k = (temp & TRB_3_CYCLE_BIT) ? 1 : 0;
|
||||
if (j != k)
|
||||
break;
|
||||
|
||||
uint8 event = TRB_TYPE_GET(temp);
|
||||
|
||||
TRACE("event[%u] = %u (0x%016llx 0x%08lx 0x%08lx)\n", i, event,
|
||||
fEventRing[i].qwtrb0, fEventRing[i].dwtrb2, fEventRing[i].dwtrb3);
|
||||
switch (event) {
|
||||
case TRB_COMPLETION:
|
||||
HandleCmdComplete(&fEventRing[i]);
|
||||
result = B_INVOKE_SCHEDULER;
|
||||
break;
|
||||
default:
|
||||
TRACE_ERROR("Unhandled event = %u\n", event);
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
if (i == MAX_EVENTS) {
|
||||
i = 0;
|
||||
j ^= 1;
|
||||
if (!--t)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fEventIdx = i;
|
||||
fEventCcs = j;
|
||||
|
||||
uint64 addr = fErst->rs_addr + i * sizeof(xhci_trb);
|
||||
addr |= ERST_EHB;
|
||||
WriteRunReg32(XHCI_ERDP_LO(0), (uint32)addr);
|
||||
WriteRunReg32(XHCI_ERDP_HI(0), (uint32)(addr >> 32));
|
||||
|
||||
|
||||
release_spinlock(&fSpinlock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XHCI::Ring()
|
||||
{
|
||||
TRACE("Ding Dong!\n")
|
||||
WriteDoorReg32(XHCI_DOORBELL(0), 0);
|
||||
/* Flush PCI posted writes */
|
||||
ReadDoorReg32(XHCI_DOORBELL(0));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XHCI::QueueCommand(xhci_trb *trb)
|
||||
{
|
||||
uint8 i, j;
|
||||
uint32 temp;
|
||||
|
||||
i = fCmdIdx;
|
||||
j = fCmdCcs;
|
||||
|
||||
TRACE("command[%u] = %lx (0x%016llx, 0x%08lx, 0x%08lx)\n",
|
||||
i, TRB_TYPE_GET(trb->dwtrb3),
|
||||
trb->qwtrb0, trb->dwtrb2, trb->dwtrb3);
|
||||
|
||||
fCmdRing[i].qwtrb0 = trb->qwtrb0;
|
||||
fCmdRing[i].dwtrb2 = trb->dwtrb2;
|
||||
temp = trb->dwtrb3;
|
||||
|
||||
if (j)
|
||||
temp |= TRB_3_CYCLE_BIT;
|
||||
else
|
||||
temp &= ~TRB_3_CYCLE_BIT;
|
||||
temp &= ~TRB_3_TC_BIT;
|
||||
fCmdRing[i].dwtrb3 = temp;
|
||||
|
||||
fCmdAddr = fErst->rs_addr + (MAX_EVENTS + i) * sizeof(xhci_trb);
|
||||
|
||||
i++;
|
||||
|
||||
if (i == (MAX_COMMANDS - 1)) {
|
||||
if (j)
|
||||
temp = TRB_3_CYCLE_BIT | TRB_TYPE(TRB_LINK);
|
||||
else
|
||||
temp = TRB_TYPE(TRB_LINK);
|
||||
fCmdRing[i].dwtrb3 = temp;
|
||||
|
||||
i = 0;
|
||||
j ^= 1;
|
||||
}
|
||||
|
||||
fCmdIdx = i;
|
||||
fCmdCcs = j;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XHCI::HandleCmdComplete(xhci_trb *trb)
|
||||
{
|
||||
if (fCmdAddr == trb->qwtrb0) {
|
||||
TRACE("Received command event\n");
|
||||
fCmdResult[0] = trb->dwtrb2;
|
||||
fCmdResult[1] = trb->dwtrb3;
|
||||
release_sem_etc(fCmdCompSem, 1, B_DO_NOT_RESCHEDULE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XHCI::QueueNoop()
|
||||
{
|
||||
xhci_trb trb;
|
||||
uint32 temp;
|
||||
|
||||
trb.qwtrb0 = 0;
|
||||
trb.dwtrb2 = 0;
|
||||
temp = TRB_TYPE(TRB_TR_NOOP);
|
||||
trb.dwtrb3 = temp;
|
||||
cpu_status state = disable_interrupts();
|
||||
acquire_spinlock(&fSpinlock);
|
||||
QueueCommand(&trb);
|
||||
Ring();
|
||||
release_spinlock(&fSpinlock);
|
||||
restore_interrupts(state);
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
XHCI::CmdCompThread(void *data)
|
||||
{
|
||||
((XHCI *)data)->CmdComplete();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XHCI::CmdComplete()
|
||||
{
|
||||
while (!fStopThreads) {
|
||||
if (acquire_sem(fCmdCompSem) < B_OK)
|
||||
continue;
|
||||
|
||||
// eat up sems that have been released by multiple interrupts
|
||||
int32 semCount = 0;
|
||||
get_sem_count(fCmdCompSem, &semCount);
|
||||
if (semCount > 0)
|
||||
acquire_sem_etc(fCmdCompSem, semCount, B_RELATIVE_TIMEOUT, 0);
|
||||
|
||||
TRACE("Command Complete\n");
|
||||
if (COMP_CODE_GET(fCmdResult[0]) != COMP_SUCCESS) {
|
||||
TRACE_ERROR("unsuccessful no-op command\n");
|
||||
//continue;
|
||||
}
|
||||
snooze(1000000 * 5);
|
||||
QueueNoop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
XHCI::FinishThread(void *data)
|
||||
{
|
||||
((XHCI *)data)->FinishTransfers();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XHCI::FinishTransfers()
|
||||
{
|
||||
while (!fStopThreads) {
|
||||
if (acquire_sem(fFinishTransfersSem) < B_OK)
|
||||
continue;
|
||||
|
||||
// eat up sems that have been released by multiple interrupts
|
||||
int32 semCount = 0;
|
||||
get_sem_count(fFinishTransfersSem, &semCount);
|
||||
if (semCount > 0)
|
||||
acquire_sem_etc(fFinishTransfersSem, semCount, B_RELATIVE_TIMEOUT, 0);
|
||||
|
||||
TRACE("finishing transfers\n");
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
XHCI::WriteOpReg(uint32 reg, uint32 value)
|
||||
{
|
||||
@ -451,3 +878,30 @@ XHCI::WriteCapReg32(uint32 reg, uint32 value)
|
||||
*(volatile uint32 *)(fCapabilityRegisters + reg) = value;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
XHCI::ReadRunReg32(uint32 reg)
|
||||
{
|
||||
return *(volatile uint32 *)(fRuntimeRegisters + reg);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
XHCI::WriteRunReg32(uint32 reg, uint32 value)
|
||||
{
|
||||
*(volatile uint32 *)(fRuntimeRegisters + reg) = value;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
XHCI::ReadDoorReg32(uint32 reg)
|
||||
{
|
||||
return *(volatile uint32 *)(fDoorbellRegisters + reg);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
XHCI::WriteDoorReg32(uint32 reg, uint32 value)
|
||||
{
|
||||
*(volatile uint32 *)(fDoorbellRegisters + reg) = value;
|
||||
}
|
||||
|
@ -14,8 +14,43 @@
|
||||
#include "xhci_hardware.h"
|
||||
|
||||
|
||||
#define MAX_EVENTS (16 * 13)
|
||||
#define MAX_COMMANDS (16 * 1)
|
||||
#define XHCI_MAX_SLOTS 256
|
||||
#define XHCI_MAX_PORTS 127
|
||||
|
||||
|
||||
struct pci_info;
|
||||
struct pci_module_info;
|
||||
class XHCIRootHub;
|
||||
|
||||
|
||||
struct xhci_trb {
|
||||
uint64 qwtrb0;
|
||||
uint32 dwtrb2;
|
||||
uint32 dwtrb3;
|
||||
};
|
||||
|
||||
|
||||
struct xhci_segment {
|
||||
xhci_trb * trbs;
|
||||
xhci_segment * next;
|
||||
};
|
||||
|
||||
|
||||
struct xhci_ring {
|
||||
xhci_segment * first_seg;
|
||||
xhci_trb * enqueue;
|
||||
xhci_trb * dequeue;
|
||||
};
|
||||
|
||||
|
||||
// Section 6.5
|
||||
struct xhci_erst_element {
|
||||
uint64 rs_addr;
|
||||
uint32 rs_size;
|
||||
uint32 rsvdz;
|
||||
} __attribute__((__aligned__(64)));
|
||||
|
||||
|
||||
class XHCI : public BusManager {
|
||||
@ -32,51 +67,107 @@ public:
|
||||
|
||||
static status_t AddTo(Stack *stack);
|
||||
|
||||
// Port operations for root hub
|
||||
// Port operations for root hub
|
||||
uint8 PortCount() { return fPortCount; };
|
||||
status_t GetPortStatus(uint8 index, usb_port_status *status);
|
||||
status_t SetPortFeature(uint8 index, uint16 feature);
|
||||
status_t ClearPortFeature(uint8 index, uint16 feature);
|
||||
|
||||
status_t ResetPort(uint8 index);
|
||||
status_t SuspendPort(uint8 index);
|
||||
|
||||
virtual const char * TypeName() const { return "xhci"; };
|
||||
|
||||
private:
|
||||
// Controller resets
|
||||
// Controller resets
|
||||
status_t ControllerReset();
|
||||
status_t ControllerHalt();
|
||||
status_t LightReset();
|
||||
|
||||
// Interrupt functions
|
||||
// Interrupt functions
|
||||
static int32 InterruptHandler(void *data);
|
||||
int32 Interrupt();
|
||||
|
||||
// Transfer management
|
||||
static int32 FinishThread(void *data);
|
||||
void FinishTransfers();
|
||||
|
||||
// Operational register functions
|
||||
// Command
|
||||
void QueueCommand(xhci_trb *trb);
|
||||
void HandleCmdComplete(xhci_trb *trb);
|
||||
|
||||
//Doorbell
|
||||
void Ring();
|
||||
|
||||
//no-op
|
||||
void QueueNoop();
|
||||
static int32 CmdCompThread(void *data);
|
||||
void CmdComplete();
|
||||
|
||||
// Operational register functions
|
||||
inline void WriteOpReg(uint32 reg, uint32 value);
|
||||
inline uint32 ReadOpReg(uint32 reg);
|
||||
|
||||
// Capability register functions
|
||||
// Capability register functions
|
||||
inline uint8 ReadCapReg8(uint32 reg);
|
||||
inline uint16 ReadCapReg16(uint32 reg);
|
||||
inline uint32 ReadCapReg32(uint32 reg);
|
||||
inline void WriteCapReg32(uint32 reg, uint32 value);
|
||||
|
||||
// Runtime register functions
|
||||
inline uint32 ReadRunReg32(uint32 reg);
|
||||
inline void WriteRunReg32(uint32 reg, uint32 value);
|
||||
|
||||
// Doorbell register functions
|
||||
inline uint32 ReadDoorReg32(uint32 reg);
|
||||
inline void WriteDoorReg32(uint32 reg, uint32 value);
|
||||
|
||||
static pci_module_info * sPCIModule;
|
||||
|
||||
uint8 * fCapabilityRegisters;
|
||||
uint8 * fOperationalRegisters;
|
||||
uint8 * fRuntimeRegisters;
|
||||
uint8 * fDoorbellRegisters;
|
||||
area_id fRegisterArea;
|
||||
pci_info * fPCIInfo;
|
||||
Stack * fStack;
|
||||
|
||||
// Root Hub
|
||||
area_id fErstArea;
|
||||
xhci_erst_element * fErst;
|
||||
xhci_trb * fEventRing;
|
||||
xhci_trb * fCmdRing;
|
||||
uint64 fCmdAddr;
|
||||
uint32 fCmdResult[2];
|
||||
|
||||
// Port management
|
||||
area_id fDcbaArea;
|
||||
uint8 * fDcba;
|
||||
|
||||
spinlock fSpinlock;
|
||||
|
||||
sem_id fCmdCompSem;
|
||||
thread_id fCmdCompThread;
|
||||
sem_id fFinishTransfersSem;
|
||||
thread_id fFinishThread;
|
||||
bool fStopThreads;
|
||||
|
||||
// Root Hub
|
||||
XHCIRootHub * fRootHub;
|
||||
uint8 fRootHubAddress;
|
||||
|
||||
// Port management
|
||||
uint8 fPortCount;
|
||||
uint8 fSlotCount;
|
||||
|
||||
uint16 fEventIdx;
|
||||
uint16 fCmdIdx;
|
||||
uint8 fEventCcs;
|
||||
uint8 fCmdCcs;
|
||||
};
|
||||
|
||||
|
||||
class XHCIRootHub : public Hub {
|
||||
public:
|
||||
XHCIRootHub(Object *rootObject,
|
||||
int8 deviceAddress);
|
||||
|
||||
static status_t ProcessTransfer(XHCI *ehci,
|
||||
Transfer *transfer);
|
||||
};
|
||||
|
||||
|
||||
|
@ -13,31 +13,71 @@
|
||||
#define XHCI_CAPLENGTH 0x00 // Capability Register Length
|
||||
#define XHCI_HCIVERSION 0x02 // Interface Version Number
|
||||
#define XHCI_HCSPARAMS1 0x04 // Structural Parameters 1
|
||||
// HCSPARAMS1
|
||||
#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff)
|
||||
#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f)
|
||||
#define XHCI_HCSPARAMS2 0x08 // Structural Parameters 2
|
||||
#define XHCI_HCSPARAMS3 0x0C // Structural Parameters 3
|
||||
#define XHCI_HCCPARAMS 0x10 // Capability Parameters
|
||||
#define XHCI_DBOFF 0x14 // Doorbell Register offset
|
||||
#define XHCI_RTSOFF 0x18 // Runtime Register Space offset
|
||||
|
||||
|
||||
// Host Controller Operational Registers
|
||||
#define XHCI_CMD 0x00 // USB Command
|
||||
#define XHCI_STS 0x04 // USB Status
|
||||
|
||||
|
||||
// USB Command Register
|
||||
#define CMD_RUN (1 << 0)
|
||||
#define CMD_HCRST (1 << 1) // Host Controller Reset
|
||||
#define CMD_EIE (1 << 2)
|
||||
#define CMD_HSEIE (1 << 3)
|
||||
|
||||
|
||||
#define XHCI_STS 0x04 // USB Status
|
||||
// USB Status Register
|
||||
#define STS_HCH (1<<0)
|
||||
#define STS_HCH (1 << 0)
|
||||
#define STS_HSE (1 << 2)
|
||||
#define STS_PCD (1 << 4)
|
||||
#define STS_CNR (1<<11)
|
||||
#define STS_HCE (1 << 12)
|
||||
#define XHCI_PAGESIZE 0x08 // PAGE SIZE
|
||||
// Section 5.4.5
|
||||
#define XHCI_CRCR_LO 0x18
|
||||
#define XHCI_CRCR_HI 0x1C
|
||||
#define CRCR_RCS (1<<0)
|
||||
// Section 5.4.6
|
||||
#define XHCI_DCBAAP_LO 0x30
|
||||
#define XHCI_DCBAAP_HI 0x34
|
||||
// Section 5.4.7
|
||||
#define XHCI_CONFIG 0x38
|
||||
|
||||
|
||||
// Host Controller Runtime Registers
|
||||
// Section 5.5.2.1
|
||||
#define XHCI_IMAN(n) (0x0020 + (0x20 * (n)))
|
||||
// IMAN
|
||||
#define IMAN_INTR_ENA 0x00000002
|
||||
// Section 5.5.2.2
|
||||
#define XHCI_IMOD(n) (0x0024 + (0x20 * (n)))
|
||||
// Section 5.5.2.3.1
|
||||
#define XHCI_ERSTSZ(n) (0x0028 + (0x20 * (n)))
|
||||
// ERSTSZ
|
||||
#define XHCI_ERSTS_SET(x) ((x) & 0xFFFF)
|
||||
// Section 5.5.2.3.2
|
||||
#define XHCI_ERSTBA_LO(n) (0x0030 + (0x20 * (n)))
|
||||
#define XHCI_ERSTBA_HI(n) (0x0034 + (0x20 * (n)))
|
||||
// Section 5.5.2.3.3
|
||||
#define XHCI_ERDP_LO(n) (0x0038 + (0x20 * (n)))
|
||||
#define XHCI_ERDP_HI(n) (0x003C + (0x20 * (n)))
|
||||
// Event Handler Busy (EHB)
|
||||
#define ERST_EHB (1 << 3)
|
||||
|
||||
|
||||
// Host Controller Doorbell Registers
|
||||
#define XHCI_DOORBELL(n) (0x0000 + (4 * (n)))
|
||||
|
||||
// Extended Capabilities
|
||||
#define XHCI_LEGSUP_CAPID_MASK 0xff
|
||||
#define XECP_ID(x) ((x) & 0xff)
|
||||
#define HCS0_XECP(x) (((x) >> 16) & 0xffff)
|
||||
#define XECP_NEXT(x) (((x) >> 8) & 0xff)
|
||||
#define XHCI_LEGSUP_CAPID 0x01
|
||||
#define XHCI_LEGSUP_OSOWNED (1 << 24) // OS Owned Semaphore
|
||||
#define XHCI_LEGSUP_BIOSOWNED (1 << 16) // BIOS Owned Semaphore
|
||||
@ -46,5 +86,48 @@
|
||||
#define XHCI_LEGCTLSTS_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17))
|
||||
|
||||
|
||||
#endif // !XHCI_HARDWARE_H
|
||||
// Port status Registers
|
||||
// Section 5.4.8
|
||||
#define XHCI_PORTSC(n) (0x3F0 + (0x10 * (n)))
|
||||
#define PS_CCS (1 << 0)
|
||||
#define PS_PED (1 << 1)
|
||||
#define PS_OCA (1 << 3)
|
||||
#define PS_PR (1 << 4)
|
||||
#define PS_PP (1 << 9)
|
||||
#define PS_SPEED_GET(x) (((x) >> 10) & 0xF)
|
||||
#define PS_LWS (1 << 16)
|
||||
#define PS_CSC (1 << 17)
|
||||
#define PS_PEC (1 << 18)
|
||||
#define PS_WRC (1 << 19)
|
||||
#define PS_OCC (1 << 20)
|
||||
#define PS_PRC (1 << 21)
|
||||
#define PS_PLC (1 << 22)
|
||||
#define PS_CEC (1 << 23)
|
||||
#define PS_CAS (1 << 24)
|
||||
#define PS_WCE (1 << 25)
|
||||
#define PS_WDE (1 << 26)
|
||||
#define PS_WPR (1 << 30)
|
||||
|
||||
#define PS_CLEAR 0x80FF00F7U
|
||||
|
||||
#define PS_PLS_MASK (0xf << 5)
|
||||
#define PS_XDEV_U0 (0x0 << 5)
|
||||
#define PS_XDEV_U3 (0x3 << 5)
|
||||
|
||||
|
||||
// Completion Code
|
||||
#define COMP_CODE_GET(x) (((x) >> 24) & 0xff)
|
||||
#define COMP_SUCCESS 0x01
|
||||
|
||||
|
||||
// TRB Type
|
||||
#define TRB_TYPE(x) ((x) << 10)
|
||||
#define TRB_TYPE_GET(x) (((x) >> 10) & 0x3F)
|
||||
#define TRB_LINK 6
|
||||
#define TRB_TR_NOOP 8
|
||||
#define TRB_TRANSFER 32
|
||||
#define TRB_COMPLETION 33
|
||||
#define TRB_3_CYCLE_BIT (1U << 0)
|
||||
#define TRB_3_TC_BIT (1U << 1)
|
||||
|
||||
#endif // !XHCI_HARDWARE_H
|
||||
|
281
src/add-ons/kernel/busses/usb/xhci_rh.cpp
Normal file
281
src/add-ons/kernel/busses/usb/xhci_rh.cpp
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Copyright 2011, Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Michael Lotz <mmlr@mlotz.ch>
|
||||
* Jian Chiang <j.jian.chiang@gmail.com>
|
||||
*/
|
||||
|
||||
|
||||
#define TRACE_USB
|
||||
#include "xhci.h"
|
||||
|
||||
#define USB_MODULE_NAME "xhci roothub"
|
||||
|
||||
static usb_device_descriptor sXHCIRootHubDevice =
|
||||
{
|
||||
18, // Descriptor length
|
||||
USB_DESCRIPTOR_DEVICE, // Descriptor type
|
||||
0x300, // USB 3.0
|
||||
0x09, // Class (9 = Hub)
|
||||
0, // Subclass
|
||||
3, // Protocol
|
||||
9, // Max packet size on endpoint 0
|
||||
0, // Vendor ID
|
||||
0, // Product ID
|
||||
0x003, // Version
|
||||
1, // Index of manufacturer string
|
||||
2, // Index of product string
|
||||
0, // Index of serial number string
|
||||
1 // Number of configurations
|
||||
};
|
||||
|
||||
|
||||
struct usb_endpoint_ss_comp_descriptor {
|
||||
uint8 length;
|
||||
uint8 descriptor_type;
|
||||
uint16 burst;
|
||||
uint8 attributes;
|
||||
uint16 internal;
|
||||
} _PACKED;
|
||||
|
||||
|
||||
struct xhci_root_hub_configuration_s {
|
||||
usb_configuration_descriptor configuration;
|
||||
usb_interface_descriptor interface;
|
||||
usb_endpoint_descriptor endpoint;
|
||||
usb_endpoint_ss_comp_descriptor endpc;
|
||||
usb_hub_descriptor hub;
|
||||
} _PACKED;
|
||||
|
||||
|
||||
static xhci_root_hub_configuration_s sXHCIRootHubConfig =
|
||||
{
|
||||
{ // configuration descriptor
|
||||
9, // Descriptor length
|
||||
USB_DESCRIPTOR_CONFIGURATION, // Descriptor type
|
||||
sizeof(sXHCIRootHubConfig), // Total length of configuration (including
|
||||
// interface, endpoint and hub descriptors)
|
||||
1, // Number of interfaces
|
||||
1, // Value of this configuration
|
||||
0, // Index of configuration string
|
||||
0x40, // Attributes (0x40 = self powered)
|
||||
0 // Max power (0, since self powered)
|
||||
},
|
||||
|
||||
{ // interface descriptor
|
||||
9, // Descriptor length
|
||||
USB_DESCRIPTOR_INTERFACE, // Descriptor type
|
||||
0, // Interface number
|
||||
0, // Alternate setting
|
||||
1, // Number of endpoints
|
||||
0x09, // Interface class (9 = Hub)
|
||||
0, // Interface subclass
|
||||
0, // Interface protocol
|
||||
0 // Index of interface string
|
||||
},
|
||||
|
||||
{ // endpoint descriptor
|
||||
7, // Descriptor length
|
||||
USB_DESCRIPTOR_ENDPOINT, // Descriptor type
|
||||
USB_REQTYPE_DEVICE_IN | 1, // Endpoint address (first in IN endpoint)
|
||||
0x03, // Attributes (0x03 = interrupt endpoint)
|
||||
2, // Max packet size
|
||||
0xff // Interval
|
||||
},
|
||||
|
||||
{ // endpoint companion descriptor
|
||||
7,
|
||||
0x30,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
|
||||
{ // hub descriptor
|
||||
9, // Descriptor length (including
|
||||
// deprecated power control mask)
|
||||
USB_DESCRIPTOR_HUB, // Descriptor type
|
||||
0x0f, // Number of ports
|
||||
0x0000, // Hub characteristics
|
||||
10, // Power on to power good (in 2ms units)
|
||||
0, // Maximum current (in mA)
|
||||
0x00, // All ports are removable
|
||||
0xff // Deprecated power control mask
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct xhci_root_hub_string_s {
|
||||
uint8 length;
|
||||
uint8 descriptor_type;
|
||||
uint16 unicode_string[12];
|
||||
} _PACKED;
|
||||
|
||||
|
||||
static xhci_root_hub_string_s sXHCIRootHubStrings[3] = {
|
||||
{
|
||||
4, // Descriptor length
|
||||
USB_DESCRIPTOR_STRING, // Descriptor type
|
||||
{
|
||||
0x0409 // Supported language IDs (English US)
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
22, // Descriptor length
|
||||
USB_DESCRIPTOR_STRING, // Descriptor type
|
||||
{
|
||||
'H', 'A', 'I', 'K', 'U', // Characters
|
||||
' ', 'I', 'n', 'c', '.'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
26, // Descriptor length
|
||||
USB_DESCRIPTOR_STRING, // Descriptor type
|
||||
{
|
||||
'X', 'H', 'C', 'I', ' ', // Characters
|
||||
'R', 'o', 'o', 't', 'H',
|
||||
'u', 'b'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
XHCIRootHub::XHCIRootHub(Object *rootObject, int8 deviceAddress)
|
||||
: Hub(rootObject, 0, rootObject->GetStack()->IndexOfBusManager(rootObject->GetBusManager()),
|
||||
sXHCIRootHubDevice, deviceAddress, USB_SPEED_SUPER, true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
XHCIRootHub::ProcessTransfer(XHCI *xhci, Transfer *transfer)
|
||||
{
|
||||
if ((transfer->TransferPipe()->Type() & USB_OBJECT_CONTROL_PIPE) == 0)
|
||||
return B_ERROR;
|
||||
|
||||
usb_request_data *request = transfer->RequestData();
|
||||
TRACE_MODULE("request: %d\n", request->Request);
|
||||
|
||||
status_t status = B_TIMED_OUT;
|
||||
size_t actualLength = 0;
|
||||
switch (request->Request) {
|
||||
case USB_REQUEST_GET_STATUS: {
|
||||
if (request->Index == 0) {
|
||||
// get hub status
|
||||
actualLength = MIN(sizeof(usb_port_status),
|
||||
transfer->DataLength());
|
||||
// the hub reports whether the local power failed (bit 0)
|
||||
// and if there is a over-current condition (bit 1).
|
||||
// everything as 0 means all is ok.
|
||||
memset(transfer->Data(), 0, actualLength);
|
||||
status = B_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
usb_port_status portStatus;
|
||||
if (xhci->GetPortStatus(request->Index - 1, &portStatus) >= B_OK) {
|
||||
actualLength = MIN(sizeof(usb_port_status), transfer->DataLength());
|
||||
memcpy(transfer->Data(), (void *)&portStatus, actualLength);
|
||||
status = B_OK;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case USB_REQUEST_SET_ADDRESS:
|
||||
if (request->Value >= 128) {
|
||||
status = B_TIMED_OUT;
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_MODULE("set address: %d\n", request->Value);
|
||||
status = B_OK;
|
||||
break;
|
||||
|
||||
case USB_REQUEST_GET_DESCRIPTOR:
|
||||
TRACE_MODULE("get descriptor: %d\n", request->Value >> 8);
|
||||
|
||||
switch (request->Value >> 8) {
|
||||
case USB_DESCRIPTOR_DEVICE: {
|
||||
actualLength = MIN(sizeof(usb_device_descriptor),
|
||||
transfer->DataLength());
|
||||
memcpy(transfer->Data(), (void *)&sXHCIRootHubDevice,
|
||||
actualLength);
|
||||
status = B_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
case USB_DESCRIPTOR_CONFIGURATION: {
|
||||
actualLength = MIN(sizeof(xhci_root_hub_configuration_s),
|
||||
transfer->DataLength());
|
||||
sXHCIRootHubConfig.hub.num_ports = xhci->PortCount();
|
||||
memcpy(transfer->Data(), (void *)&sXHCIRootHubConfig,
|
||||
actualLength);
|
||||
status = B_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
case USB_DESCRIPTOR_STRING: {
|
||||
uint8 index = request->Value & 0x00ff;
|
||||
if (index > 2)
|
||||
break;
|
||||
|
||||
actualLength = MIN(sXHCIRootHubStrings[index].length,
|
||||
transfer->DataLength());
|
||||
memcpy(transfer->Data(), (void *)&sXHCIRootHubStrings[index],
|
||||
actualLength);
|
||||
status = B_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
case USB_DESCRIPTOR_HUB: {
|
||||
actualLength = MIN(sizeof(usb_hub_descriptor),
|
||||
transfer->DataLength());
|
||||
sXHCIRootHubConfig.hub.num_ports = xhci->PortCount();
|
||||
memcpy(transfer->Data(), (void *)&sXHCIRootHubConfig.hub,
|
||||
actualLength);
|
||||
status = B_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_REQUEST_SET_CONFIGURATION:
|
||||
status = B_OK;
|
||||
break;
|
||||
|
||||
case USB_REQUEST_CLEAR_FEATURE: {
|
||||
if (request->Index == 0) {
|
||||
// we don't support any hub changes
|
||||
TRACE_MODULE_ERROR("clear feature: no hub changes\n");
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_MODULE("clear feature: %d\n", request->Value);
|
||||
if (xhci->ClearPortFeature(request->Index - 1, request->Value) >= B_OK)
|
||||
status = B_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
case USB_REQUEST_SET_FEATURE: {
|
||||
if (request->Index == 0) {
|
||||
// we don't support any hub changes
|
||||
TRACE_MODULE_ERROR("set feature: no hub changes\n");
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_MODULE("set feature: %d\n", request->Value);
|
||||
if (xhci->SetPortFeature(request->Index - 1, request->Value) >= B_OK)
|
||||
status = B_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
transfer->Finished(status, actualLength);
|
||||
delete transfer;
|
||||
return B_OK;
|
||||
}
|
Loading…
Reference in New Issue
Block a user