diff --git a/src/add-ons/kernel/busses/usb/xhci.cpp b/src/add-ons/kernel/busses/usb/xhci.cpp index 0d5fb986e1..a67c50ea19 100644 --- a/src/add-ons/kernel/busses/usb/xhci.cpp +++ b/src/add-ons/kernel/busses/usb/xhci.cpp @@ -15,6 +15,8 @@ #include #include +#include + #define TRACE_USB #include "xhci.h" @@ -67,7 +69,6 @@ XHCI::XHCI(pci_info *info, Stack *stack) fDcbaArea(-1), fSpinlock(B_SPINLOCK_INITIALIZER), fCmdCompSem(-1), - fCmdCompThread(-1), fFinishTransfersSem(-1), fFinishThread(-1), fStopThreads(false), @@ -75,6 +76,7 @@ XHCI::XHCI(pci_info *info, Stack *stack) fRootHubAddress(0), fPortCount(0), fSlotCount(0), + fScratchpadCount(0), fEventIdx(0), fCmdIdx(0), fEventCcs(1), @@ -99,13 +101,13 @@ XHCI::XHCI(pci_info *info, Stack *stack) // map the registers uint32 offset = fPCIInfo->u.h0.base_registers[0] & (B_PAGE_SIZE - 1); - addr_t physicalAddress = fPCIInfo->u.h0.base_registers[0] - offset; + phys_addr_t physicalAddress = fPCIInfo->u.h0.base_registers[0] - offset; size_t mapSize = (fPCIInfo->u.h0.base_register_sizes[0] + offset + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); - TRACE("map physical memory 0x%08lx (base: 0x%08lx; offset: %lx); size: %ld\n", - fPCIInfo->u.h0.base_registers[0], physicalAddress, offset, - fPCIInfo->u.h0.base_register_sizes[0]); + TRACE("map physical memory 0x%08lx (base: 0x%08" B_PRIxPHYSADDR "; offset:" + " %lx); size: %ld\n", fPCIInfo->u.h0.base_registers[0], + physicalAddress, offset, fPCIInfo->u.h0.base_register_sizes[0]); fRegisterArea = map_physical_memory("XHCI memory mapped registers", physicalAddress, mapSize, B_ANY_KERNEL_BLOCK_ADDRESS, @@ -191,11 +193,6 @@ XHCI::XHCI(pci_info *info, Stack *stack) 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, @@ -218,8 +215,9 @@ XHCI::~XHCI() delete_sem(fFinishTransfersSem); delete_area(fRegisterArea); delete_area(fErstArea); + for (uint32 i = 0; i < fScratchpadCount; i++) + delete_area(fScratchpadArea[i]); delete_area(fDcbaArea); - wait_for_thread(fCmdCompThread, &result); wait_for_thread(fFinishThread, &result); put_module(B_PCI_MODULE_NAME); } @@ -240,29 +238,60 @@ XHCI::Start() // 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); + fPortCount = HCS_MAX_PORTS(capabilities); + if (fPortCount == 0) { + TRACE_ERROR("Invalid number of ports: %u\n", fPortCount); + fPortCount = 0; 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"); + uint32 params2 = ReadCapReg32(XHCI_HCSPARAMS2); + fScratchpadCount = HCS_MAX_SC_BUFFERS(params2); + if (fScratchpadCount > XHCI_MAX_SCRATCHPADS) { + TRACE_ERROR("Invalid number of scratchpads: %u\n", fScratchpadCount); + return B_ERROR; + } + + WriteOpReg(XHCI_DNCTRL, 0); + + // allocate Device Context Base Address array + addr_t dmaAddress; + fDcbaArea = fStack->AllocateArea((void **)&fDcba, (void**)&dmaAddress, + sizeof(*fDcba), "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); + memset(fDcba, 0, sizeof(*fDcba)); + memset(fScratchpadArea, 0, sizeof(fScratchpadArea)); + memset(fScratchpad, 0, sizeof(fScratchpad)); - fErstArea = fStack->AllocateArea((void **)&fErst, &dmaAddress, - (MAX_COMMANDS + MAX_EVENTS) * sizeof(xhci_trb) + // setting the first address to the scratchpad array address + fDcba->baseAddress[0] = dmaAddress + + offsetof(struct xhci_device_context_array, scratchpad); + + // fill up the scratchpad array with scratchpad pages + for (uint32 i = 0; i < fScratchpadCount; i++) { + addr_t scratchDmaAddress; + fScratchpadArea[i] = fStack->AllocateArea((void **)&fScratchpad[i], + (void**)&scratchDmaAddress, B_PAGE_SIZE, "Scratchpad Area"); + if (fScratchpadArea[i] < B_OK) { + TRACE_ERROR("unable to create the scratchpad area\n"); + return B_ERROR; + } + fDcba->scratchpad[i] = scratchDmaAddress; + } + + TRACE("setting DCBAAP %lx\n", dmaAddress); + WriteOpReg(XHCI_DCBAAP_LO, (uint32)dmaAddress); + WriteOpReg(XHCI_DCBAAP_HI, /*(uint32)(dmaAddress >> 32)*/0); + + // allocate Event Ring Segment Table + uint8 *addr; + fErstArea = fStack->AllocateArea((void **)&addr, (void**)&dmaAddress, + (XHCI_MAX_COMMANDS + XHCI_MAX_EVENTS) * sizeof(xhci_trb) + sizeof(xhci_erst_element), "USB XHCI ERST CMD_RING and EVENT_RING Area"); @@ -271,16 +300,18 @@ XHCI::Start() delete_area(fDcbaArea); return B_ERROR; } - memset(fErst, 0, (MAX_COMMANDS + MAX_EVENTS) * sizeof(xhci_trb) + fErst = (xhci_erst_element *)addr; + memset(fErst, 0, (XHCI_MAX_COMMANDS + XHCI_MAX_EVENTS) * sizeof(xhci_trb) + sizeof(xhci_erst_element)); - fErst->rs_addr = (uint32)dmaAddress + sizeof(xhci_erst_element); - fErst->rs_size = MAX_EVENTS; + // fill with Event Ring Segment Base Address and Event Ring Segment Size + fErst->rs_addr = (uint64)(dmaAddress + sizeof(xhci_erst_element)); + fErst->rs_size = XHCI_MAX_EVENTS; fErst->rsvdz = 0; - uint32 addr = (uint32)fErst + sizeof(xhci_erst_element); + addr += sizeof(xhci_erst_element); fEventRing = (xhci_trb *)addr; - addr += MAX_EVENTS * sizeof(xhci_trb); + addr += XHCI_MAX_EVENTS * sizeof(xhci_trb); fCmdRing = (xhci_trb *)addr; TRACE("setting ERST size\n"); @@ -288,27 +319,37 @@ XHCI::Start() 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)); + WriteRunReg32(XHCI_ERDP_HI(0), /*(uint32)(fErst->rs_addr >> 32)*/0); - TRACE("setting ERST base addr = 0x%llx\n", (uint64)dmaAddress); + TRACE("setting ERST base addr = 0x%lx\n", dmaAddress); WriteRunReg32(XHCI_ERSTBA_LO(0), (uint32)dmaAddress); - WriteRunReg32(XHCI_ERSTBA_HI(0), 0); + WriteRunReg32(XHCI_ERSTBA_HI(0), /*(uint32)(dmaAddress >> 32)*/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); + dmaAddress += sizeof(xhci_erst_element) + XHCI_MAX_EVENTS * sizeof(xhci_trb); + TRACE("setting CRCR addr = 0x%lx\n", dmaAddress); + WriteOpReg(XHCI_CRCR_LO, (uint32)dmaAddress | CRCR_RCS); + WriteOpReg(XHCI_CRCR_HI, /*(uint32)(dmaAddress >> 32)*/0); //link trb - fCmdRing[MAX_COMMANDS - 1].qwtrb0 = addr; + fCmdRing[XHCI_MAX_COMMANDS - 1].qwtrb0 = dmaAddress; TRACE("setting interrupt rate\n"); - WriteRunReg32(XHCI_IMOD(0), 160);//4000 irq/s + WriteRunReg32(XHCI_IMOD(0), 160); // 25000 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); + // wait for start up state + int32 tries = 100; + while ((ReadOpReg(XHCI_STS) & STS_HCH) != 0) { + snooze(1000); + if (tries-- < 0) { + TRACE_ERROR("start up timeout\n"); + break; + } + } + fRootHubAddress = AllocateAddress(); fRootHub = new(std::nothrow) XHCIRootHub(RootObject(), fRootHubAddress); if (!fRootHub) { @@ -324,8 +365,10 @@ XHCI::Start() SetRootHub(fRootHub); TRACE_ALWAYS("successfully started the controller\n"); +#ifdef TRACE_USB TRACE("No-Op test\n"); - QueueNoop(); + Noop(); +#endif return BusManager::Start(); } @@ -337,6 +380,11 @@ XHCI::SubmitTransfer(Transfer *transfer) if (transfer->TransferPipe()->DeviceAddress() == fRootHubAddress) return fRootHub->ProcessTransfer(this, transfer); + TRACE("SubmitTransfer()\n"); + Pipe *pipe = transfer->TransferPipe(); + if (pipe->Type() & USB_OBJECT_ISO_PIPE) + return B_UNSUPPORTED; + return B_OK; } @@ -458,7 +506,7 @@ XHCI::GetPortStatus(uint8 index, usb_port_status *status) TRACE("port status=0x%08lx\n", portStatus); // build the status - switch(PS_SPEED_GET(portStatus)) { + switch (PS_SPEED_GET(portStatus)) { case 3: status->status |= PORT_STATUS_HIGH_SPEED; break; @@ -502,32 +550,30 @@ XHCI::SetPortFeature(uint8 index, uint16 feature) return B_BAD_INDEX; uint32 portRegister = XHCI_PORTSC(index); - uint32 portStatus = ReadOpReg(portRegister); + uint32 portStatus = ReadOpReg(portRegister) & ~PS_CLEAR; switch (feature) { case PORT_SUSPEND: - if ((portStatus & PS_PED ) == 0 || (portStatus & PS_PR) + 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; + WriteOpReg(portRegister, portStatus | PS_LWS | PS_XDEV_U3); + break; case PORT_RESET: - portStatus &= ~PS_CLEAR; WriteOpReg(portRegister, portStatus | PS_PR); - return B_OK; + break; case PORT_POWER: - portStatus &= ~PS_CLEAR; WriteOpReg(portRegister, portStatus | PS_PP); - return B_OK; + break; + default: + return B_BAD_VALUE; } - return B_BAD_VALUE; + return B_OK; } @@ -539,8 +585,7 @@ XHCI::ClearPortFeature(uint8 index, uint16 feature) return B_BAD_INDEX; uint32 portRegister = XHCI_PORTSC(index); - uint32 portStatus = ReadOpReg(portRegister); - portStatus &= ~PS_CLEAR; + uint32 portStatus = ReadOpReg(portRegister) & ~PS_CLEAR; switch (feature) { case PORT_SUSPEND: @@ -550,7 +595,6 @@ XHCI::ClearPortFeature(uint8 index, uint16 feature) 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); } @@ -598,20 +642,26 @@ XHCI::ControllerHalt() status_t XHCI::ControllerReset() { - WriteOpReg(XHCI_CMD, CMD_HCRST); + TRACE("ControllerReset() cmd: 0x%lx sts: 0x%lx\n", ReadOpReg(XHCI_CMD), + ReadOpReg(XHCI_STS)); + WriteOpReg(XHCI_CMD, ReadOpReg(XHCI_CMD) | CMD_HCRST); - int32 tries = 100; + int32 tries = 250; while (ReadOpReg(XHCI_CMD) & CMD_HCRST) { snooze(1000); - if (tries-- < 0) + if (tries-- < 0) { + TRACE("ControllerReset() failed CMD_HCRST\n"); return B_ERROR; + } } - tries = 100; + tries = 250; while (ReadOpReg(XHCI_STS) & STS_CNR) { snooze(1000); - if (tries-- < 0) + if (tries-- < 0) { + TRACE("ControllerReset() failed STS_CNR\n"); return B_ERROR; + } } return B_OK; @@ -628,7 +678,7 @@ XHCI::InterruptHandler(void *data) int32 XHCI::Interrupt() { - acquire_spinlock(&fSpinlock); + SpinLocker _(&fSpinlock); uint32 status = ReadOpReg(XHCI_STS); uint32 temp = ReadRunReg32(XHCI_IMAN(0)); @@ -637,7 +687,11 @@ XHCI::Interrupt() TRACE("STS: %lx IRQ_PENDING: %lx\n", status, temp); int32 result = B_HANDLED_INTERRUPT; - + + if (status & STS_HCH) { + TRACE_ERROR("Host Controller halted\n"); + return result; + } if (status & STS_HSE) { TRACE_ERROR("Host System Error\n"); return result; @@ -649,19 +703,19 @@ XHCI::Interrupt() 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); + uint8 event = TRB_3_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: + case TRB_TYPE_COMMAND_COMPLETION: HandleCmdComplete(&fEventRing[i]); result = B_INVOKE_SCHEDULER; break; @@ -671,7 +725,7 @@ XHCI::Interrupt() } i++; - if (i == MAX_EVENTS) { + if (i == XHCI_MAX_EVENTS) { i = 0; j ^= 1; if (!--t) @@ -687,9 +741,6 @@ XHCI::Interrupt() WriteRunReg32(XHCI_ERDP_LO(0), (uint32)addr); WriteRunReg32(XHCI_ERDP_HI(0), (uint32)(addr >> 32)); - - release_spinlock(&fSpinlock); - return result; } @@ -714,7 +765,7 @@ XHCI::QueueCommand(xhci_trb *trb) j = fCmdCcs; TRACE("command[%u] = %lx (0x%016llx, 0x%08lx, 0x%08lx)\n", - i, TRB_TYPE_GET(trb->dwtrb3), + i, TRB_3_TYPE_GET(trb->dwtrb3), trb->qwtrb0, trb->dwtrb2, trb->dwtrb3); fCmdRing[i].qwtrb0 = trb->qwtrb0; @@ -728,15 +779,14 @@ XHCI::QueueCommand(xhci_trb *trb) temp &= ~TRB_3_TC_BIT; fCmdRing[i].dwtrb3 = temp; - fCmdAddr = fErst->rs_addr + (MAX_EVENTS + i) * sizeof(xhci_trb); + fCmdAddr = fErst->rs_addr + (XHCI_MAX_EVENTS + i) * sizeof(xhci_trb); i++; - if (i == (MAX_COMMANDS - 1)) { + if (i == (XHCI_MAX_COMMANDS - 1)) { + temp = TRB_3_TYPE(TRB_TYPE_LINK) | TRB_3_TC_BIT; if (j) - temp = TRB_3_CYCLE_BIT | TRB_TYPE(TRB_LINK); - else - temp = TRB_TYPE(TRB_LINK); + temp |= TRB_3_CYCLE_BIT; fCmdRing[i].dwtrb3 = temp; i = 0; @@ -761,54 +811,176 @@ XHCI::HandleCmdComplete(xhci_trb *trb) } -void -XHCI::QueueNoop() +status_t +XHCI::DoCommand(xhci_trb *trb) { - xhci_trb trb; - uint32 temp; + if (!Lock()) + return B_ERROR; - 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); + QueueCommand(trb); Ring(); - release_spinlock(&fSpinlock); - restore_interrupts(state); + + if (acquire_sem(fCmdCompSem) < B_OK) { + Unlock(); + return B_ERROR; + } + // 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); + + status_t status = B_OK; + TRACE("Command Complete\n"); + if (TRB_2_COMP_CODE_GET(fCmdResult[0]) != COMP_SUCCESS) { + TRACE_ERROR("unsuccessful no-op command %ld\n", + TRB_2_COMP_CODE_GET(fCmdResult[0])); + status = B_IO_ERROR; + } + + trb->dwtrb2 = fCmdResult[0]; + trb->dwtrb3 = fCmdResult[1]; + + Unlock(); + return status; } -int32 -XHCI::CmdCompThread(void *data) +status_t +XHCI::Noop() { - ((XHCI *)data)->CmdComplete(); + xhci_trb trb; + trb.qwtrb0 = 0; + trb.dwtrb2 = 0; + trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_CMD_NOOP); + + return DoCommand(&trb); +} + + +status_t +XHCI::EnableSlot(uint8 *slot) +{ + xhci_trb trb; + trb.qwtrb0 = 0; + trb.dwtrb2 = 0; + trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_ENABLE_SLOT); + + status_t status = DoCommand(&trb); + if (status != B_OK) + return status; + + *slot = TRB_3_SLOT_GET(trb.dwtrb3); return B_OK; } -void -XHCI::CmdComplete() +status_t +XHCI::DisableSlot(uint8 slot) { - while (!fStopThreads) { - if (acquire_sem(fCmdCompSem) < B_OK) - continue; + xhci_trb trb; + trb.qwtrb0 = 0; + trb.dwtrb2 = 0; + trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_DISABLE_SLOT) | TRB_3_SLOT(slot); - // 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); + return DoCommand(&trb); +} - TRACE("Command Complete\n"); - if (COMP_CODE_GET(fCmdResult[0]) != COMP_SUCCESS) { - TRACE_ERROR("unsuccessful no-op command\n"); - //continue; - } - snooze(1000000 * 5); - QueueNoop(); - } + +status_t +XHCI::SetAddress(uint64 inputContext, bool bsr, uint8 slot) +{ + xhci_trb trb; + trb.qwtrb0 = inputContext; + trb.dwtrb2 = 0; + trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_ADDRESS_DEVICE) | TRB_3_SLOT(slot); + + if (bsr) + trb.dwtrb3 |= TRB_3_BSR_BIT; + + return DoCommand(&trb); +} + + +status_t +XHCI::ConfigureEndpoint(uint64 inputContext, bool deconfigure, uint8 slot) +{ + xhci_trb trb; + trb.qwtrb0 = inputContext; + trb.dwtrb2 = 0; + trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_CONFIGURE_ENDPOINT) | TRB_3_SLOT(slot); + + if (deconfigure) + trb.dwtrb3 |= TRB_3_DCEP_BIT; + + return DoCommand(&trb); +} + + +status_t +XHCI::EvaluateContext(uint64 inputContext, uint8 slot) +{ + xhci_trb trb; + trb.qwtrb0 = inputContext; + trb.dwtrb2 = 0; + trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_EVALUATE_CONTEXT) | TRB_3_SLOT(slot); + + return DoCommand(&trb); +} + + +status_t +XHCI::ResetEndpoint(bool preserve, uint8 endpoint, uint8 slot) +{ + xhci_trb trb; + trb.qwtrb0 = 0; + trb.dwtrb2 = 0; + trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_RESET_ENDPOINT) | TRB_3_SLOT(slot) + | TRB_3_ENDPOINT(endpoint); + if (preserve) + trb.dwtrb3 |= TRB_3_PRSV_BIT; + + return DoCommand(&trb); +} + + +status_t +XHCI::StopEndpoint(bool suspend, uint8 endpoint, uint8 slot) +{ + xhci_trb trb; + trb.qwtrb0 = 0; + trb.dwtrb2 = 0; + trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_STOP_ENDPOINT) | TRB_3_SLOT(slot) + | TRB_3_ENDPOINT(endpoint); + if (suspend) + trb.dwtrb3 |= TRB_3_SUSPEND_ENDPOINT_BIT; + + return DoCommand(&trb); +} + + +status_t +XHCI::SetTRDequeue(uint64 dequeue, uint16 stream, uint8 endpoint, uint8 slot) +{ + xhci_trb trb; + trb.qwtrb0 = dequeue; + trb.dwtrb2 = TRB_2_STREAM(stream); + trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_SET_TR_DEQUEUE) | TRB_3_SLOT(slot) + | TRB_3_ENDPOINT(endpoint); + + return DoCommand(&trb); +} + + +status_t +XHCI::ResetDevice(uint8 slot) +{ + xhci_trb trb; + trb.qwtrb0 = 0; + trb.dwtrb2 = 0; + trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_RESET_DEVICE) | TRB_3_SLOT(slot); + + return DoCommand(&trb); } diff --git a/src/add-ons/kernel/busses/usb/xhci.h b/src/add-ons/kernel/busses/usb/xhci.h index 1bc99ab79e..f35e8dbb6a 100644 --- a/src/add-ons/kernel/busses/usb/xhci.h +++ b/src/add-ons/kernel/busses/usb/xhci.h @@ -14,45 +14,11 @@ #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 { public: XHCI(pci_info *info, Stack *stack); @@ -68,12 +34,12 @@ public: static status_t AddTo(Stack *stack); // Port operations for root hub - uint8 PortCount() { return fPortCount; }; + uint8 PortCount() const { 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); - virtual const char * TypeName() const { return "xhci"; }; + virtual const char * TypeName() const { return "xhci"; } private: // Controller resets @@ -91,14 +57,27 @@ private: // Command void QueueCommand(xhci_trb *trb); void HandleCmdComplete(xhci_trb *trb); - + status_t DoCommand(xhci_trb *trb); //Doorbell void Ring(); - //no-op - void QueueNoop(); - static int32 CmdCompThread(void *data); - void CmdComplete(); + // Commands + status_t Noop(); + status_t EnableSlot(uint8 *slot); + status_t DisableSlot(uint8 slot); + status_t SetAddress(uint64 inputContext, bool bsr, + uint8 slot); + status_t ConfigureEndpoint(uint64 inputContext, + bool deconfigure, uint8 slot); + status_t EvaluateContext(uint64 inputContext, + uint8 slot); + status_t ResetEndpoint(bool preserve, uint8 endpoint, + uint8 slot); + status_t StopEndpoint(bool suspend, uint8 endpoint, + uint8 slot); + status_t SetTRDequeue(uint64 dequeue, uint16 stream, + uint8 endpoint, uint8 slot); + status_t ResetDevice(uint8 slot); // Operational register functions inline void WriteOpReg(uint32 reg, uint32 value); @@ -136,12 +115,11 @@ private: uint32 fCmdResult[2]; area_id fDcbaArea; - uint8 * fDcba; + struct xhci_device_context_array * fDcba; spinlock fSpinlock; sem_id fCmdCompSem; - thread_id fCmdCompThread; sem_id fFinishTransfersSem; thread_id fFinishThread; bool fStopThreads; @@ -154,6 +132,11 @@ private: uint8 fPortCount; uint8 fSlotCount; + // Scratchpad + uint8 fScratchpadCount; + area_id fScratchpadArea[XHCI_MAX_SCRATCHPADS]; + void * fScratchpad[XHCI_MAX_SCRATCHPADS]; + uint16 fEventIdx; uint16 fCmdIdx; uint8 fEventCcs; diff --git a/src/add-ons/kernel/busses/usb/xhci_hardware.h b/src/add-ons/kernel/busses/usb/xhci_hardware.h index ea4238c54d..2838981591 100644 --- a/src/add-ons/kernel/busses/usb/xhci_hardware.h +++ b/src/add-ons/kernel/busses/usb/xhci_hardware.h @@ -14,10 +14,16 @@ #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 HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff) +#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f) #define XHCI_HCSPARAMS2 0x08 // Structural Parameters 2 +#define HCS_IST(p) (((p) >> 0) & 0xf) +#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) +#define HCS_SPR(p) (((p) >> 26) & 0x1) +#define HCS_MAX_SC_BUFFERS(p) (((p) >> 27) & 0x1f) #define XHCI_HCSPARAMS3 0x0C // Structural Parameters 3 +#define HCS_U1_DEVICE_LATENCY(p) (((p) >> 0) & 0xff) +#define HCS_U2_DEVICE_LATENCY(p) (((p) >> 16) & 0xffff) #define XHCI_HCCPARAMS 0x10 // Capability Parameters #define XHCI_DBOFF 0x14 // Doorbell Register offset #define XHCI_RTSOFF 0x18 // Runtime Register Space offset @@ -39,6 +45,7 @@ #define STS_CNR (1<<11) #define STS_HCE (1 << 12) #define XHCI_PAGESIZE 0x08 // PAGE SIZE +#define XHCI_DNCTRL 0x14 // Section 5.4.5 #define XHCI_CRCR_LO 0x18 #define XHCI_CRCR_HI 0x1C @@ -72,7 +79,7 @@ // Host Controller Doorbell Registers -#define XHCI_DOORBELL(n) (0x0000 + (4 * (n))) +#define XHCI_DOORBELL(n) (0x0000 + (4 * (n))) // Extended Capabilities #define XECP_ID(x) ((x) & 0xff) @@ -88,7 +95,7 @@ // Port status Registers // Section 5.4.8 -#define XHCI_PORTSC(n) (0x3F0 + (0x10 * (n))) +#define XHCI_PORTSC(n) (0x400 + (0x10 * (n))) #define PS_CCS (1 << 0) #define PS_PED (1 << 1) #define PS_OCA (1 << 3) @@ -116,18 +123,167 @@ // Completion Code -#define COMP_CODE_GET(x) (((x) >> 24) & 0xff) -#define COMP_SUCCESS 0x01 +#define TRB_2_COMP_CODE_GET(x) (((x) >> 24) & 0xff) +#define COMP_INVALID 0 +#define COMP_SUCCESS 1 +#define COMP_DATA_BUFFER 2 +#define COMP_BABBLE 3 +#define COMP_USB_TRANSACTION 4 +#define COMP_TRB 5 +#define COMP_STALL 6 +#define COMP_RESOURCE 7 +#define COMP_BANDWIDTH 8 +#define COMP_NO_SLOTS 9 +#define COMP_INVALID_STREAM 10 +#define COMP_SLOT_NOT_ENABLED 11 +#define COMP_ENDPOINT_NOT_ENABLED 12 +#define COMP_SHORT_PACKET 13 +#define COMP_RING_UNDERRUN 14 +#define COMP_RING_OVERRUN 15 +#define COMP_VF_RING_FULL 16 +#define COMP_PARAMETER 17 +#define COMP_BANDWIDTH_OVERRUN 18 +#define COMP_CONTEXT_STATE 19 +#define COMP_NO_PING_RESPONSE 20 +#define COMP_EVENT_RING_FULL 21 +#define COMP_INCOMPATIBLE_DEVICE 22 +#define COMP_MISSED_SERVICE 23 +#define COMP_COMMAND_RING_STOPPED 24 +#define COMP_COMMAND_ABORTED 25 +#define COMP_STOPPED 26 +#define COMP_LENGTH_INVALID 27 +#define COMP_MAX_EXIT_LATENCY 29 +#define COMP_ISOC_OVERRUN 31 +#define COMP_EVENT_LOST 32 +#define COMP_UNDEFINED 33 +#define COMP_INVALID_STREAM_ID 34 +#define COMP_SECONDARY_BANDWIDTH 35 +#define COMP_SPLIT_TRANSACTION 36 +#define TRB_2_TD_SIZE(x) (((x) & 0x1F) << 17) +#define TRB_2_TD_SIZE_GET(x) (((x) >> 17) & 0x1F) +#define TRB_2_REM(x) ((x) & 0xFFFFFF) +#define TRB_2_REM_GET(x) ((x) & 0xFFFFFF) +#define TRB_2_BYTES(x) ((x) & 0x1FFFF) +#define TRB_2_BYTES_GET(x) ((x) & 0x1FFFF) +#define TRB_2_IRQ(x) (((x) & 0x3FF) << 22) +#define TRB_2_IRQ_GET(x) (((x) >> 22) & 0x3FF) +#define TRB_2_STREAM(x) (((x) & 0xFF) << 16) +#define TRB_2_STREAM_GET(x) (((x) >> 16) & 0xFF) + +#define TRB_3_TYPE(x) (((x) & 0x3F) << 10) +#define TRB_3_TYPE_GET(x) (((x) >> 10) & 0x3F) +// TRB Type (table 131) +#define TRB_TYPE_NORMAL 1 +#define TRB_TYPE_SETUP_STAGE 2 +#define TRB_TYPE_DATA_STAGE 3 +#define TRB_TYPE_STATUS_STAGE 4 +#define TRB_TYPE_ISOCH 5 +#define TRB_TYPE_LINK 6 +#define TRB_TYPE_EVENT_DATA 7 +#define TRB_TYPE_TR_NOOP 8 +// commands +#define TRB_TYPE_ENABLE_SLOT 9 +#define TRB_TYPE_DISABLE_SLOT 10 +#define TRB_TYPE_ADDRESS_DEVICE 11 +#define TRB_TYPE_CONFIGURE_ENDPOINT 12 +#define TRB_TYPE_EVALUATE_CONTEXT 13 +#define TRB_TYPE_RESET_ENDPOINT 14 +#define TRB_TYPE_STOP_ENDPOINT 15 +#define TRB_TYPE_SET_TR_DEQUEUE 16 +#define TRB_TYPE_RESET_DEVICE 17 +#define TRB_TYPE_FORCE_EVENT 18 +#define TRB_TYPE_NEGOCIATE_BW 19 +#define TRB_TYPE_SET_LATENCY_TOLERANCE 20 +#define TRB_TYPE_GET_PORT_BW 21 +#define TRB_TYPE_FORCE_HEADER 22 +#define TRB_TYPE_CMD_NOOP 23 +// events +#define TRB_TYPE_TRANSFER 32 +#define TRB_TYPE_COMMAND_COMPLETION 33 +#define TRB_TYPE_PORT_STATUS_CHANGE 34 +#define TRB_TYPE_BANDWIDTH_REQUEST 35 +#define TRB_TYPE_DOORBELL 36 +#define TRB_TYPE_HOST_CONTROLLER 37 +#define TRB_TYPE_DEVICE_NOTIFICATION 38 +#define TRB_TYPE_MFINDEX_WRAP 39 +// vendor +#define TRB_TYPE_NEC_COMMAND_COMPLETION 48 +#define TRB_TYPE_NEC_GET_FIRMWARE_REV 49 -// 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) +#define TRB_3_ENT_BIT (1U << 1) +#define TRB_3_ISP_BIT (1U << 2) +#define TRB_3_NSNOOP_BIT (1U << 3) +#define TRB_3_CHAIN_BIT (1U << 4) +#define TRB_3_IOC_BIT (1U << 5) +#define TRB_3_IDT_BIT (1U << 6) +#define TRB_3_BEI_BIT (1U << 9) +#define TRB_3_DCEP_BIT (1U << 9) +#define TRB_3_PRSV_BIT (1U << 9) +#define TRB_3_BSR_BIT (1U << 9) +#define TRB_3_TRT_MASK (3U << 16) +#define TRB_3_DIR_IN (1U << 16) +#define TRB_3_TRT_OUT (2U << 16) +#define TRB_3_TRT_IN (3U << 16) +#define TRB_3_SUSPEND_ENDPOINT_BIT (1U << 23) +#define TRB_3_ISO_SIA_BIT (1U << 31) + +#define TRB_3_TBC(x) (((x) & 0x3) << 7) +#define TRB_3_TBC_GET(x) (((x) >> 7) & 0x3) +#define TRB_3_TLBPC(x) (((x) & 0xf) << 16) +#define TRB_3_TLBPC_GET(x) (((x) >> 16) & 0xf) +#define TRB_3_ENDPOINT(x) (((x) & 0xf) << 16) +#define TRB_3_ENDPOINT_GET(x) (((x) >> 16) & 0xf) +#define TRB_3_FRID(x) (((x) & 0x7ff) << 20) +#define TRB_3_FRID_GET(x) (((x) >> 20) & 0x7ff) +#define TRB_3_SLOT(x) (((x) & 0xff) << 24) +#define TRB_3_SLOT_GET(x) (((x) >> 24) & 0xff) + + +#define XHCI_MAX_EVENTS (16 * 13) +#define XHCI_MAX_COMMANDS (16 * 1) +#define XHCI_MAX_SLOTS 256 +#define XHCI_MAX_PORTS 127 +#define XHCI_MAX_SCRATCHPADS 32 + + +struct xhci_trb { + uint64 qwtrb0; + uint32 dwtrb2; + uint32 dwtrb3; +} __attribute__((__aligned__(4)));; + + +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))); + + +struct xhci_device_context_array { + uint64 baseAddress[XHCI_MAX_SLOTS]; + struct { + uint64 padding; + } __attribute__((__aligned__(64))); + uint64 scratchpad[XHCI_MAX_SCRATCHPADS]; +}; + #endif // !XHCI_HARDWARE_H