23 #include "modules/system/usb/Usb.h" 24 #include "pedigree/kernel/LockGuard.h" 25 #include "pedigree/kernel/Log.h" 26 #include "pedigree/kernel/machine/Device.h" 27 #include "pedigree/kernel/machine/IrqManager.h" 28 #include "pedigree/kernel/machine/Machine.h" 29 #include "pedigree/kernel/machine/Pci.h" 30 #include "pedigree/kernel/machine/Timer.h" 31 #include "pedigree/kernel/process/Thread.h" 32 #include "pedigree/kernel/processor/IoBase.h" 33 #include "pedigree/kernel/processor/PhysicalMemoryManager.h" 34 #include "pedigree/kernel/processor/Processor.h" 35 #include "pedigree/kernel/processor/ProcessorInformation.h" 36 #include "pedigree/kernel/processor/VirtualAddressSpace.h" 37 #include "pedigree/kernel/time/Time.h" 38 #include "pedigree/kernel/utilities/Iterator.h" 39 #include "pedigree/kernel/utilities/Vector.h" 40 #include "pedigree/kernel/utilities/utility.h" 42 #define INDEX_FROM_TD_VIRT(ptr) \ 43 (((reinterpret_cast<uintptr_t>((ptr)) - \ 44 reinterpret_cast<uintptr_t>(m_pTDList)) / \ 46 #define INDEX_FROM_TD_PHYS(ptr) ((((ptr) -m_pTDListPhys) / sizeof(TD))) 47 #define PHYS_TD(idx) (m_pTDListPhys + ((idx) * sizeof(TD))) 49 #define QH_REGION_SIZE 0x4000 50 #define TD_REGION_SIZE 0x8000 52 #define TOTAL_MEM_PAGES \ 53 ((QH_REGION_SIZE / 0x1000) + (TD_REGION_SIZE / 0x1000) + 1) 55 static int threadStub(
void *p)
57 Uhci *pUhci =
reinterpret_cast<Uhci *
>(p);
63 m_AsyncQueueListChangeLock(), m_UhciMR(
"Uhci-MR"),
64 m_pCurrentAsyncQueueTail(0), m_pCurrentAsyncQueueHead(0),
65 m_AsyncSchedule(), m_DequeueList(), m_DequeueCount(0),
68 setSpecificType(
String(
"UHCI"));
75 m_pBase = m_Addresses[0]->m_Io;
76 m_Addresses[0]->map();
83 ERROR(
"USB: UHCI: Couldn't allocate memory region!");
88 reinterpret_cast<uintptr_t
>(m_UhciMR.virtualAddress());
89 physical_uintptr_t pPhysBase = m_UhciMR.physicalAddress();
91 m_pFrameList =
reinterpret_cast<uint32_t *
>(pVirtBase);
92 m_pQHList =
reinterpret_cast<QH *
>(pVirtBase + 0x1000);
93 m_pTDList =
reinterpret_cast<TD *
>(pVirtBase + 0x1000 + QH_REGION_SIZE);
95 m_pFrameListPhys = pPhysBase;
96 m_pQHListPhys = pPhysBase + 0x1000;
97 m_pTDListPhys = m_pQHListPhys + QH_REGION_SIZE;
101 QH *pDummyQH = &m_pQHList[0];
104 DoubleWordSet(m_pFrameList, m_pQHListPhys | 2, 0x400);
106 ByteSet(pDummyQH, 0,
sizeof(QH));
108 pDummyQH->pNext = m_pQHListPhys >> 4;
109 pDummyQH->bNextQH = 1;
110 pDummyQH->bElemInvalid = 1;
112 pDummyQH->pMetaData =
new QH::MetaData;
113 pDummyQH->pMetaData->pPrev = pDummyQH->pMetaData->pNext = pDummyQH;
115 m_pCurrentAsyncQueueTail = m_pCurrentAsyncQueueHead = pDummyQH;
120 reinterpret_cast<void *>(
this));
125 Machine::instance().getIrqManager()->
control(
126 getInterruptNumber(), IrqManager::MitigationThreshold,
134 #ifdef USB_VERBOSE_DEBUG 135 DEBUG_LOG(
"USB: UHCI: Pci command+status: " << nCommand);
144 m_pBase->write16(m_pBase->read16(UHCI_CMD) & ~0x41, UHCI_CMD);
145 while (!(m_pBase->read16(UHCI_STS) & UHCI_STS_HALT))
146 Time::delay(10 * Time::Multiplier::Millisecond);
147 m_pBase->write16(m_pBase->read16(UHCI_STS), UHCI_STS);
150 m_pBase->write16(m_pBase->read16(UHCI_CMD) | UHCI_CMD_HCRES, UHCI_CMD);
151 while (m_pBase->read16(UHCI_CMD) & UHCI_CMD_HCRES)
152 Time::delay(5 * Time::Multiplier::Millisecond);
155 m_pBase->write32(m_pFrameListPhys, UHCI_FRLP);
158 m_pBase->write16(0xf, UHCI_INTR);
165 m_pBase->write16(0xC1 | 0x10, UHCI_CMD);
166 Time::delay(10 * Time::Multiplier::Millisecond);
167 m_pBase->write16(0xC1, UHCI_CMD);
168 while (m_pBase->read16(UHCI_STS) & UHCI_STS_HALT)
169 Time::delay(10 * Time::Multiplier::Millisecond);
171 #ifdef USB_VERBOSE_DEBUG 176 Time::delay(100 * Time::Multiplier::Millisecond);
178 for (
size_t i = 0; i < 8; i++)
180 uint16_t nPortStatus = m_pBase->read16(UHCI_PORTSC + (i * 2));
181 NOTICE(
"Port status is " << nPortStatus);
182 if ((!(nPortStatus & 0x80)) && (m_nPorts >= 2))
196 Machine::instance().
getTimer()->registerHandler(
this);
203 void Uhci::doDequeue()
207 m_DequeueCount.acquire();
212 pQH = m_DequeueList.popFront();
219 if (pQH->pMetaData->completedTdList.count())
222 pQH->pMetaData->completedTdList.
begin();
223 it != pQH->pMetaData->completedTdList.end(); it++)
225 size_t idx = (*it)->id;
226 ByteSet(*it, 0,
sizeof(TD));
228 m_TDBitmap.clear(idx);
234 if (pQH->pMetaData->tdList.count())
237 it != pQH->pMetaData->tdList.end(); it++)
239 size_t idx = (*it)->id;
240 ByteSet(*it, 0,
sizeof(TD));
242 m_TDBitmap.clear(idx);
247 size_t id = pQH->pMetaData->id;
250 delete pQH->pMetaData;
251 ByteSet(pQH, 0,
sizeof(QH));
253 m_QHBitmap.clear(
id);
255 #ifdef USB_VERBOSE_DEBUG 263 uint16_t nStatus = m_pBase->read16(UHCI_STS);
270 m_pBase->write16(nStatus, UHCI_STS);
272 #ifdef USB_VERBOSE_DEBUG 281 if (nStatus & (UHCI_STS_INT | UHCI_STS_ERR))
288 if (m_AsyncSchedule.count())
289 pQH = m_AsyncSchedule.popFront();
295 bool bPeriodic = pQH->pMetaData->bPeriodic;
299 while (pQH->pMetaData->tdList.
count())
301 pTD = pQH->pMetaData->tdList.
popFront();
305 if (pTD->bShortTransferTD)
308 bool bEndOfTransfer =
false;
309 if (pTD->nStatus == 0x80)
316 if (((pTD->nErr == 0) && (pTD->nStatus & 0x7e)) ||
317 (nStatus & UHCI_STS_ERR))
321 ((nStatus & UHCI_STS_ERR) ?
"USB" :
"TD")
324 "TD Status: " << pTD->nStatus <<
" [" << pTD->nErr
325 <<
"], USB status: " << nStatus);
327 nResult = -pTD->getError();
331 nResult = (pTD->nActLen + 1) % 0x800;
332 pQH->pMetaData->nTotalBytes += nResult;
334 #ifdef USB_VERBOSE_DEBUG 337 <<
Dec << pTD->id <<
" [QH #" << pQH->pMetaData->
id 338 <<
Hex <<
"] DONE: " <<
Dec << pTD->nAddress <<
":" 339 << pTD->nEndpoint <<
" " 340 << (pTD->nPid == UsbPidOut ?
342 (pTD->nPid == UsbPidIn ?
344 (pTD->nPid == UsbPidSetup ?
"SETUP" :
"")))
345 <<
" " << nResult <<
Hex);
351 ((nResult < 0) || (pTD == pQH->pMetaData->pLastTD))) ||
352 (bPeriodic && (nResult >= 0));
355 if ((pTD != pQH->pMetaData->pLastTD) && !bEndOfTransfer)
360 if ((nResult >= 0) && (pTD->nPid == UsbPidIn))
368 if (nResult < pTD->nBufferSize)
371 "UHCI: Short read - got " 372 << nResult <<
" bytes, wanted " 373 << pTD->nBufferSize <<
" bytes");
374 pTD->bShortTransferTD =
true;
375 bEndOfTransfer =
true;
382 pQH->pMetaData->completedTdList.
pushBack(pTD);
389 if (pQH->pMetaData->pCallback)
391 pQH->pMetaData->pCallback(
392 pQH->pMetaData->pParam,
393 nResult < 0 ? nResult :
394 pQH->pMetaData->nTotalBytes);
407 m_AsyncQueueListChangeLock
415 QH *pPrev = pQH->pMetaData->pPrev;
416 QH *pNext = pQH->pMetaData->pNext;
419 pPrev->pMetaData->pNext = pNext;
420 pNext->pMetaData->pPrev = pPrev;
423 pPrev->pNext = pQH->pNext;
425 pPrev->bNextInvalid = 0;
428 if (pQH == m_pCurrentAsyncQueueTail)
429 m_pCurrentAsyncQueueTail = pPrev;
432 m_DequeueList.pushBack(pQH);
437 m_AsyncQueueListChangeLock.release();
439 m_DequeueCount.release();
445 pQH->pMetaData->bIgnore =
true;
450 pTD->bDataToggle = !pTD->bDataToggle;
454 pQH->pMetaData->nTotalBytes = 0;
461 pQH->pMetaData->bIgnore =
false;
466 pQH->pElem = PHYS_TD(pTD->id) >> 4;
467 pQH->bElemInvalid = 0;
470 pQH->pMetaData->tdList.
pushBack(pTD);
476 if (!pQH->pMetaData->bIgnore)
482 if (persistList.
count())
486 it != persistList.
end();)
488 m_AsyncSchedule.pushBack(*it);
489 it = persistList.
erase(it);
497 uintptr_t pTransaction,
bool bToggle, UsbPid pid, uintptr_t pBuffer,
504 nIndex = m_TDBitmap.getFirstClear();
505 if (nIndex >= (TD_REGION_SIZE /
sizeof(
TD)))
507 ERROR(
"USB: UHCI: TD space full");
510 m_TDBitmap.set(nIndex);
514 TD *pTD = &m_pTDList[nIndex];
515 ByteSet(pTD, 0,
sizeof(
TD));
521 QH *pQH = &m_pQHList[pTransaction];
533 pTD->bLoSpeed = pQH->pMetaData->endpointInfo.speed == LowSpeed;
539 pTD->nAddress = pQH->pMetaData->endpointInfo.nAddress;
540 pTD->nEndpoint = pQH->pMetaData->endpointInfo.nEndpoint;
541 pTD->bDataToggle = bToggle;
544 pTD->nMaxLen = nBytes ? nBytes - 1 : 0x7ff;
545 pTD->nBufferSize = nBytes;
550 if (va.
isMapped(reinterpret_cast<void *>(pBuffer)))
552 physical_uintptr_t phys = 0;
554 va.
getMapping(reinterpret_cast<void *>(pBuffer), phys, flags);
555 pTD->pBuff = phys + (pBuffer & 0xFFF);
560 "UHCI: addTransferToTransaction: Buffer (page " 561 <<
Dec << pBuffer <<
Hex <<
") isn't mapped!");
562 m_TDBitmap.clear(nIndex);
568 if (pQH->pMetaData->pLastTD)
570 pQH->pMetaData->pLastTD->pNext = PHYS_TD(nIndex) >> 4;
571 pQH->pMetaData->pLastTD->bNextInvalid = 0;
572 pQH->pMetaData->pLastTD->bNextQH = 0;
577 pQH->pMetaData->pFirstTD = pTD;
578 pQH->pElem = PHYS_TD(nIndex) >> 4;
579 pQH->bElemInvalid = 0;
582 pQH->pMetaData->pLastTD = pTD;
584 pQH->pMetaData->tdList.
pushBack(pTD);
593 nIndex = m_QHBitmap.getFirstClear();
594 if (nIndex >= (QH_REGION_SIZE /
sizeof(
QH)))
596 ERROR(
"USB: UHCI: QH space full");
597 return static_cast<uintptr_t
>(-1);
599 m_QHBitmap.set(nIndex);
603 QH *pQH = &m_pQHList[nIndex];
604 ByteSet(pQH, 0,
sizeof(
QH));
609 pQH->pMetaData->endpointInfo = endpointInfo;
610 pQH->pMetaData->bPeriodic =
false;
611 pQH->pMetaData->pFirstTD = pQH->pMetaData->pLastTD = 0;
612 pQH->pMetaData->nTotalBytes = 0;
613 pQH->pMetaData->pPrev = pQH->pMetaData->pNext = 0;
614 pQH->pMetaData->bIgnore =
false;
615 pQH->pMetaData->
id = nIndex;
621 uintptr_t pTransaction,
void (*pCallback)(uintptr_t, ssize_t),
626 if ((pTransaction == static_cast<uintptr_t>(-1)) ||
627 !m_QHBitmap.test(pTransaction))
630 "UHCI: doAsync: didn't get a valid transaction id [" 631 << pTransaction <<
"].");
642 QH *pQH = &m_pQHList[pTransaction];
643 pQH->pMetaData->pCallback = pCallback;
644 pQH->pMetaData->pParam = pParam;
645 pQH->pMetaData->pLastTD->bIoc = 1;
648 if (m_pCurrentAsyncQueueTail)
651 size_t queueHeadIndex =
652 (
reinterpret_cast<uintptr_t
>(m_pCurrentAsyncQueueHead) -
653 reinterpret_cast<uintptr_t>(m_pQHList)) /
655 pQH->pNext = (m_pQHListPhys + (queueHeadIndex *
sizeof(
QH))) >> 4;
656 pQH->bNextInvalid = 0;
661 m_AsyncQueueListChangeLock.acquire();
663 pQH->pMetaData->bIgnore =
true;
664 m_AsyncSchedule.pushBack(pQH);
666 QH *pOldTail = m_pCurrentAsyncQueueTail;
669 m_pCurrentAsyncQueueTail = pQH;
672 pOldTail->pNext = (m_pQHListPhys + (pTransaction *
sizeof(
QH))) >> 4;
673 pOldTail->bNextInvalid = 0;
674 pOldTail->bNextQH = 1;
677 pOldTail->pMetaData->pNext = pQH;
680 pQH->pMetaData->pNext = m_pCurrentAsyncQueueHead;
681 pQH->pMetaData->pPrev = pOldTail;
682 m_pCurrentAsyncQueueHead->pMetaData->pPrev = pQH;
685 pQH->pMetaData->bIgnore =
false;
687 m_AsyncQueueListChangeLock.release();
691 ERROR(
"UHCI: Queue tail is null!");
698 UsbEndpoint endpointInfo, uintptr_t pBuffer, uint16_t nBytes,
699 void (*pCallback)(uintptr_t, ssize_t), uintptr_t pParam)
702 uintptr_t nTransaction = createTransaction(endpointInfo);
705 QH *pQH = &m_pQHList[nTransaction];
706 pQH->pMetaData->bPeriodic =
true;
709 addTransferToTransaction(nTransaction,
false, UsbPidIn, pBuffer, nBytes);
710 if (!pQH->pMetaData->pLastTD)
712 ERROR(
"USB: UHCI: Couldn't add transfer to transaction!");
717 TD *pTD = pQH->pMetaData->pLastTD;
721 doAsync(nTransaction, pCallback, pParam);
726 #ifdef USB_VERBOSE_DEBUG 727 DEBUG_LOG(
"USB: UHCI: Reset on port " << nPort);
734 m_pBase->read16(UHCI_PORTSC + (nPort * 2)) & ~UHCI_PORTSC_ENABLE,
735 UHCI_PORTSC + (nPort * 2));
736 while (m_pBase->read16(UHCI_PORTSC + (nPort * 2)) & UHCI_PORTSC_ENABLE)
737 Time::delay(10 * Time::Multiplier::Millisecond);
742 m_pBase->read16(UHCI_PORTSC + (nPort * 2)) | UHCI_PORTSC_PRES,
743 UHCI_PORTSC + (nPort * 2));
744 Time::delay(50 * Time::Multiplier::Millisecond);
746 m_pBase->read16(UHCI_PORTSC + (nPort * 2)) & ~UHCI_PORTSC_PRES,
747 UHCI_PORTSC + (nPort * 2));
751 m_pBase->read16(UHCI_PORTSC + (nPort * 2)) | UHCI_PORTSC_ENABLE,
752 UHCI_PORTSC + (nPort * 2));
753 Time::delay((bErrorResponse ? 500 : 100) * Time::Multiplier::Millisecond);
756 if (!(m_pBase->read16(UHCI_PORTSC + (nPort * 2)) & UHCI_PORTSC_ENABLE))
760 "USB: UHCI: During reset, port " 761 << nPort <<
" could not be enabled. It may become enabled soon.");
768 m_pBase->read16(UHCI_PORTSC + (nPort * 2)) |
769 (UHCI_PORTSC_EDCH | UHCI_PORTSC_CSCH),
770 UHCI_PORTSC + (nPort * 2));
773 if (!(m_pBase->read16(UHCI_PORTSC + (nPort * 2)) & UHCI_PORTSC_CONN))
777 "USB: UHCI: During reset, port " 779 <<
" was enabled but had no device on it. A device may be detected " 785 #ifdef USB_VERBOSE_DEBUG 787 "USB: Post-reset status is " 788 << m_pBase->read16(UHCI_PORTSC + (nPort * 2)));
795 uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5,
796 uint64_t p6, uint64_t p7, uint64_t p8)
799 if (m_pBase->read16(UHCI_PORTSC + (p1 * 2)) & UHCI_PORTSC_CONN)
802 if (m_pBase->read16(UHCI_PORTSC + (p1 * 2)) & UHCI_PORTSC_LOSPEED)
805 "USB: UHCI [" <<
this <<
"]: Port " <<
Dec << p1 <<
Hex 806 <<
" has a low-speed device connected to it");
807 if (!deviceConnected(p1, LowSpeed))
810 <<
this <<
"]: Port " <<
Dec << p1 <<
Hex 811 <<
" appeared to be connected but could not be set up");
816 "USB: UHCI [" <<
this <<
"]: Port " <<
Dec << p1 <<
Hex 817 <<
" has a full-speed device connected to it");
818 if (!deviceConnected(p1, FullSpeed))
821 <<
this <<
"]: Port " <<
Dec << p1 <<
Hex 822 <<
" appeared to be connected but could not be set up");
828 "USB: UHCI [" <<
this <<
"]: Device on port " <<
Dec << p1 <<
Hex 829 <<
" disconnected.");
830 deviceDisconnected(p1);
837 m_nPortCheckTicks += delta;
840 if (m_nPortCheckTicks >= 1000000)
843 m_nPortCheckTicks = 0;
846 for (
size_t i = 0; i < m_nPorts; i++)
848 if (m_pBase->read16(UHCI_PORTSC + (i * 2)) & UHCI_PORTSC_CSCH)
852 m_pBase->read16(UHCI_PORTSC + (i * 2)) | UHCI_PORTSC_CSCH,
853 UHCI_PORTSC + (i * 2));
856 if (!m_IgnoredPorts.test(i))
857 addAsyncRequest(0, i);
865 m_pBase->write16(m_pBase->read16(UHCI_CMD) & ~1, UHCI_CMD);
866 while (!(m_pBase->read16(UHCI_STS) & UHCI_STS_HALT))
867 Time::delay(10 * Time::Multiplier::Millisecond);
872 m_pBase->write16(m_pBase->read16(UHCI_CMD) | 1, UHCI_CMD);
873 while (m_pBase->read16(UHCI_STS) & UHCI_STS_HALT)
874 Time::delay(10 * Time::Multiplier::Millisecond);
void pushBack(const T &value)
static PhysicalMemoryManager & instance()
static bool getInterrupts()
Iterator erase(Iterator &Iter)
static const size_t continuous
virtual void getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
virtual Timer * getTimer()=0
void pushFront(const T &value)
virtual bool isMapped(void *virtualAddress)=0
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)
Gets a UsbDevice from a given vendor:product pair.
static ProcessorInformation & information()
void start()
Starts the UHCI controller.
virtual void addInterruptInHandler(UsbEndpoint endpointInfo, uintptr_t pBuffer, uint16_t nBytes, void(*pCallback)(uintptr_t, ssize_t), uintptr_t pParam=0)
Adds a new handler for an interrupt IN transaction.
virtual uintptr_t createTransaction(UsbEndpoint endpointInfo)
Creates a new transaction with the given endpoint data.
void timer(uint64_t delta, InterruptState &state)
Timer callback to handle port status changes.
static const size_t Write
virtual bool irq(irq_id_t number, InterruptState &state)
IRQ handler.
static const size_t KernelMode
uint32_t readConfigSpace(Device *pDev, uint8_t offset)
::Iterator< T, node_t > Iterator
static void setInterrupts(bool bEnable)
void stop()
Stops the UHCI controller.
virtual irq_id_t registerPciIrqHandler(IrqHandler *handler, Device *pDevice)=0
virtual uint64_t executeRequest(uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
virtual void addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid, uintptr_t pBuffer, size_t nBytes)
Adds a new transfer to an existent transaction.
virtual bool control(uint8_t irq, ControlCode code, size_t argument)
virtual void doAsync(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t)=0, uintptr_t pParam=0)
void writeConfigSpace(Device *pDev, uint8_t offset, uint32_t data)