* 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:
parent
6f52af5f5c
commit
bcc2d1b69a
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 ,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user