diff --git a/src/add-ons/kernel/busses/usb/ehci.cpp b/src/add-ons/kernel/busses/usb/ehci.cpp index cebc5d27de..8f16aaf378 100644 --- a/src/add-ons/kernel/busses/usb/ehci.cpp +++ b/src/add-ons/kernel/busses/usb/ehci.cpp @@ -8,6 +8,7 @@ */ +#include #include #include #include @@ -131,7 +132,8 @@ EHCI::EHCI(pci_info *info, Stack *stack) fRootHubAddress(0), fPortCount(0), fPortResetChange(0), - fPortSuspendChange(0) + fPortSuspendChange(0), + fInterruptPollThread(-1) { if (BusManager::InitCheck() < B_OK) { TRACE_ERROR("bus manager failed to init\n"); @@ -319,23 +321,44 @@ EHCI::EHCI(pci_info *info, Stack *stack) B_NORMAL_PRIORITY, (void *)this); resume_thread(fCleanupThread); - // install the interrupt handler and enable interrupts - install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line, - InterruptHandler, (void *)this, 0); - fEnabledInterrupts = EHCI_USBINTR_HOSTSYSERR | EHCI_USBINTR_USBERRINT - | EHCI_USBINTR_USBINT | EHCI_USBINTR_INTONAA; - WriteOpReg(EHCI_USBINTR, fEnabledInterrupts); + // set up interrupts or interrupt polling now that the controller is ready + bool polling = false; + void *settings = load_driver_settings(B_SAFEMODE_DRIVER_SETTINGS); + if (settings != NULL) { + polling = get_driver_boolean_parameter(settings, "ehci_polling", false, + false); + unload_driver_settings(settings); + } - // ensure that interrupts are enabled on the PCI device as well + if (polling) { + // create and run the polling thread + TRACE_ALWAYS("enabling ehci polling\n"); + fInterruptPollThread = spawn_kernel_thread(InterruptPollThread, + "ehci interrupt poll thread", B_NORMAL_PRIORITY, (void *)this); + resume_thread(fInterruptPollThread); + } else { + // install the interrupt handler and enable interrupts + install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line, + InterruptHandler, (void *)this, 0); + } + + // ensure that interrupts are en-/disabled on the PCI device command = sPCIModule->read_pci_config(fPCIInfo->bus, fPCIInfo->device, fPCIInfo->function, PCI_command, 2); - if ((command & PCI_command_int_disable) != 0) { - TRACE_ALWAYS("PCI interrupts were disabled, enabling\n"); - command &= ~PCI_command_int_disable; + if (polling == ((command & PCI_command_int_disable) == 0)) { + if (polling) + command &= ~PCI_command_int_disable; + else + command |= PCI_command_int_disable; + sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device, fPCIInfo->function, PCI_command, 2, command); } + fEnabledInterrupts = EHCI_USBINTR_HOSTSYSERR | EHCI_USBINTR_USBERRINT + | EHCI_USBINTR_USBINT | EHCI_USBINTR_INTONAA; + WriteOpReg(EHCI_USBINTR, fEnabledInterrupts); + // structures don't span page boundaries size_t itdListSize = EHCI_VFRAMELIST_ENTRIES_COUNT / (B_PAGE_SIZE / sizeof(itd_entry)) * B_PAGE_SIZE; @@ -458,7 +481,6 @@ EHCI::EHCI(pci_info *info, Stack *stack) fFrameBandwidth[i] = MAX_AVAILABLE_BANDWIDTH; } - // allocate a queue head that will always stay in the async frame list fAsyncQueueHead = CreateQueueHead(); if (!fAsyncQueueHead) { @@ -499,6 +521,9 @@ EHCI::~EHCI() wait_for_thread(fCleanupThread, &result); wait_for_thread(fFinishIsochronousThread, &result); + if (fInterruptPollThread >= 0) + wait_for_thread(fInterruptPollThread, &result); + LockIsochronous(); isochronous_transfer_data *isoTransfer = fFirstIsochronousTransfer; while (isoTransfer) { @@ -584,6 +609,7 @@ EHCI::Start() } SetRootHub(fRootHub); + TRACE_ALWAYS("successfully started the controller\n"); return BusManager::Start(); } @@ -1181,6 +1207,25 @@ EHCI::Interrupt() } +int32 +EHCI::InterruptPollThread(void *data) +{ + EHCI *ehci = (EHCI *)data; + + while (!ehci->fStopThreads) { + // TODO: this could be handled much better by only polling when there + // are actual transfers going on... + snooze(1000); + + cpu_status status = disable_interrupts(); + ehci->Interrupt(); + restore_interrupts(status); + } + + return 0; +} + + status_t EHCI::AddPendingTransfer(Transfer *transfer, ehci_qh *queueHead, ehci_qtd *dataDescriptor, bool directionIn) diff --git a/src/add-ons/kernel/busses/usb/ehci.h b/src/add-ons/kernel/busses/usb/ehci.h index 7f10975fde..ce6184bc39 100644 --- a/src/add-ons/kernel/busses/usb/ehci.h +++ b/src/add-ons/kernel/busses/usb/ehci.h @@ -57,7 +57,7 @@ virtual status_t SubmitTransfer(Transfer *transfer); virtual status_t CancelQueuedTransfers(Pipe *pipe, bool force); status_t CancelQueuedIsochronousTransfers(Pipe *pipe, bool force); status_t SubmitIsochronous(Transfer *transfer); - + virtual status_t NotifyPipeChange(Pipe *pipe, usb_change change); @@ -82,6 +82,7 @@ private: // Interrupt functions static int32 InterruptHandler(void *data); int32 Interrupt(); +static int32 InterruptPollThread(void *data); // Transfer management status_t AddPendingTransfer(Transfer *transfer, @@ -228,7 +229,7 @@ static pci_module_info * sPCIModule; sem_id fFinishIsochronousTransfersSem; thread_id fFinishIsochronousThread; mutex fIsochronousLock; - + // Root Hub EHCIRootHub * fRootHub; uint8 fRootHubAddress; @@ -237,6 +238,9 @@ static pci_module_info * sPCIModule; uint8 fPortCount; uint16 fPortResetChange; uint16 fPortSuspendChange; + + // Interrupt polling + thread_id fInterruptPollThread; };