* Reworked transfer queuing so that each transfer is grouped with it's own queue head

* Fixed max packet size under bandwidth reclamation
* Increased the thread priority of the finisher thread so that USB input devices should be more responsible under load
* Added a comment to clarify the queue concept

The queue management should now be less complex and less error prone.
The max packet size is now set to 64 bytes which is the maximum size allowed. Transfers that caused babble errors before should now work.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19738 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2007-01-08 03:16:29 +00:00
parent 4dbea1ffa8
commit 9c8cdfd89e
3 changed files with 118 additions and 99 deletions

View File

@ -41,7 +41,7 @@ uhci_std_ops(int32 op, ...)
host_controller_info uhci_module = {
{
"busses/usb/uhci/nielx",
"busses/usb/uhci/v1",
0,
uhci_std_ops
},
@ -194,7 +194,6 @@ Queue::TerminateByStrayDescriptor()
fQueueHead->link_phy = fStrayDescriptor->this_phy;
fQueueHead->link_log = fStrayDescriptor;
fQueueHead->element_phy = QH_TERMINATE;
Unlock();
return B_OK;
@ -202,33 +201,26 @@ Queue::TerminateByStrayDescriptor()
status_t
Queue::AppendDescriptorChain(uhci_td *descriptor)
Queue::AppendTransfer(uhci_qh *transfer)
{
TRACE(("usb_uhci: appending descriptor chain\n"));
if (!Lock())
return B_ERROR;
#ifdef TRACE_USB
print_descriptor_chain(descriptor);
#endif
transfer->link_log = NULL;
transfer->link_phy = fQueueHead->link_phy;
if (fQueueTop == NULL) {
// the queue is empty, make this the first element
fQueueTop = descriptor;
fQueueHead->element_phy = descriptor->this_phy;
TRACE(("usb_uhci: first transfer in queue\n"));
if (!fQueueTop) {
// the list is empty, make this the first element
fQueueTop = transfer;
fQueueHead->element_phy = transfer->this_phy | QH_NEXT_IS_QH;
} else {
// there are transfers linked, append to the queue
uhci_td *element = fQueueTop;
while ((element->link_phy & TD_TERMINATE) == 0)
element = (uhci_td *)element->link_log;
// append the transfer queue to the list
uhci_qh *element = fQueueTop;
while (element && element->link_log)
element = (uhci_qh *)element->link_log;
element->link_log = descriptor;
element->link_phy = descriptor->this_phy | TD_DEPTH_FIRST;
if (fQueueHead->element_phy & QH_TERMINATE)
fQueueHead->element_phy = descriptor->this_phy;
TRACE(("usb_uhci: appended transfer to queue\n"));
element->link_log = transfer;
element->link_phy = transfer->this_phy | QH_NEXT_IS_QH;
}
Unlock();
@ -237,56 +229,40 @@ Queue::AppendDescriptorChain(uhci_td *descriptor)
status_t
Queue::RemoveDescriptorChain(uhci_td *firstDescriptor, uhci_td *lastDescriptor)
Queue::RemoveTransfer(uhci_qh *transfer)
{
TRACE(("usb_uhci: removing descriptor chain\n"));
if (!Lock())
return B_ERROR;
if (fQueueTop == firstDescriptor) {
// it is the first chain in this queue
if ((lastDescriptor->link_phy & TD_TERMINATE) > 0) {
// it is the only chain in this queue
fQueueTop = NULL;
if (fQueueTop == transfer) {
// this was the top element
fQueueTop = (uhci_qh *)transfer->link_log;
if (!fQueueTop) {
// this was the only element, terminate this queue
fQueueHead->element_phy = QH_TERMINATE;
} else {
// there are still linked transfers
fQueueTop = (uhci_td *)lastDescriptor->link_log;
// there are elements left, adjust the element pointer
fQueueHead->element_phy = transfer->link_phy;
}
Unlock();
return B_OK;
} else {
// unlink the chain
uhci_td *element = fQueueTop;
uhci_qh *element = fQueueTop;
while (element) {
if (element->link_log == firstDescriptor) {
element->link_log = lastDescriptor->link_log;
element->link_phy = lastDescriptor->link_phy;
break;
if (element->link_log == transfer) {
element->link_log = transfer->link_log;
element->link_phy = transfer->link_phy;
Unlock();
return B_OK;
}
element = (uhci_td *)element->link_log;
element = (uhci_qh *)element->link_log;
}
}
uhci_td *element = firstDescriptor;
while (element && element != lastDescriptor->link_log) {
if ((fQueueHead->element_phy & TD_LINK_MASK) == element->this_phy) {
fQueueHead->element_phy = lastDescriptor->link_phy;
TRACE_ERROR(("uhci: queue element pointer still pointing to removed chain\n"));
break;
}
element = (uhci_td *)element->link_log;
}
lastDescriptor->link_log = NULL;
lastDescriptor->link_phy = TD_TERMINATE;
#ifdef TRACE_USB
print_descriptor_chain(firstDescriptor);
#endif
Unlock();
return B_OK;
return B_BAD_VALUE;
}
@ -385,6 +361,14 @@ UHCI::UHCI(pci_info *info, Stack *stack)
WriteReg32(UHCI_FRBASEADD, (uint32)physicalAddress);
WriteReg16(UHCI_FRNUM, 0);
// Set the max packet size for bandwidth reclamation to 64 bytes
WriteReg16(UHCI_USBCMD, ReadReg16(UHCI_USBCMD) | UHCI_USBCMD_MAXP);
// we will create four queues:
// 0: interrupt transfers
// 1: low speed control transfers
// 2: full speed control transfers
// 3: bulk transfers
fQueueCount = 4;
fQueues = new(std::nothrow) Queue *[fQueueCount];
if (!fQueues) {
@ -419,7 +403,7 @@ UHCI::UHCI(pci_info *info, Stack *stack)
// Create the finisher service thread
fFinishThread = spawn_kernel_thread(FinishThread,
"uhci finish thread", B_NORMAL_PRIORITY, (void *)this);
"uhci finish thread", B_URGENT_DISPLAY_PRIORITY, (void *)this);
resume_thread(fFinishThread);
// Install the interrupt handler
@ -547,22 +531,22 @@ UHCI::SubmitTransfer(Transfer *transfer)
transfer->VectorCount());
}
Queue *queue = fQueues[3];
if (pipe->Type() & USB_OBJECT_INTERRUPT_PIPE)
queue = fQueues[2];
Queue *queue = NULL;
if (pipe->Type() & USB_OBJECT_INTERRUPT_PIPE) {
// use interrupt queue
queue = fQueues[0];
} else {
// use bulk queue
queue = fQueues[3];
}
result = AddPendingTransfer(transfer, queue, firstDescriptor,
firstDescriptor, lastDescriptor, directionIn);
uhci_qh *transferQueue = CreateTransferQueue(firstDescriptor);
result = AddPendingTransfer(transfer, queue, transferQueue,
firstDescriptor, firstDescriptor, directionIn);
if (result < B_OK) {
TRACE_ERROR(("usb_uhci: failed to add pending transfer\n"));
FreeDescriptorChain(firstDescriptor);
return result;
}
result = queue->AppendDescriptorChain(firstDescriptor);
if (result < B_OK) {
TRACE_ERROR(("usb_uhci: failed to append descriptor chain\n"));
FreeDescriptorChain(firstDescriptor);
FreeTransferQueue(transferQueue);
return result;
}
@ -625,18 +609,22 @@ UHCI::SubmitRequest(Transfer *transfer)
LinkDescriptors(setupDescriptor, statusDescriptor);
}
status_t result = AddPendingTransfer(transfer, fQueues[1], setupDescriptor,
dataDescriptor, statusDescriptor, directionIn);
Queue *queue = NULL;
if (pipe->Speed() == USB_SPEED_LOWSPEED) {
// use the low speed control queue
queue = fQueues[1];
} else {
// use the full speed control queue
queue = fQueues[2];
}
uhci_qh *transferQueue = CreateTransferQueue(setupDescriptor);
status_t result = AddPendingTransfer(transfer, queue, transferQueue,
setupDescriptor, dataDescriptor, directionIn);
if (result < B_OK) {
TRACE_ERROR(("usb_uhci: failed to add pending transfer\n"));
FreeDescriptorChain(setupDescriptor);
return result;
}
result = fQueues[1]->AppendDescriptorChain(setupDescriptor);
if (result < B_OK) {
TRACE_ERROR(("usb_uhci: failed to append descriptor chain\n"));
FreeDescriptorChain(setupDescriptor);
FreeTransferQueue(transferQueue);
return result;
}
@ -646,18 +634,21 @@ UHCI::SubmitRequest(Transfer *transfer)
status_t
UHCI::AddPendingTransfer(Transfer *transfer, Queue *queue,
uhci_td *firstDescriptor, uhci_td *dataDescriptor, uhci_td *lastDescriptor,
uhci_qh *transferQueue, uhci_td *firstDescriptor, uhci_td *dataDescriptor,
bool directionIn)
{
if (!transfer || !queue || !transferQueue || !firstDescriptor)
return B_BAD_VALUE;
transfer_data *data = new(std::nothrow) transfer_data();
if (!data)
return B_NO_MEMORY;
data->transfer = transfer;
data->queue = queue;
data->transfer_queue = transferQueue;
data->first_descriptor = firstDescriptor;
data->data_descriptor = dataDescriptor;
data->last_descriptor = lastDescriptor;
data->user_area = -1;
data->incoming = directionIn;
data->link = NULL;
@ -718,6 +709,7 @@ UHCI::AddPendingTransfer(Transfer *transfer, Queue *queue,
fLastTransfer = data;
Unlock();
queue->AppendTransfer(data->transfer_queue);
return B_OK;
}
@ -764,7 +756,7 @@ UHCI::FinishTransfers()
}
if (status & TD_ERROR_MASK) {
TRACE_ERROR(("usb_uhci: td (0x%08lx) error: 0x%08lx\n", descriptor->this_phy, status));
TRACE_ERROR(("usb_uhci: td (0x%08lx) error: status: 0x%08lx; token: 0x%08lx;\n", descriptor->this_phy, status, descriptor->token));
// an error occured. we have to remove the
// transfer from the queue and clean up
@ -803,25 +795,21 @@ UHCI::FinishTransfers()
callbackStatus = B_DEV_STALLED;
}
transfer->queue->RemoveDescriptorChain(
transfer->first_descriptor,
transfer->last_descriptor);
transfer->queue->RemoveTransfer(transfer->transfer_queue);
FreeDescriptorChain(transfer->first_descriptor);
FreeTransferQueue(transfer->transfer_queue);
transfer->transfer->Finished(callbackStatus, 0);
transferDone = true;
break;
}
// either all descriptors are done, or we have a short packet
if (descriptor == transfer->last_descriptor
if ((descriptor->link_phy & TD_TERMINATE)
|| (descriptor->status & TD_STATUS_ACTLEN_MASK)
< (descriptor->token >> TD_TOKEN_MAXLEN_SHIFT)) {
TRACE(("usb_uhci: td (0x%08lx) ok\n", descriptor->this_phy));
// we got through without errors so we are finished
transfer->queue->RemoveDescriptorChain(
transfer->first_descriptor,
transfer->last_descriptor);
transfer->queue->RemoveTransfer(transfer->transfer_queue);
size_t actualLength = 0;
uint8 lastDataToggle = 0;
@ -863,6 +851,7 @@ UHCI::FinishTransfers()
}
FreeDescriptorChain(transfer->first_descriptor);
FreeTransferQueue(transfer->transfer_queue);
transfer->transfer->TransferPipe()->SetDataToggle(lastDataToggle == 0);
transfer->transfer->Finished(B_OK, actualLength);
transferDone = true;
@ -1206,6 +1195,31 @@ UHCI::AddTo(Stack *stack)
}
uhci_qh *
UHCI::CreateTransferQueue(uhci_td *descriptor)
{
uhci_qh *queueHead;
void *physicalAddress;
if (fStack->AllocateChunk((void **)&queueHead,
&physicalAddress, sizeof(uhci_qh)) < B_OK)
return NULL;
queueHead->this_phy = (addr_t)physicalAddress;
queueHead->element_phy = descriptor->this_phy;
return queueHead;
}
void
UHCI::FreeTransferQueue(uhci_qh *queueHead)
{
if (!queueHead)
return;
fStack->FreeChunk(queueHead, (void *)queueHead->this_phy, sizeof(uhci_qh));
}
uhci_td *
UHCI::CreateDescriptor(Pipe *pipe, uint8 direction, size_t bufferSize)
{
@ -1219,7 +1233,9 @@ UHCI::CreateDescriptor(Pipe *pipe, uint8 direction, size_t bufferSize)
}
result->this_phy = (addr_t)physicalAddress;
result->status = TD_STATUS_ACTIVE | TD_CONTROL_3_ERRORS | TD_CONTROL_SPD;
result->status = TD_STATUS_ACTIVE | TD_CONTROL_3_ERRORS;
if (direction == TD_TOKEN_IN)
result->status |= TD_CONTROL_SPD;
if (pipe->Speed() == USB_SPEED_LOWSPEED)
result->status |= TD_CONTROL_LOWSPEED;

View File

@ -32,10 +32,8 @@ public:
status_t LinkTo(Queue *other);
status_t TerminateByStrayDescriptor();
status_t AppendDescriptorChain(uhci_td *descriptor);
status_t RemoveDescriptorChain(
uhci_td *firstDescriptor,
uhci_td *lastDescriptor);
status_t AppendTransfer(uhci_qh *transfer);
status_t RemoveTransfer(uhci_qh *transfer);
addr_t PhysicalAddress();
@ -46,7 +44,7 @@ private:
Stack *fStack;
uhci_qh *fQueueHead;
uhci_td *fStrayDescriptor;
uhci_td *fQueueTop;
uhci_qh *fQueueTop;
benaphore fLock;
};
@ -54,9 +52,9 @@ private:
typedef struct transfer_data_s {
Transfer *transfer;
Queue *queue;
uhci_qh *transfer_queue;
uhci_td *first_descriptor;
uhci_td *data_descriptor;
uhci_td *last_descriptor;
area_id user_area;
bool incoming;
transfer_data_s *link;
@ -93,13 +91,17 @@ static int32 InterruptHandler(void *data);
// Transfer functions
status_t AddPendingTransfer(Transfer *transfer,
Queue *queue,
uhci_qh *transferQueue,
uhci_td *firstDescriptor,
uhci_td *dataDescriptor,
uhci_td *lastDescriptor,
bool directionIn);
static int32 FinishThread(void *data);
void FinishTransfers();
// Transfer queue functions
uhci_qh *CreateTransferQueue(uhci_td *descriptor);
void FreeTransferQueue(uhci_qh *queueHead);
// Descriptor functions
uhci_td *CreateDescriptor(Pipe *pipe,
uint8 direction,

View File

@ -40,6 +40,7 @@
#define UHCI_USBCMD_FGR 0x10 // Force Global resume
#define UHCI_USBCMD_SWDBG 0x20 // Software Debug
#define UHCI_USBCMD_CF 0x40 // Configure Flag
#define UHCI_USBCMD_MAXP 0x80 // Max packet
//USBSTS
#define UHCI_USBSTS_USBINT 0x1 // USB interrupt