The Pedigree Project  0.1
Uhci.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #ifdef X86_COMMON
21 
22 #include "Uhci.h"
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"
41 
42 #define INDEX_FROM_TD_VIRT(ptr) \
43  (((reinterpret_cast<uintptr_t>((ptr)) - \
44  reinterpret_cast<uintptr_t>(m_pTDList)) / \
45  sizeof(TD)))
46 #define INDEX_FROM_TD_PHYS(ptr) ((((ptr) -m_pTDListPhys) / sizeof(TD)))
47 #define PHYS_TD(idx) (m_pTDListPhys + ((idx) * sizeof(TD)))
48 
49 #define QH_REGION_SIZE 0x4000
50 #define TD_REGION_SIZE 0x8000
51 
52 #define TOTAL_MEM_PAGES \
53  ((QH_REGION_SIZE / 0x1000) + (TD_REGION_SIZE / 0x1000) + 1)
54 
55 static int threadStub(void *p)
56 {
57  Uhci *pUhci = reinterpret_cast<Uhci *>(p);
58  pUhci->doDequeue();
59 }
60 
61 Uhci::Uhci(Device *pDev)
62  : UsbHub(pDev), RequestQueue("UHCI"), m_pBase(0), m_nPorts(0),
63  m_AsyncQueueListChangeLock(), m_UhciMR("Uhci-MR"),
64  m_pCurrentAsyncQueueTail(0), m_pCurrentAsyncQueueHead(0),
65  m_AsyncSchedule(), m_DequeueList(), m_DequeueCount(0),
66  m_nPortCheckTicks(0)
67 {
68  setSpecificType(String("UHCI"));
69 
70  // Verify that IRQs are enabled - we need them!
73 
74  // Grab the ports
75  m_pBase = m_Addresses[0]->m_Io;
76  m_Addresses[0]->map();
77 
78  // Allocate the memory region
79  if (!PhysicalMemoryManager::instance().allocateRegion(
80  m_UhciMR, TOTAL_MEM_PAGES, PhysicalMemoryManager::continuous,
82  {
83  ERROR("USB: UHCI: Couldn't allocate memory region!");
84  return;
85  }
86 
87  uintptr_t pVirtBase =
88  reinterpret_cast<uintptr_t>(m_UhciMR.virtualAddress());
89  physical_uintptr_t pPhysBase = m_UhciMR.physicalAddress();
90 
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);
94 
95  m_pFrameListPhys = pPhysBase;
96  m_pQHListPhys = pPhysBase + 0x1000;
97  m_pTDListPhys = m_pQHListPhys + QH_REGION_SIZE;
98 
99  // Allocate room for the Dummy QH
100  m_QHBitmap.set(0);
101  QH *pDummyQH = &m_pQHList[0];
102 
103  // Set all frame list entries to the dummy QH
104  DoubleWordSet(m_pFrameList, m_pQHListPhys | 2, 0x400);
105 
106  ByteSet(pDummyQH, 0, sizeof(QH));
107 
108  pDummyQH->pNext = m_pQHListPhys >> 4;
109  pDummyQH->bNextQH = 1;
110  pDummyQH->bElemInvalid = 1;
111 
112  pDummyQH->pMetaData = new QH::MetaData;
113  pDummyQH->pMetaData->pPrev = pDummyQH->pMetaData->pNext = pDummyQH;
114 
115  m_pCurrentAsyncQueueTail = m_pCurrentAsyncQueueHead = pDummyQH;
116 
117  // Dequeue main thread
118  Thread *pThread = new Thread(
119  Processor::information().getCurrentThread()->getParent(), threadStub,
120  reinterpret_cast<void *>(this));
121  pThread->detach();
122 
123  // Install the IRQ handler
124  Machine::instance().getIrqManager()->registerPciIrqHandler(this, this);
125  Machine::instance().getIrqManager()->control(
126  getInterruptNumber(), IrqManager::MitigationThreshold,
127  (1500000 / 64)); // 12KB/ms (12Mbps) in bytes, divided by 64 bytes
128  // maximum per transfer/IRQ
129 
130  // Set up the RequestQueue
131  initialise();
132 
133  uint32_t nCommand = PciBus::instance().readConfigSpace(this, 1);
134 #ifdef USB_VERBOSE_DEBUG
135  DEBUG_LOG("USB: UHCI: Pci command+status: " << nCommand);
136 #endif
137  PciBus::instance().writeConfigSpace(this, 1, nCommand | 0x6);
138 
139  // Disable legacy emulation and SMI generation
140  PciBus::instance().writeConfigSpace(this, 0xC0 / 4, 0x8F00); // Clear bits
141 
142  // Stop a running controller (BIOS may have started it up). Unset the
143  // configured flag, as we are no longer configured properly.
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);
148 
149  // Reset the host controller
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);
153 
154  // Write frame list pointer
155  m_pBase->write32(m_pFrameListPhys, UHCI_FRLP);
156 
157  // Enable wanted interrupts
158  m_pBase->write16(0xf, UHCI_INTR);
159 
160  // Enable USB controller interrupts
161  PciBus::instance().writeConfigSpace(this, 0xC0 / 4, 0x2000); // PIRQ enble
162 
163  // Start the controller: 64-byte reclamation and CF set, as well as run bit.
164  // Also, force a global resume of all ports out of any form of suspend state
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);
170 
171 #ifdef USB_VERBOSE_DEBUG
172  DEBUG_LOG("USB: UHCI: Reset complete");
173 #endif
174 
175  // Give time for ports to resume and stabilise.
176  Time::delay(100 * Time::Multiplier::Millisecond);
177 
178  for (size_t i = 0; i < 8; i++)
179  {
180  uint16_t nPortStatus = m_pBase->read16(UHCI_PORTSC + (i * 2));
181  NOTICE("Port status is " << nPortStatus);
182  if ((!(nPortStatus & 0x80)) && (m_nPorts >= 2))
183  {
184  break; // Controllers must have 2 ports, but can have up to 7 by
185  // the spec.
186  }
187 
188  // Reset the port (the timer will receive the connection status changes)
189  if (portReset(i))
190  executeRequest(i);
191 
192  m_nPorts++;
193  }
194 
195  // Install the timer handler for the periodic port checks
196  Machine::instance().getTimer()->registerHandler(this);
197 }
198 
199 Uhci::~Uhci()
200 {
201 }
202 
203 void Uhci::doDequeue()
204 {
205  while (1)
206  {
207  m_DequeueCount.acquire();
208 
209  QH *pQH = 0;
210  {
211  LockGuard<Spinlock> guard(m_AsyncQueueListChangeLock);
212  pQH = m_DequeueList.popFront();
213  }
214 
215  if (!pQH)
216  continue;
217 
218  // Remove all TDs
219  if (pQH->pMetaData->completedTdList.count())
220  {
221  for (List<TD *>::Iterator it =
222  pQH->pMetaData->completedTdList.begin();
223  it != pQH->pMetaData->completedTdList.end(); it++)
224  {
225  size_t idx = (*it)->id;
226  ByteSet(*it, 0, sizeof(TD));
227 
228  m_TDBitmap.clear(idx);
229  }
230  }
231 
232  // Will only be valid if we hit an error - some TDs may not have been
233  // run, so they'll not be in the completed list.
234  if (pQH->pMetaData->tdList.count())
235  {
236  for (List<TD *>::Iterator it = pQH->pMetaData->tdList.begin();
237  it != pQH->pMetaData->tdList.end(); it++)
238  {
239  size_t idx = (*it)->id;
240  ByteSet(*it, 0, sizeof(TD));
241 
242  m_TDBitmap.clear(idx);
243  }
244  }
245 
246  // This QH is done
247  size_t id = pQH->pMetaData->id;
248 
249  // Completely invalidate the QH
250  delete pQH->pMetaData;
251  ByteSet(pQH, 0, sizeof(QH));
252 
253  m_QHBitmap.clear(id);
254 
255 #ifdef USB_VERBOSE_DEBUG
256  DEBUG_LOG("Dequeue complete.");
257 #endif
258  }
259 }
260 
261 bool Uhci::irq(irq_id_t number, InterruptState &state)
262 {
263  uint16_t nStatus = m_pBase->read16(UHCI_STS);
264 
265  if (!nStatus)
266  {
267  return false; // Shared IRQ: this IRQ is for another device
268  }
269 
270  m_pBase->write16(nStatus, UHCI_STS);
271 
272 #ifdef USB_VERBOSE_DEBUG
273  DEBUG_LOG("UHCI IRQ " << nStatus);
274 #endif
275 
276  List<QH *> persistList;
277 
278  // Because there's no IOC for *every* transfer, we need to handle errors
279  // that occur before the last transfer. These will create an error status
280  // only.
281  if (nStatus & (UHCI_STS_INT | UHCI_STS_ERR))
282  {
283  QH *pQH = 0;
284  do
285  {
286  {
287  LockGuard<Spinlock> guard(m_AsyncQueueListChangeLock);
288  if (m_AsyncSchedule.count())
289  pQH = m_AsyncSchedule.popFront();
290  else
291  break;
292  }
293 
294  {
295  bool bPeriodic = pQH->pMetaData->bPeriodic;
296 
297  // Iterate the TD list
298  TD *pTD = 0;
299  while (pQH->pMetaData->tdList.count())
300  {
301  pTD = pQH->pMetaData->tdList.popFront();
302 
303  // If we've already detected that this TD was a short
304  // transfer, don't process any more TDs
305  if (pTD->bShortTransferTD)
306  break;
307 
308  bool bEndOfTransfer = false;
309  if (pTD->nStatus == 0x80)
310  {
311  pQH->pMetaData->tdList.pushFront(pTD);
312  break;
313  }
314 
315  ssize_t nResult;
316  if (((pTD->nErr == 0) && (pTD->nStatus & 0x7e)) ||
317  (nStatus & UHCI_STS_ERR))
318  {
319  // #ifdef USB_VERBOSE_DEBUG
320  ERROR_NOLOCK(
321  ((nStatus & UHCI_STS_ERR) ? "USB" : "TD")
322  << " ERROR!");
323  ERROR_NOLOCK(
324  "TD Status: " << pTD->nStatus << " [" << pTD->nErr
325  << "], USB status: " << nStatus);
326  // #endif
327  nResult = -pTD->getError();
328  }
329  else
330  {
331  nResult = (pTD->nActLen + 1) % 0x800;
332  pQH->pMetaData->nTotalBytes += nResult;
333  }
334 #ifdef USB_VERBOSE_DEBUG
335  DEBUG_LOG_NOLOCK(
336  "TD #"
337  << Dec << pTD->id << " [QH #" << pQH->pMetaData->id
338  << Hex << "] DONE: " << Dec << pTD->nAddress << ":"
339  << pTD->nEndpoint << " "
340  << (pTD->nPid == UsbPidOut ?
341  "OUT" :
342  (pTD->nPid == UsbPidIn ?
343  "IN" :
344  (pTD->nPid == UsbPidSetup ? "SETUP" : "")))
345  << " " << nResult << Hex);
346 #endif
347 
348  // Handle the "end of transfer" cases
349  bEndOfTransfer =
350  (!bPeriodic &&
351  ((nResult < 0) || (pTD == pQH->pMetaData->pLastTD))) ||
352  (bPeriodic && (nResult >= 0));
353 
354  // Some extra cases we need to handle
355  if ((pTD != pQH->pMetaData->pLastTD) && !bEndOfTransfer)
356  {
357  // Control endpoints are irrelevant here
358  if (pTD->nEndpoint)
359  {
360  if ((nResult >= 0) && (pTD->nPid == UsbPidIn))
361  {
362  // Check for a short read. There is no point
363  // continuing to read from the device if it's
364  // sent us less than we asked for before the
365  // last TD. This stops the transfer hanging on
366  // IN transactions that return less data than
367  // expected.
368  if (nResult < pTD->nBufferSize)
369  {
370  DEBUG_LOG_NOLOCK(
371  "UHCI: Short read - got "
372  << nResult << " bytes, wanted "
373  << pTD->nBufferSize << " bytes");
374  pTD->bShortTransferTD = true;
375  bEndOfTransfer = true;
376  }
377  }
378  }
379  }
380 
381  if (!bPeriodic)
382  pQH->pMetaData->completedTdList.pushBack(pTD);
383 
384  // Last TD or error condition, if async, otherwise only when
385  // it gives no error
386  if (bEndOfTransfer)
387  {
388  // Valid callback?
389  if (pQH->pMetaData->pCallback)
390  {
391  pQH->pMetaData->pCallback(
392  pQH->pMetaData->pParam,
393  nResult < 0 ? nResult :
394  pQH->pMetaData->nTotalBytes);
395  }
396 
397  if (!bPeriodic)
398  {
399  // This queue head is done, dequeue.
407  m_AsyncQueueListChangeLock
408  .acquire(); // Atomic operation
409 
410  // Stop the controller while we dequeue
411  stop();
412 
413  // Update the hardware and software linked lists
414  {
415  QH *pPrev = pQH->pMetaData->pPrev;
416  QH *pNext = pQH->pMetaData->pNext;
417 
418  // Main non-hardware linked list update
419  pPrev->pMetaData->pNext = pNext;
420  pNext->pMetaData->pPrev = pPrev;
421 
422  // Hardware linked list update
423  pPrev->pNext = pQH->pNext;
424  pPrev->bNextQH = 1;
425  pPrev->bNextInvalid = 0;
426 
427  // Update the tail pointer if we need to
428  if (pQH == m_pCurrentAsyncQueueTail)
429  m_pCurrentAsyncQueueTail = pPrev;
430  }
431 
432  m_DequeueList.pushBack(pQH);
433 
434  // Resume the controller, dequeue done.
435  start();
436 
437  m_AsyncQueueListChangeLock.release();
438 
439  m_DequeueCount.release();
440 
441  // Ignore after the linked list update to avoid a
442  // case where the dequeue call hits this QH while
443  // we unlink it. This is a problem as the dequeue
444  // call zeroes all TDs and the QH itself. Ouch!
445  pQH->pMetaData->bIgnore = true;
446  }
447  else
448  {
449  // Invert data toggle
450  pTD->bDataToggle = !pTD->bDataToggle;
451 
452  // Clear the total bytes field so it won't grow with
453  // each completed transfer
454  pQH->pMetaData->nTotalBytes = 0;
455  }
456  }
457 
458  // Interrupt TDs need to be always active
459  if (bPeriodic)
460  {
461  pQH->pMetaData->bIgnore = false;
462  pTD->nStatus = 0x80;
463  pTD->nActLen = 0;
464 
465  // Modified by the host controller
466  pQH->pElem = PHYS_TD(pTD->id) >> 4;
467  pQH->bElemInvalid = 0;
468  pQH->bElemQH = 0;
469 
470  pQH->pMetaData->tdList.pushBack(pTD);
471  break; // Periodic QHs should only have one TD
472  }
473  }
474  }
475 
476  if (!pQH->pMetaData->bIgnore)
477  persistList.pushBack(pQH);
478 
479  } while (pQH);
480  }
481 
482  if (persistList.count())
483  {
484  LockGuard<Spinlock> guard(m_AsyncQueueListChangeLock);
485  for (List<QH *>::Iterator it = persistList.begin();
486  it != persistList.end();)
487  {
488  m_AsyncSchedule.pushBack(*it);
489  it = persistList.erase(it);
490  }
491  }
492 
493  return true;
494 }
495 
497  uintptr_t pTransaction, bool bToggle, UsbPid pid, uintptr_t pBuffer,
498  size_t nBytes)
499 {
500  // Atomic operation: find clear bit, set it
501  size_t nIndex = 0;
502  {
503  LockGuard<Mutex> guard(m_Mutex);
504  nIndex = m_TDBitmap.getFirstClear();
505  if (nIndex >= (TD_REGION_SIZE / sizeof(TD)))
506  {
507  ERROR("USB: UHCI: TD space full");
508  return;
509  }
510  m_TDBitmap.set(nIndex);
511  }
512 
513  // Grab the TD
514  TD *pTD = &m_pTDList[nIndex];
515  ByteSet(pTD, 0, sizeof(TD));
516  pTD->bNextInvalid =
517  1; // Assume next is invalid, will be zeroed if another TD is linked
518  pTD->id = nIndex;
519 
520  // Grab the QH
521  QH *pQH = &m_pQHList[pTransaction];
522 
523  // Active, and only allow one retry
524  pTD->nStatus = 0x80;
525  pTD->bIoc = 0; // Don't issue an interrupt on completion until the very
526  // last TD in the transaction.
527  pTD->nErr = 3;
528 
529  // PID for this transfer
530  pTD->nPid = pid;
531 
532  // Speed information
533  pTD->bLoSpeed = pQH->pMetaData->endpointInfo.speed == LowSpeed;
534 
535  // Don't care about short packet detection
536  pTD->bSpd = 0;
537 
538  // Endpoint information
539  pTD->nAddress = pQH->pMetaData->endpointInfo.nAddress;
540  pTD->nEndpoint = pQH->pMetaData->endpointInfo.nEndpoint;
541  pTD->bDataToggle = bToggle;
542 
543  // Transfer information
544  pTD->nMaxLen = nBytes ? nBytes - 1 : 0x7ff;
545  pTD->nBufferSize = nBytes;
546  if (nBytes)
547  {
548  VirtualAddressSpace &va =
549  Processor::information().getVirtualAddressSpace();
550  if (va.isMapped(reinterpret_cast<void *>(pBuffer)))
551  {
552  physical_uintptr_t phys = 0;
553  size_t flags = 0;
554  va.getMapping(reinterpret_cast<void *>(pBuffer), phys, flags);
555  pTD->pBuff = phys + (pBuffer & 0xFFF);
556  }
557  else
558  {
559  ERROR(
560  "UHCI: addTransferToTransaction: Buffer (page "
561  << Dec << pBuffer << Hex << ") isn't mapped!");
562  m_TDBitmap.clear(nIndex);
563  return;
564  }
565  }
566 
567  // Link into the existing TD list
568  if (pQH->pMetaData->pLastTD)
569  {
570  pQH->pMetaData->pLastTD->pNext = PHYS_TD(nIndex) >> 4;
571  pQH->pMetaData->pLastTD->bNextInvalid = 0;
572  pQH->pMetaData->pLastTD->bNextQH = 0;
573  // pQH->pMetaData->pLastTD->bNextDepth = 1;
574  }
575  else
576  {
577  pQH->pMetaData->pFirstTD = pTD;
578  pQH->pElem = PHYS_TD(nIndex) >> 4;
579  pQH->bElemInvalid = 0;
580  pQH->bElemQH = 0;
581  }
582  pQH->pMetaData->pLastTD = pTD;
583 
584  pQH->pMetaData->tdList.pushBack(pTD);
585 }
586 
587 uintptr_t Uhci::createTransaction(UsbEndpoint endpointInfo)
588 {
589  // Atomic operation: find clear bit, set it
590  size_t nIndex = 0;
591  {
592  LockGuard<Mutex> guard(m_Mutex);
593  nIndex = m_QHBitmap.getFirstClear();
594  if (nIndex >= (QH_REGION_SIZE / sizeof(QH)))
595  {
596  ERROR("USB: UHCI: QH space full");
597  return static_cast<uintptr_t>(-1);
598  }
599  m_QHBitmap.set(nIndex);
600  }
601 
602  // Grab the QH
603  QH *pQH = &m_pQHList[nIndex];
604  ByteSet(pQH, 0, sizeof(QH));
605 
606  // Only need to configure metadata, everything else is set during linkage
607  // and TD creation
608  pQH->pMetaData = new QH::MetaData;
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;
616 
617  return nIndex;
618 }
619 
621  uintptr_t pTransaction, void (*pCallback)(uintptr_t, ssize_t),
622  uintptr_t pParam)
623 {
624  {
625  LockGuard<Mutex> guard(m_Mutex);
626  if ((pTransaction == static_cast<uintptr_t>(-1)) ||
627  !m_QHBitmap.test(pTransaction))
628  {
629  ERROR(
630  "UHCI: doAsync: didn't get a valid transaction id ["
631  << pTransaction << "].");
632  return;
633  }
634  }
635 
636  // Stop a running controller. We're modifying the hardware list and we don't
637  // want it to be touched while we're changing it. Hardware doesn't care
638  // about our "change spinlock".
639  stop();
640 
641  // Configure remaining metadata
642  QH *pQH = &m_pQHList[pTransaction];
643  pQH->pMetaData->pCallback = pCallback;
644  pQH->pMetaData->pParam = pParam;
645  pQH->pMetaData->pLastTD->bIoc = 1;
646 
647  // Do we need to configure the asynchronous schedule?
648  if (m_pCurrentAsyncQueueTail)
649  {
650  // Current QH needs to point to the schedule's head
651  size_t queueHeadIndex =
652  (reinterpret_cast<uintptr_t>(m_pCurrentAsyncQueueHead) -
653  reinterpret_cast<uintptr_t>(m_pQHList)) /
654  sizeof(QH);
655  pQH->pNext = (m_pQHListPhys + (queueHeadIndex * sizeof(QH))) >> 4;
656  pQH->bNextInvalid = 0;
657  pQH->bNextQH = 1;
658 
659  // Atomic operation - modifying both the housekeeping and the
660  // hardware linked lists
661  m_AsyncQueueListChangeLock.acquire();
662 
663  pQH->pMetaData->bIgnore = true;
664  m_AsyncSchedule.pushBack(pQH);
665 
666  QH *pOldTail = m_pCurrentAsyncQueueTail;
667 
668  // Update the tail pointer
669  m_pCurrentAsyncQueueTail = pQH;
670 
671  // The current tail needs to point to this QH
672  pOldTail->pNext = (m_pQHListPhys + (pTransaction * sizeof(QH))) >> 4;
673  pOldTail->bNextInvalid = 0;
674  pOldTail->bNextQH = 1;
675 
676  // Finally, fix the linked list
677  pOldTail->pMetaData->pNext = pQH;
678 
679  // Enter the information for correct dequeue
680  pQH->pMetaData->pNext = m_pCurrentAsyncQueueHead;
681  pQH->pMetaData->pPrev = pOldTail;
682  m_pCurrentAsyncQueueHead->pMetaData->pPrev = pQH;
683 
684  // Ready for IRQs
685  pQH->pMetaData->bIgnore = false;
686 
687  m_AsyncQueueListChangeLock.release();
688  }
689  else
690  {
691  ERROR("UHCI: Queue tail is null!");
692  }
693 
694  start();
695 }
696 
698  UsbEndpoint endpointInfo, uintptr_t pBuffer, uint16_t nBytes,
699  void (*pCallback)(uintptr_t, ssize_t), uintptr_t pParam)
700 {
701  // Create a new transaction
702  uintptr_t nTransaction = createTransaction(endpointInfo);
703 
704  // Get the QH and set the periodic flag
705  QH *pQH = &m_pQHList[nTransaction];
706  pQH->pMetaData->bPeriodic = true;
707 
708  // Add a single transfer to the transaction
709  addTransferToTransaction(nTransaction, false, UsbPidIn, pBuffer, nBytes);
710  if (!pQH->pMetaData->pLastTD)
711  {
712  ERROR("USB: UHCI: Couldn't add transfer to transaction!");
713  return;
714  }
715 
716  // Get the TD and set the error counter to "unlimited retries"
717  TD *pTD = pQH->pMetaData->pLastTD;
718  pTD->nErr = 0;
719 
720  // Let doAsync do the rest
721  doAsync(nTransaction, pCallback, pParam);
722 }
723 
724 bool Uhci::portReset(uint8_t nPort, bool bErrorResponse)
725 {
726 #ifdef USB_VERBOSE_DEBUG
727  DEBUG_LOG("USB: UHCI: Reset on port " << nPort);
728 #endif
729 
730  if (bErrorResponse)
731  {
732  // Before port reset, disable the port
733  m_pBase->write16(
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);
738  }
739 
740  // Perform a reset of the port
741  m_pBase->write16(
742  m_pBase->read16(UHCI_PORTSC + (nPort * 2)) | UHCI_PORTSC_PRES,
743  UHCI_PORTSC + (nPort * 2));
744  Time::delay(50 * Time::Multiplier::Millisecond);
745  m_pBase->write16(
746  m_pBase->read16(UHCI_PORTSC + (nPort * 2)) & ~UHCI_PORTSC_PRES,
747  UHCI_PORTSC + (nPort * 2));
748 
749  // Enable the port
750  m_pBase->write16(
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);
754 
755  // Check that the device is completely enabled
756  if (!(m_pBase->read16(UHCI_PORTSC + (nPort * 2)) & UHCI_PORTSC_ENABLE))
757  {
758  //#ifdef USB_VERBOSE_DEBUG
759  DEBUG_LOG(
760  "USB: UHCI: During reset, port "
761  << nPort << " could not be enabled. It may become enabled soon.");
762  //#endif
763  return false;
764  }
765 
766  // Clear the "enable/disable change status" register
767  m_pBase->write16(
768  m_pBase->read16(UHCI_PORTSC + (nPort * 2)) |
769  (UHCI_PORTSC_EDCH | UHCI_PORTSC_CSCH),
770  UHCI_PORTSC + (nPort * 2));
771 
772  // Verify that we have a device connected here
773  if (!(m_pBase->read16(UHCI_PORTSC + (nPort * 2)) & UHCI_PORTSC_CONN))
774  {
775  //#ifdef USB_VERBOSE_DEBUG
776  DEBUG_LOG(
777  "USB: UHCI: During reset, port "
778  << nPort
779  << " was enabled but had no device on it. A device may be detected "
780  "shortly.");
781  //#endif
782  return false;
783  }
784 
785 #ifdef USB_VERBOSE_DEBUG
786  DEBUG_LOG(
787  "USB: Post-reset status is "
788  << m_pBase->read16(UHCI_PORTSC + (nPort * 2)));
789 #endif
790 
791  return true;
792 }
793 
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)
797 {
798  // Check for a connected device
799  if (m_pBase->read16(UHCI_PORTSC + (p1 * 2)) & UHCI_PORTSC_CONN)
800  {
801  // Determine the speed of the attached device
802  if (m_pBase->read16(UHCI_PORTSC + (p1 * 2)) & UHCI_PORTSC_LOSPEED)
803  {
804  DEBUG_LOG(
805  "USB: UHCI [" << this << "]: Port " << Dec << p1 << Hex
806  << " has a low-speed device connected to it");
807  if (!deviceConnected(p1, LowSpeed))
808  WARNING(
809  "USB: UHCI ["
810  << this << "]: Port " << Dec << p1 << Hex
811  << " appeared to be connected but could not be set up");
812  }
813  else
814  {
815  DEBUG_LOG(
816  "USB: UHCI [" << this << "]: Port " << Dec << p1 << Hex
817  << " has a full-speed device connected to it");
818  if (!deviceConnected(p1, FullSpeed))
819  WARNING(
820  "USB UHCI ["
821  << this << "]: Port " << Dec << p1 << Hex
822  << " appeared to be connected but could not be set up");
823  }
824  }
825  else
826  {
827  DEBUG_LOG(
828  "USB: UHCI [" << this << "]: Device on port " << Dec << p1 << Hex
829  << " disconnected.");
830  deviceDisconnected(p1);
831  }
832  return 0;
833 }
834 
835 void Uhci::timer(uint64_t delta, InterruptState &state)
836 {
837  m_nPortCheckTicks += delta;
838 
839  // We check the ports once in a Millisecond
840  if (m_nPortCheckTicks >= 1000000)
841  {
842  // Reset the counter
843  m_nPortCheckTicks = 0;
844 
845  // Check every port for a change
846  for (size_t i = 0; i < m_nPorts; i++)
847  {
848  if (m_pBase->read16(UHCI_PORTSC + (i * 2)) & UHCI_PORTSC_CSCH)
849  {
850  // Clear the port status change
851  m_pBase->write16(
852  m_pBase->read16(UHCI_PORTSC + (i * 2)) | UHCI_PORTSC_CSCH,
853  UHCI_PORTSC + (i * 2));
854 
855  // Now we can safely add the request
856  if (!m_IgnoredPorts.test(i))
857  addAsyncRequest(0, i);
858  }
859  }
860  }
861 }
862 
864 {
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);
868 }
869 
871 {
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);
875 }
876 
877 #endif
Definition: Uhci.h:110
void pushBack(const T &value)
Definition: List.h:232
static PhysicalMemoryManager & instance()
static bool getInterrupts()
Iterator erase(Iterator &Iter)
Definition: List.h:343
virtual void getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
virtual Timer * getTimer()=0
void pushFront(const T &value)
Definition: List.h:287
virtual bool isMapped(void *virtualAddress)=0
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)
Gets a UsbDevice from a given vendor:product pair.
Definition: Uhci.cc:724
T popFront()
Definition: List.h:319
Definition: String.h:49
static ProcessorInformation & information()
Definition: Processor.cc:45
void start()
Starts the UHCI controller.
Definition: Uhci.cc:870
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.
Definition: Uhci.cc:697
virtual uintptr_t createTransaction(UsbEndpoint endpointInfo)
Creates a new transaction with the given endpoint data.
Definition: Uhci.cc:587
Definition: Device.h:43
void timer(uint64_t delta, InterruptState &state)
Timer callback to handle port status changes.
Definition: Uhci.cc:835
#define WARNING(text)
Definition: Log.h:78
Definition: List.h:64
virtual bool irq(irq_id_t number, InterruptState &state)
IRQ handler.
Definition: Uhci.cc:261
#define NOTICE(text)
Definition: Log.h:74
uint32_t readConfigSpace(Device *pDev, uint8_t offset)
Definition: Pci.cc:75
Definition: Log.h:136
::Iterator< T, node_t > Iterator
Definition: List.h:71
Iterator begin()
Definition: List.h:123
Definition: UsbHub.h:30
static void setInterrupts(bool bEnable)
Definition: Thread.h:54
void stop()
Stops the UHCI controller.
Definition: Uhci.cc:863
virtual irq_id_t registerPciIrqHandler(IrqHandler *handler, Device *pDevice)=0
Definition: Uhci.h:56
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)
Definition: Uhci.cc:794
#define ERROR(text)
Definition: Log.h:82
bool detach()
Definition: Thread.cc:885
Definition: Log.h:138
Definition: Uhci.h:47
virtual void addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid, uintptr_t pBuffer, size_t nBytes)
Adds a new transfer to an existent transaction.
Definition: Uhci.cc:496
virtual bool control(uint8_t irq, ControlCode code, size_t argument)
Definition: IrqManager.cc:29
#define DEBUG_LOG(text)
Definition: Log.h:69
virtual void doAsync(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t)=0, uintptr_t pParam=0)
Definition: Uhci.cc:620
Iterator end()
Definition: List.h:135
void writeConfigSpace(Device *pDev, uint8_t offset, uint32_t data)
Definition: Pci.cc:104
size_t count() const
Definition: List.h:227