* 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 = {
{
"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) {

View File

@ -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;

View File

@ -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 ,

View File

@ -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;
}

View File

@ -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;