From bcc2d1b69a3c9b2a7951b894ad3e5579dcbaffa1 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sat, 16 Sep 2006 00:06:04 +0000 Subject: [PATCH] * Implemented usable thread synchronisation for UHCI and EHCI. * Cleaned interrupt handlers for both busses, too. * Fixed memory leak of the queue head stray descriptors in EHCI. * Fixed warnings about assigning NULL to the module flags field. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18857 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/busses/usb/ehci.cpp | 90 +++++++++++++++----------- src/add-ons/kernel/busses/usb/ehci.h | 6 +- src/add-ons/kernel/busses/usb/ohci.cpp | 2 +- src/add-ons/kernel/busses/usb/uhci.cpp | 52 +++++++++------ src/add-ons/kernel/busses/usb/uhci.h | 2 +- 5 files changed, 90 insertions(+), 62 deletions(-) diff --git a/src/add-ons/kernel/busses/usb/ehci.cpp b/src/add-ons/kernel/busses/usb/ehci.cpp index 28741bacd7..3d62eba4e3 100644 --- a/src/add-ons/kernel/busses/usb/ehci.cpp +++ b/src/add-ons/kernel/busses/usb/ehci.cpp @@ -35,7 +35,7 @@ ehci_std_ops(int32 op, ...) host_controller_info ehci_module = { { "busses/usb/ehci", - NULL, + 0, ehci_std_ops }, NULL, @@ -104,7 +104,6 @@ EHCI::EHCI(pci_info *info, Stack *stack) fPeriodicFrameList(NULL), fFirstTransfer(NULL), fLastTransfer(NULL), - fFinishTransfers(false), fFinishThread(-1), fStopFinishThread(false), fRootHub(NULL), @@ -209,6 +208,14 @@ EHCI::EHCI(pci_info *info, Stack *stack) return; } + // create semaphores the finisher thread will wait for + fAsyncAdvanceSem = create_sem(0, "EHCI Async Advance"); + fFinishTransfersSem = create_sem(0, "EHCI Finish Transfers"); + if (fFinishTransfersSem < B_OK || fAsyncAdvanceSem < B_OK) { + TRACE_ERROR(("usb_ehci: failed to create semaphores\n")); + return; + } + // create finisher service thread fFinishThread = spawn_kernel_thread(FinishThread, "ehci finish thread", B_NORMAL_PRIORITY, (void *)this); @@ -262,13 +269,15 @@ EHCI::~EHCI() { TRACE(("usb_ehci: tear down EHCI Host Controller Driver\n")); - int32 result = 0; - fStopFinishThread = true; - wait_for_thread(fFinishThread, &result); - - CancelAllPendingTransfers(); WriteOpReg(EHCI_USBCMD, 0); WriteOpReg(EHCI_CONFIGFLAG, 0); + CancelAllPendingTransfers(); + + int32 result = 0; + fStopFinishThread = true; + delete_sem(fAsyncAdvanceSem); + delete_sem(fFinishTransfersSem); + wait_for_thread(fFinishThread, &result); delete fRootHub; delete_area(fPeriodicFrameListArea); @@ -306,6 +315,10 @@ EHCI::Start() } } + // set the interrupt threshold + WriteOpReg(EHCI_USBCMD, ReadOpReg(EHCI_USBCMD) + | (1 << EHCI_USBCMD_ITC_SHIFT)); + if (!running) { TRACE(("usb_ehci: Host Controller didn't start\n")); return B_ERROR; @@ -709,37 +722,40 @@ EHCI::LightReset() int32 EHCI::InterruptHandler(void *data) { - cpu_status status = disable_interrupts(); - spinlock lock = 0; - acquire_spinlock(&lock); - - int32 result = ((EHCI *)data)->Interrupt(); - - release_spinlock(&lock); - restore_interrupts(status); - return result; + return ((EHCI *)data)->Interrupt(); } int32 EHCI::Interrupt() { + spinlock lock = 0; + acquire_spinlock(&lock); + // check if any interrupt was generated uint32 status = ReadOpReg(EHCI_USBSTS); - if ((status & EHCI_USBSTS_INTMASK) == 0) + if ((status & EHCI_USBSTS_INTMASK) == 0) { + release_spinlock(&lock); return B_UNHANDLED_INTERRUPT; + } uint32 acknowledge = 0; + bool asyncAdvance = false; + bool finishTransfers = false; + int32 result = B_HANDLED_INTERRUPT; + if (status & EHCI_USBSTS_USBINT) { TRACE(("usb_ehci: transfer finished\n")); acknowledge |= EHCI_USBSTS_USBINT; - fFinishTransfers = true; + result = B_INVOKE_SCHEDULER; + finishTransfers = true; } if (status & EHCI_USBSTS_USBERRINT) { TRACE(("usb_ehci: transfer error\n")); acknowledge |= EHCI_USBSTS_USBERRINT; - fFinishTransfers = true; + result = B_INVOKE_SCHEDULER; + finishTransfers = true; } if (status & EHCI_USBSTS_PORTCHANGE) { @@ -755,7 +771,8 @@ EHCI::Interrupt() if (status & EHCI_USBSTS_INTONAA) { TRACE(("usb_ehci: interrupt on async advance\n")); acknowledge |= EHCI_USBSTS_INTONAA; - fAsyncAdvance = true; + asyncAdvance = true; + result = B_INVOKE_SCHEDULER; } if (status & EHCI_USBSTS_HOSTSYSERR) { @@ -766,7 +783,14 @@ EHCI::Interrupt() if (acknowledge) WriteOpReg(EHCI_USBSTS, acknowledge); - return B_HANDLED_INTERRUPT; + release_spinlock(&lock); + + if (asyncAdvance) + release_sem_etc(fAsyncAdvanceSem, 1, B_DO_NOT_RESCHEDULE); + if (finishTransfers) + release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE); + + return result; } @@ -871,13 +895,9 @@ void EHCI::FinishTransfers() { while (!fStopFinishThread) { - while (!fFinishTransfers) { - if (fStopFinishThread) - return; - snooze(1000); - } + if (acquire_sem(fFinishTransfersSem) < B_OK) + continue; - fFinishTransfers = false; if (!Lock()) continue; @@ -977,16 +997,14 @@ EHCI::FinishTransfers() } if (freeListHead) { - fAsyncAdvance = false; // set the doorbell and wait for the host controller to notify us WriteOpReg(EHCI_USBCMD, ReadOpReg(EHCI_USBCMD) | EHCI_USBCMD_INTONAAD); - while (!fAsyncAdvance) - snooze(10); - - while (freeListHead) { - ehci_qh *next = (ehci_qh *)freeListHead->next_log; - FreeQueueHead(freeListHead); - freeListHead = next; + if (acquire_sem_etc(fAsyncAdvanceSem, 1, B_RELATIVE_TIMEOUT, 1000) == B_OK) { + while (freeListHead) { + ehci_qh *next = (ehci_qh *)freeListHead->next_log; + FreeQueueHead(freeListHead); + freeListHead = next; + } } } } @@ -1039,6 +1057,7 @@ EHCI::FreeQueueHead(ehci_qh *queueHead) return; FreeDescriptorChain((ehci_qtd *)queueHead->element_log); + FreeDescriptor((ehci_qtd *)queueHead->stray_log); fStack->FreeChunk(queueHead, (void *)queueHead->this_phy, sizeof(ehci_qh)); } @@ -1112,7 +1131,6 @@ EHCI::FillQueueWithRequest(Transfer *transfer, ehci_qh *queueHead, ehci_qtd *strayDescriptor = (ehci_qtd *)queueHead->stray_log; statusDescriptor->token |= EHCI_QTD_IOC | EHCI_QTD_DATA_TOGGLE; - LinkDescriptors(statusDescriptor, strayDescriptor, strayDescriptor); ehci_qtd *dataDescriptor = NULL; if (transfer->VectorCount() > 0) { diff --git a/src/add-ons/kernel/busses/usb/ehci.h b/src/add-ons/kernel/busses/usb/ehci.h index 318b67cd6a..4bd6572b2e 100644 --- a/src/add-ons/kernel/busses/usb/ehci.h +++ b/src/add-ons/kernel/busses/usb/ehci.h @@ -131,14 +131,14 @@ static pci_module_info *sPCIModule; area_id fPeriodicFrameListArea; addr_t *fPeriodicFrameList; - // Async frame list + // Async frame list management ehci_qh *fAsyncQueueHead; - bool fAsyncAdvance; + sem_id fAsyncAdvanceSem; // Maintain a linked list of transfers transfer_data *fFirstTransfer; transfer_data *fLastTransfer; - bool fFinishTransfers; + sem_id fFinishTransfersSem; thread_id fFinishThread; bool fStopFinishThread; diff --git a/src/add-ons/kernel/busses/usb/ohci.cpp b/src/add-ons/kernel/busses/usb/ohci.cpp index 73f4b4d2b9..8cc08d7a6e 100644 --- a/src/add-ons/kernel/busses/usb/ohci.cpp +++ b/src/add-ons/kernel/busses/usb/ohci.cpp @@ -152,7 +152,7 @@ ohci_add_to(Stack *stack) host_controller_info ohci_module = { { "busses/usb/ohci", - NULL, // No flag like B_KEEP_LOADED : the usb module does that + 0, // No flag like B_KEEP_LOADED : the usb module does that ohci_std_ops }, NULL , diff --git a/src/add-ons/kernel/busses/usb/uhci.cpp b/src/add-ons/kernel/busses/usb/uhci.cpp index e2f621d0f2..21685921da 100644 --- a/src/add-ons/kernel/busses/usb/uhci.cpp +++ b/src/add-ons/kernel/busses/usb/uhci.cpp @@ -42,7 +42,7 @@ uhci_std_ops(int32 op, ...) host_controller_info uhci_module = { { "busses/usb/uhci/nielx", - NULL, + 0, uhci_std_ops }, NULL, @@ -324,7 +324,6 @@ UHCI::UHCI(pci_info *info, Stack *stack) fQueues(NULL), fFirstTransfer(NULL), fLastTransfer(NULL), - fFinishTransfers(false), fFinishThread(-1), fStopFinishThread(false), fRootHub(NULL), @@ -410,6 +409,13 @@ UHCI::UHCI(pci_info *info, Stack *stack) for (int32 i = 0; i < 1024; i++) fFrameList[i] = fQueues[0]->PhysicalAddress() | FRAMELIST_NEXT_IS_QH; + // create semaphore the finisher thread will wait for + fFinishTransfersSem = create_sem(0, "UHCI Finish Transfers"); + if (fFinishTransfersSem < B_OK) { + TRACE_ERROR(("usb_ehci: failed to create semaphore\n")); + return; + } + // Create the finisher service thread fFinishThread = spawn_kernel_thread(FinishThread, "uhci finish thread", B_NORMAL_PRIORITY, (void *)this); @@ -433,6 +439,7 @@ UHCI::~UHCI() { int32 result = 0; fStopFinishThread = true; + delete_sem(fFinishTransfersSem); wait_for_thread(fFinishThread, &result); Lock(); @@ -686,14 +693,9 @@ void UHCI::FinishTransfers() { while (!fStopFinishThread) { - while (!fFinishTransfers) { - if (fStopFinishThread) - return; + if (acquire_sem(fFinishTransfersSem) < B_OK) + continue; - snooze(1000); - } - - fFinishTransfers = false; if (!Lock()) continue; @@ -966,35 +968,39 @@ UHCI::ResetPort(uint8 index) int32 UHCI::InterruptHandler(void *data) { - cpu_status status = disable_interrupts(); - spinlock lock = 0; - acquire_spinlock(&lock); - - int32 result = ((UHCI *)data)->Interrupt(); - - release_spinlock(&lock); - restore_interrupts(status); - return result; + return ((UHCI *)data)->Interrupt(); } int32 UHCI::Interrupt() { + spinlock lock = 0; + acquire_spinlock(&lock); + // Check if we really had an interrupt uint16 status = ReadReg16(UHCI_USBSTS); - if ((status & UHCI_INTERRUPT_MASK) == 0) + if ((status & UHCI_INTERRUPT_MASK) == 0) { + release_spinlock(&lock); return B_UNHANDLED_INTERRUPT; + } uint16 acknowledge = 0; + bool finishTransfers = false; + int32 result = B_HANDLED_INTERRUPT; + if (status & UHCI_USBSTS_USBINT) { TRACE(("usb_uhci: transfer finished\n")); acknowledge |= UHCI_USBSTS_USBINT; + result = B_INVOKE_SCHEDULER; + finishTransfers = true; } if (status & UHCI_USBSTS_ERRINT) { TRACE(("usb_uhci: transfer error\n")); acknowledge |= UHCI_USBSTS_ERRINT; + result = B_INVOKE_SCHEDULER; + finishTransfers = true; } if (status & UHCI_USBSTS_RESDET) { @@ -1021,8 +1027,12 @@ UHCI::Interrupt() if (acknowledge) WriteReg16(UHCI_USBSTS, acknowledge); - fFinishTransfers = true; - return B_HANDLED_INTERRUPT; + release_spinlock(&lock); + + if (finishTransfers) + release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE); + + return result; } diff --git a/src/add-ons/kernel/busses/usb/uhci.h b/src/add-ons/kernel/busses/usb/uhci.h index 95eb9abee0..bc24cb7bd2 100644 --- a/src/add-ons/kernel/busses/usb/uhci.h +++ b/src/add-ons/kernel/busses/usb/uhci.h @@ -148,7 +148,7 @@ static pci_module_info *sPCIModule; // Maintain a linked list of transfers transfer_data *fFirstTransfer; transfer_data *fLastTransfer; - bool fFinishTransfers; + sem_id fFinishTransfersSem; thread_id fFinishThread; bool fStopFinishThread;