From 016ce5dd542c3ef0731dc3d7f3b7ab29603b04e8 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sun, 18 May 2008 21:15:07 +0000 Subject: [PATCH] * Handle the data == first and last == first case as this can occure for single td bulk transfers. * Implement SubmitTransfer() for bulk transfers (interrupts will use the exact same code path, but as their endpoints aren't yet setup, interrupt transfers won't work). * Handle the cancel case when finishing transfers. The descriptors of the canceled transfers cannot be accessed by the controller so they can be freed. Bulk transfers should work now, so devices only using control and bulk transfers should too (anything using usb_disk for example). Note though that a transfer error will cause the whole thing to fail miserably as the error case is not yet handled correctly (failed descriptors aren't removed from the endpoint). Therefore I suggest not testing with the memory stick with that important presentation you haven't stored anywhere else... git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25549 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/busses/usb/ohci.cpp | 96 ++++++++++++++++++++++---- src/add-ons/kernel/busses/usb/ohci.h | 1 + 2 files changed, 82 insertions(+), 15 deletions(-) diff --git a/src/add-ons/kernel/busses/usb/ohci.cpp b/src/add-ons/kernel/busses/usb/ohci.cpp index c6519a4292..6da3a26a79 100644 --- a/src/add-ons/kernel/busses/usb/ohci.cpp +++ b/src/add-ons/kernel/busses/usb/ohci.cpp @@ -763,8 +763,9 @@ OHCI::_Interrupt() status_t OHCI::_AddPendingTransfer(Transfer *transfer, - ohci_endpoint_descriptor *endpoint, ohci_general_td *dataDescriptor, - ohci_general_td *lastDescriptor, bool directionIn) + ohci_endpoint_descriptor *endpoint, ohci_general_td *firstDescriptor, + ohci_general_td *dataDescriptor, ohci_general_td *lastDescriptor, + bool directionIn) { if (!transfer || !endpoint || !lastDescriptor) return B_BAD_VALUE; @@ -781,14 +782,25 @@ OHCI::_AddPendingTransfer(Transfer *transfer, data->transfer = transfer; data->endpoint = endpoint; - // the current tail will become the fist descriptor - data->first_descriptor = (ohci_general_td *)endpoint->tail_logical_descriptor; - data->data_descriptor = dataDescriptor; - data->last_descriptor = lastDescriptor; data->incoming = directionIn; data->canceled = false; data->link = NULL; + // the current tail will become the fist descriptor + data->first_descriptor = (ohci_general_td *)endpoint->tail_logical_descriptor; + + // the data and first descriptors might be the same + if (dataDescriptor == firstDescriptor) + data->data_descriptor = data->first_descriptor; + else + data->data_descriptor = dataDescriptor; + + // even the last and the first descriptor might be the same + if (lastDescriptor == firstDescriptor) + data->last_descriptor = data->first_descriptor; + else + data->last_descriptor = lastDescriptor; + if (!Lock()) { delete data; return B_ERROR; @@ -849,7 +861,7 @@ OHCI::_FinishTransfers() ohci_general_td *descriptor = transfer->first_descriptor; status_t callbackStatus = B_OK; - while (descriptor) { + while (descriptor && !transfer->canceled) { uint32 status = OHCI_TD_GET_CONDITION_CODE(descriptor->flags); if (status == OHCI_TD_CONDITION_NOT_ACCESSED) { // td is still active @@ -938,6 +950,15 @@ OHCI::_FinishTransfers() = (ohci_general_td *)descriptor->next_logical_descriptor; } + if (transfer->canceled) { + // when a transfer is canceled, all transfers to that endpoint + // are canceled by setting the head pointer to the tail pointer + // which causes all of the tds to become "free" (as they are + // inaccessible and not accessed anymore (as setting the head + // pointer required disabling the endpoint)) + transferDone = true; + } + if (!transferDone) { lastTransfer = transfer; transfer = transfer->link; @@ -1075,18 +1096,18 @@ OHCI::_SubmitRequest(Transfer *transfer) _LinkDescriptors(setupDescriptor, statusDescriptor); } - // Append Transfer + // Add to the transfer list ohci_endpoint_descriptor *endpoint = (ohci_endpoint_descriptor *)transfer->TransferPipe()->ControllerCookie(); - result = _AddPendingTransfer(transfer, endpoint, dataDescriptor, - statusDescriptor, directionIn); + result = _AddPendingTransfer(transfer, endpoint, setupDescriptor, + dataDescriptor, statusDescriptor, directionIn); if (result < B_OK) { TRACE_ERROR(("usb_ohci: failed to add pending transfer\n")); _FreeDescriptorChain(setupDescriptor); return result; } - // Append descriptor chain to the endpoint + // Add the descriptor chain to the endpoint _SwitchEndpointTail(endpoint, setupDescriptor, statusDescriptor); // Tell the controller to process the control list @@ -1099,8 +1120,49 @@ OHCI::_SubmitRequest(Transfer *transfer) status_t OHCI::_SubmitTransfer(Transfer *transfer) { - // TODO - return B_ERROR; + Pipe *pipe = transfer->TransferPipe(); + bool directionIn = (pipe->Direction() == Pipe::In); + + ohci_general_td *firstDescriptor = NULL; + ohci_general_td *lastDescriptor = NULL; + status_t result = _CreateDescriptorChain(&firstDescriptor, &lastDescriptor, + directionIn ? OHCI_TD_DIRECTION_PID_IN : OHCI_TD_DIRECTION_PID_OUT, + transfer->VectorLength()); + + if (result < B_OK) + return result; + + // Set the last descriptor to generate an interrupt + lastDescriptor->flags &= ~OHCI_TD_INTERRUPT_MASK; + lastDescriptor->flags |= + OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE); + + if (!directionIn) { + _WriteDescriptorChain(firstDescriptor, transfer->Vector(), + transfer->VectorCount()); + } + + // Add to the transfer list + ohci_endpoint_descriptor *endpoint + = (ohci_endpoint_descriptor *)pipe->ControllerCookie(); + result = _AddPendingTransfer(transfer, endpoint, firstDescriptor, + firstDescriptor, lastDescriptor, directionIn); + if (result < B_OK) { + TRACE_ERROR(("usb_ohci: failed to add pending transfer\n")); + _FreeDescriptorChain(firstDescriptor); + return result; + } + + // Add the descriptor chain to the endpoint + _SwitchEndpointTail(endpoint, firstDescriptor, lastDescriptor); + endpoint->flags &= ~OHCI_ENDPOINT_SKIP; + + if (pipe->Type() & USB_OBJECT_BULK_PIPE) { + // Tell the controller to process the bulk list + _WriteReg(OHCI_COMMAND_STATUS, OHCI_BULK_LIST_FILLED); + } + + return B_OK; } @@ -1133,7 +1195,11 @@ OHCI::_SwitchEndpointTail(ohci_endpoint_descriptor *endpoint, first->buffer_size = 0; first->buffer_logical = NULL; first->next_logical_descriptor = NULL; - _LinkDescriptors(last, first); + + if (first == last) + _LinkDescriptors(tail, first); + else + _LinkDescriptors(last, first); // update the endpoint tail pointer to reflect the change endpoint->tail_logical_descriptor = first; @@ -1265,7 +1331,7 @@ OHCI::_InsertEndpointForPipe(Pipe *pipe) return B_ERROR; } - // Create (necessary) dummy descriptor + // Create (necessary) tail descriptor if (pipe->Type() & USB_OBJECT_ISO_PIPE) { // Set the isochronous bit format endpoint->flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT; diff --git a/src/add-ons/kernel/busses/usb/ohci.h b/src/add-ons/kernel/busses/usb/ohci.h index 3592714bf2..f810e9cd65 100644 --- a/src/add-ons/kernel/busses/usb/ohci.h +++ b/src/add-ons/kernel/busses/usb/ohci.h @@ -64,6 +64,7 @@ static int32 _InterruptHandler(void *data); // Transfer functions status_t _AddPendingTransfer(Transfer *transfer, ohci_endpoint_descriptor *endpoint, + ohci_general_td *firstDescriptor, ohci_general_td *dataDescriptor, ohci_general_td *lastDescriptor, bool directionIn);