* 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
This commit is contained in:
Michael Lotz 2006-09-16 00:06:04 +00:00
parent 6f52af5f5c
commit bcc2d1b69a
5 changed files with 90 additions and 62 deletions

View File

@ -35,7 +35,7 @@ ehci_std_ops(int32 op, ...)
host_controller_info ehci_module = { host_controller_info ehci_module = {
{ {
"busses/usb/ehci", "busses/usb/ehci",
NULL, 0,
ehci_std_ops ehci_std_ops
}, },
NULL, NULL,
@ -104,7 +104,6 @@ EHCI::EHCI(pci_info *info, Stack *stack)
fPeriodicFrameList(NULL), fPeriodicFrameList(NULL),
fFirstTransfer(NULL), fFirstTransfer(NULL),
fLastTransfer(NULL), fLastTransfer(NULL),
fFinishTransfers(false),
fFinishThread(-1), fFinishThread(-1),
fStopFinishThread(false), fStopFinishThread(false),
fRootHub(NULL), fRootHub(NULL),
@ -209,6 +208,14 @@ EHCI::EHCI(pci_info *info, Stack *stack)
return; 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 // create finisher service thread
fFinishThread = spawn_kernel_thread(FinishThread, "ehci finish thread", fFinishThread = spawn_kernel_thread(FinishThread, "ehci finish thread",
B_NORMAL_PRIORITY, (void *)this); B_NORMAL_PRIORITY, (void *)this);
@ -262,13 +269,15 @@ EHCI::~EHCI()
{ {
TRACE(("usb_ehci: tear down EHCI Host Controller Driver\n")); 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_USBCMD, 0);
WriteOpReg(EHCI_CONFIGFLAG, 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 fRootHub;
delete_area(fPeriodicFrameListArea); 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) { if (!running) {
TRACE(("usb_ehci: Host Controller didn't start\n")); TRACE(("usb_ehci: Host Controller didn't start\n"));
return B_ERROR; return B_ERROR;
@ -709,37 +722,40 @@ EHCI::LightReset()
int32 int32
EHCI::InterruptHandler(void *data) EHCI::InterruptHandler(void *data)
{ {
cpu_status status = disable_interrupts(); return ((EHCI *)data)->Interrupt();
spinlock lock = 0;
acquire_spinlock(&lock);
int32 result = ((EHCI *)data)->Interrupt();
release_spinlock(&lock);
restore_interrupts(status);
return result;
} }
int32 int32
EHCI::Interrupt() EHCI::Interrupt()
{ {
spinlock lock = 0;
acquire_spinlock(&lock);
// check if any interrupt was generated // check if any interrupt was generated
uint32 status = ReadOpReg(EHCI_USBSTS); uint32 status = ReadOpReg(EHCI_USBSTS);
if ((status & EHCI_USBSTS_INTMASK) == 0) if ((status & EHCI_USBSTS_INTMASK) == 0) {
release_spinlock(&lock);
return B_UNHANDLED_INTERRUPT; return B_UNHANDLED_INTERRUPT;
}
uint32 acknowledge = 0; uint32 acknowledge = 0;
bool asyncAdvance = false;
bool finishTransfers = false;
int32 result = B_HANDLED_INTERRUPT;
if (status & EHCI_USBSTS_USBINT) { if (status & EHCI_USBSTS_USBINT) {
TRACE(("usb_ehci: transfer finished\n")); TRACE(("usb_ehci: transfer finished\n"));
acknowledge |= EHCI_USBSTS_USBINT; acknowledge |= EHCI_USBSTS_USBINT;
fFinishTransfers = true; result = B_INVOKE_SCHEDULER;
finishTransfers = true;
} }
if (status & EHCI_USBSTS_USBERRINT) { if (status & EHCI_USBSTS_USBERRINT) {
TRACE(("usb_ehci: transfer error\n")); TRACE(("usb_ehci: transfer error\n"));
acknowledge |= EHCI_USBSTS_USBERRINT; acknowledge |= EHCI_USBSTS_USBERRINT;
fFinishTransfers = true; result = B_INVOKE_SCHEDULER;
finishTransfers = true;
} }
if (status & EHCI_USBSTS_PORTCHANGE) { if (status & EHCI_USBSTS_PORTCHANGE) {
@ -755,7 +771,8 @@ EHCI::Interrupt()
if (status & EHCI_USBSTS_INTONAA) { if (status & EHCI_USBSTS_INTONAA) {
TRACE(("usb_ehci: interrupt on async advance\n")); TRACE(("usb_ehci: interrupt on async advance\n"));
acknowledge |= EHCI_USBSTS_INTONAA; acknowledge |= EHCI_USBSTS_INTONAA;
fAsyncAdvance = true; asyncAdvance = true;
result = B_INVOKE_SCHEDULER;
} }
if (status & EHCI_USBSTS_HOSTSYSERR) { if (status & EHCI_USBSTS_HOSTSYSERR) {
@ -766,7 +783,14 @@ EHCI::Interrupt()
if (acknowledge) if (acknowledge)
WriteOpReg(EHCI_USBSTS, 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() EHCI::FinishTransfers()
{ {
while (!fStopFinishThread) { while (!fStopFinishThread) {
while (!fFinishTransfers) { if (acquire_sem(fFinishTransfersSem) < B_OK)
if (fStopFinishThread) continue;
return;
snooze(1000);
}
fFinishTransfers = false;
if (!Lock()) if (!Lock())
continue; continue;
@ -977,16 +997,14 @@ EHCI::FinishTransfers()
} }
if (freeListHead) { if (freeListHead) {
fAsyncAdvance = false;
// set the doorbell and wait for the host controller to notify us // set the doorbell and wait for the host controller to notify us
WriteOpReg(EHCI_USBCMD, ReadOpReg(EHCI_USBCMD) | EHCI_USBCMD_INTONAAD); WriteOpReg(EHCI_USBCMD, ReadOpReg(EHCI_USBCMD) | EHCI_USBCMD_INTONAAD);
while (!fAsyncAdvance) if (acquire_sem_etc(fAsyncAdvanceSem, 1, B_RELATIVE_TIMEOUT, 1000) == B_OK) {
snooze(10); while (freeListHead) {
ehci_qh *next = (ehci_qh *)freeListHead->next_log;
while (freeListHead) { FreeQueueHead(freeListHead);
ehci_qh *next = (ehci_qh *)freeListHead->next_log; freeListHead = next;
FreeQueueHead(freeListHead); }
freeListHead = next;
} }
} }
} }
@ -1039,6 +1057,7 @@ EHCI::FreeQueueHead(ehci_qh *queueHead)
return; return;
FreeDescriptorChain((ehci_qtd *)queueHead->element_log); FreeDescriptorChain((ehci_qtd *)queueHead->element_log);
FreeDescriptor((ehci_qtd *)queueHead->stray_log);
fStack->FreeChunk(queueHead, (void *)queueHead->this_phy, sizeof(ehci_qh)); 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; ehci_qtd *strayDescriptor = (ehci_qtd *)queueHead->stray_log;
statusDescriptor->token |= EHCI_QTD_IOC | EHCI_QTD_DATA_TOGGLE; statusDescriptor->token |= EHCI_QTD_IOC | EHCI_QTD_DATA_TOGGLE;
LinkDescriptors(statusDescriptor, strayDescriptor, strayDescriptor);
ehci_qtd *dataDescriptor = NULL; ehci_qtd *dataDescriptor = NULL;
if (transfer->VectorCount() > 0) { if (transfer->VectorCount() > 0) {

View File

@ -131,14 +131,14 @@ static pci_module_info *sPCIModule;
area_id fPeriodicFrameListArea; area_id fPeriodicFrameListArea;
addr_t *fPeriodicFrameList; addr_t *fPeriodicFrameList;
// Async frame list // Async frame list management
ehci_qh *fAsyncQueueHead; ehci_qh *fAsyncQueueHead;
bool fAsyncAdvance; sem_id fAsyncAdvanceSem;
// Maintain a linked list of transfers // Maintain a linked list of transfers
transfer_data *fFirstTransfer; transfer_data *fFirstTransfer;
transfer_data *fLastTransfer; transfer_data *fLastTransfer;
bool fFinishTransfers; sem_id fFinishTransfersSem;
thread_id fFinishThread; thread_id fFinishThread;
bool fStopFinishThread; bool fStopFinishThread;

View File

@ -152,7 +152,7 @@ ohci_add_to(Stack *stack)
host_controller_info ohci_module = { host_controller_info ohci_module = {
{ {
"busses/usb/ohci", "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 ohci_std_ops
}, },
NULL , NULL ,

View File

@ -42,7 +42,7 @@ uhci_std_ops(int32 op, ...)
host_controller_info uhci_module = { host_controller_info uhci_module = {
{ {
"busses/usb/uhci/nielx", "busses/usb/uhci/nielx",
NULL, 0,
uhci_std_ops uhci_std_ops
}, },
NULL, NULL,
@ -324,7 +324,6 @@ UHCI::UHCI(pci_info *info, Stack *stack)
fQueues(NULL), fQueues(NULL),
fFirstTransfer(NULL), fFirstTransfer(NULL),
fLastTransfer(NULL), fLastTransfer(NULL),
fFinishTransfers(false),
fFinishThread(-1), fFinishThread(-1),
fStopFinishThread(false), fStopFinishThread(false),
fRootHub(NULL), fRootHub(NULL),
@ -410,6 +409,13 @@ UHCI::UHCI(pci_info *info, Stack *stack)
for (int32 i = 0; i < 1024; i++) for (int32 i = 0; i < 1024; i++)
fFrameList[i] = fQueues[0]->PhysicalAddress() | FRAMELIST_NEXT_IS_QH; 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 // Create the finisher service thread
fFinishThread = spawn_kernel_thread(FinishThread, fFinishThread = spawn_kernel_thread(FinishThread,
"uhci finish thread", B_NORMAL_PRIORITY, (void *)this); "uhci finish thread", B_NORMAL_PRIORITY, (void *)this);
@ -433,6 +439,7 @@ UHCI::~UHCI()
{ {
int32 result = 0; int32 result = 0;
fStopFinishThread = true; fStopFinishThread = true;
delete_sem(fFinishTransfersSem);
wait_for_thread(fFinishThread, &result); wait_for_thread(fFinishThread, &result);
Lock(); Lock();
@ -686,14 +693,9 @@ void
UHCI::FinishTransfers() UHCI::FinishTransfers()
{ {
while (!fStopFinishThread) { while (!fStopFinishThread) {
while (!fFinishTransfers) { if (acquire_sem(fFinishTransfersSem) < B_OK)
if (fStopFinishThread) continue;
return;
snooze(1000);
}
fFinishTransfers = false;
if (!Lock()) if (!Lock())
continue; continue;
@ -966,35 +968,39 @@ UHCI::ResetPort(uint8 index)
int32 int32
UHCI::InterruptHandler(void *data) UHCI::InterruptHandler(void *data)
{ {
cpu_status status = disable_interrupts(); return ((UHCI *)data)->Interrupt();
spinlock lock = 0;
acquire_spinlock(&lock);
int32 result = ((UHCI *)data)->Interrupt();
release_spinlock(&lock);
restore_interrupts(status);
return result;
} }
int32 int32
UHCI::Interrupt() UHCI::Interrupt()
{ {
spinlock lock = 0;
acquire_spinlock(&lock);
// Check if we really had an interrupt // Check if we really had an interrupt
uint16 status = ReadReg16(UHCI_USBSTS); uint16 status = ReadReg16(UHCI_USBSTS);
if ((status & UHCI_INTERRUPT_MASK) == 0) if ((status & UHCI_INTERRUPT_MASK) == 0) {
release_spinlock(&lock);
return B_UNHANDLED_INTERRUPT; return B_UNHANDLED_INTERRUPT;
}
uint16 acknowledge = 0; uint16 acknowledge = 0;
bool finishTransfers = false;
int32 result = B_HANDLED_INTERRUPT;
if (status & UHCI_USBSTS_USBINT) { if (status & UHCI_USBSTS_USBINT) {
TRACE(("usb_uhci: transfer finished\n")); TRACE(("usb_uhci: transfer finished\n"));
acknowledge |= UHCI_USBSTS_USBINT; acknowledge |= UHCI_USBSTS_USBINT;
result = B_INVOKE_SCHEDULER;
finishTransfers = true;
} }
if (status & UHCI_USBSTS_ERRINT) { if (status & UHCI_USBSTS_ERRINT) {
TRACE(("usb_uhci: transfer error\n")); TRACE(("usb_uhci: transfer error\n"));
acknowledge |= UHCI_USBSTS_ERRINT; acknowledge |= UHCI_USBSTS_ERRINT;
result = B_INVOKE_SCHEDULER;
finishTransfers = true;
} }
if (status & UHCI_USBSTS_RESDET) { if (status & UHCI_USBSTS_RESDET) {
@ -1021,8 +1027,12 @@ UHCI::Interrupt()
if (acknowledge) if (acknowledge)
WriteReg16(UHCI_USBSTS, acknowledge); WriteReg16(UHCI_USBSTS, acknowledge);
fFinishTransfers = true; release_spinlock(&lock);
return B_HANDLED_INTERRUPT;
if (finishTransfers)
release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE);
return result;
} }

View File

@ -148,7 +148,7 @@ static pci_module_info *sPCIModule;
// Maintain a linked list of transfers // Maintain a linked list of transfers
transfer_data *fFirstTransfer; transfer_data *fFirstTransfer;
transfer_data *fLastTransfer; transfer_data *fLastTransfer;
bool fFinishTransfers; sem_id fFinishTransfersSem;
thread_id fFinishThread; thread_id fFinishThread;
bool fStopFinishThread; bool fStopFinishThread;