diff --git a/src/add-ons/kernel/bus_managers/usb/BusManager.cpp b/src/add-ons/kernel/bus_managers/usb/BusManager.cpp index 26dee10af7..7bbeb82046 100644 --- a/src/add-ons/kernel/bus_managers/usb/BusManager.cpp +++ b/src/add-ons/kernel/bus_managers/usb/BusManager.cpp @@ -11,7 +11,7 @@ #define TRACE_BUSMANAGER #ifdef TRACE_BUSMANAGER -#define TRACE(x) dprintf(x) +#define TRACE(x) dprintf x #else #define TRACE(x) /* nothing */ #endif @@ -88,6 +88,8 @@ BusManager::AllocateNewDevice(Device *parent, bool lowSpeed) return NULL; } + TRACE(("usb BusManager::AllocateNewDevice(): setting device address to %d\n", deviceNum)); + // Set the address of the device USB 1.1 spec p202 status_t result = fDefaultPipeLowSpeed->SendRequest( USB_REQTYPE_DEVICE_OUT | USB_REQTYPE_STANDARD, // type @@ -104,17 +106,20 @@ BusManager::AllocateNewDevice(Device *parent, bool lowSpeed) return NULL; } - // Create a temporary pipe + // Wait a bit for the device to complete addressing + snooze(10000); + + // Create a temporary pipe with the new address ControlPipe pipe(this, deviceNum, Pipe::Default, Pipe::LowSpeed, 0); // Get the device descriptor // Just retrieve the first 8 bytes of the descriptor -> minimum supported - // size of any device, plus it is enough, because the device type is in - // there too + // size of any device. It is enough because it includes the device type. size_t actualLength = 0; usb_device_descriptor deviceDescriptor; + TRACE(("usb BusManager::AllocateNewDevice(): getting the device descriptor\n")); pipe.SendRequest( USB_REQTYPE_DEVICE_IN | USB_REQTYPE_STANDARD, // type USB_REQUEST_GET_DESCRIPTOR, // request diff --git a/src/add-ons/kernel/busses/usb/uhci.cpp b/src/add-ons/kernel/busses/usb/uhci.cpp index 484c42f14c..1efdd6b3ab 100644 --- a/src/add-ons/kernel/busses/usb/uhci.cpp +++ b/src/add-ons/kernel/busses/usb/uhci.cpp @@ -68,6 +68,151 @@ module_info *modules[] = { // +uhci_td * +new_descriptor(Stack *stack, Pipe *pipe, uint8 type, int32 bufferSize, + void *buffer) +{ + uhci_td *result; + void *physicalAddress; + + if (stack->AllocateChunk((void **)&result, &physicalAddress, 32) < B_OK) { + TRACE(("usb_uhci: failed to allocate a transfer descriptor\n")); + return NULL; + } + + result->this_phy = (addr_t)physicalAddress; + result->status = TD_STATUS_ACTIVE | TD_STATUS_3_ERRORS; + if (pipe->Speed() == Pipe::LowSpeed) + result->status |= TD_STATUS_LOWSPEED; + + result->buffer_size = bufferSize; + if (bufferSize == 0) + result->token = TD_TOKEN_NULL; + else + result->token = (bufferSize - 1) << 21; + + result->token |= (pipe->EndpointAddress() << 15) + | (pipe->DeviceAddress() << 8) | type; + + result->link_phy = 0; + result->link_log = NULL; + if (bufferSize <= 0) + return result; + + if (stack->AllocateChunk(&result->buffer_log, &result->buffer_phy, + bufferSize) < B_OK) { + TRACE(("usb_uhci: unable to allocate space for the request buffer\n")); + stack->FreeChunk(result, (void *)result->this_phy, 32); + return NULL; + } + + if (!buffer) + return result; + + memcpy(result->buffer_log, buffer, bufferSize); + return result; +} + + +void +link_descriptors(uhci_td *first, uhci_td *second) +{ + first->link_phy = second->this_phy | TD_DEPTH_FIRST; + first->link_log = second; +} + + +void +free_descriptor(uhci_td *descriptor, Stack *stack) +{ + if (!descriptor) + return; + + if (descriptor->buffer_log) { + stack->FreeChunk(descriptor->buffer_log, + (void *)descriptor->buffer_phy, descriptor->buffer_size); + } + + stack->FreeChunk(descriptor, (void *)descriptor->this_phy, 32); +} + + +void +free_descriptor_chain(uhci_td *topDescriptor, Stack *stack) +{ + uhci_td *current = topDescriptor; + uhci_td *next = NULL; + + while (current) { + next = (uhci_td *)current->link_log; + free_descriptor(current, stack); + current = next; + } +} + + +size_t +read_descriptor_chain(uhci_td *topDescriptor, uint8 *buffer, int32 bufferSize) +{ + TRACE(("usb_uhci: reading descriptor chain to buffer 0x%08x\n", buffer, bufferSize)); + size_t actualSize = 0; + uhci_td *current = topDescriptor; + + while (current) { + if (!current->buffer_log) + break; + + size_t length = (current->status & TD_STATUS_ACTLEN_MASK) + 1; + if (length == TD_STATUS_ACTLEN_NULL + 1) + length = 0; + + length = min_c(length, bufferSize); + memcpy(buffer, current->buffer_log, length); + + bufferSize -= length; + actualSize += length; + buffer += length; + + if (current->link_phy & TD_TERMINATE) + break; + + current = (uhci_td *)current->link_log; + } + + TRACE(("usb_uhci: read descriptor chain (%d bytes)\n", actualSize)); + return actualSize; +} + + +void +print_descriptor(uhci_td *descriptor) +{ + dprintf("ph: 0x%08x; lp: 0x%08x; vf: %s; q: %s; t: %s; st: 0x%08x; to: 0x%08x\n", + descriptor->this_phy & 0xffffffff, descriptor->link_phy & 0xfffffff0, + descriptor->link_phy & 0x4 ? "y" : "n", + descriptor->link_phy & 0x2 ? "qh" : "td", + descriptor->link_phy & 0x1 ? "y" : "n", + descriptor->status, descriptor->token); +} + + +void +print_descriptor_chain(uhci_td *descriptor) +{ + while (descriptor) { + print_descriptor(descriptor); + if (descriptor->link_phy & TD_TERMINATE) + break; + descriptor = (uhci_td *)descriptor->link_log; + } +} + + +// +// #pragma mark - +// + + Queue::Queue(Stack *stack) { fStack = stack; @@ -82,15 +227,14 @@ Queue::Queue(Stack *stack) fQueueHead->element_log = 0; fStrayDescriptor = NULL; + fQueueTop = NULL; } Queue::~Queue() { fStack->FreeChunk(fQueueHead, (void *)fQueueHead->this_phy, 32); - - if (fStrayDescriptor) - fStack->FreeChunk(fStrayDescriptor, (void *)fStrayDescriptor->this_phy, 32); + free_descriptor(fStrayDescriptor, fStack); } @@ -132,6 +276,7 @@ Queue::TerminateByStrayDescriptor() fStrayDescriptor->link_log = 0; fStrayDescriptor->buffer_phy = 0; fStrayDescriptor->buffer_log = 0; + fStrayDescriptor->buffer_size = 0; fStrayDescriptor->token = TD_TOKEN_NULL | (0x7f << TD_TOKEN_DEVADDR_SHIFT) | 0x69; @@ -147,10 +292,19 @@ Queue::TerminateByStrayDescriptor() status_t Queue::AppendDescriptor(uhci_td *descriptor) { + if (fQueueTop) + RemoveInactiveDescriptors(); + + TRACE(("usb_uhci: appending descriptors\n")); +#ifdef TRACE_UHCI + print_descriptor_chain(descriptor); +#endif + if (fQueueHead->element_phy & QH_TERMINATE) { // the queue is empty, make this the first element fQueueHead->element_phy = descriptor->this_phy; fQueueHead->element_log = descriptor; + fQueueTop = descriptor; TRACE(("usb_uhci: first transfer in queue\n")); } else { // there are transfers linked, append to the queue @@ -158,8 +312,7 @@ Queue::AppendDescriptor(uhci_td *descriptor) while ((element->link_phy & TD_TERMINATE) == 0) element = (uhci_td *)element->link_log; - element->link_phy = descriptor->this_phy; - element->link_log = descriptor; + link_descriptors(element, descriptor); TRACE(("usb_uhci: appended transfer to queue\n")); } @@ -168,96 +321,116 @@ Queue::AppendDescriptor(uhci_td *descriptor) status_t -Queue::AddTransfer(Transfer *transfer, bigtime_t timeout) +Queue::AddRequest(Transfer *transfer, bigtime_t timeout) { - // Allocate the transfer descriptor for the transfer - void *physicalAddress; - uhci_td *transferDescriptor; - if (fStack->AllocateChunk((void **)&transferDescriptor, &physicalAddress, 32) < B_OK) { - TRACE(("usb_uhci: failed to allocate a transfer descriptor\n")); + Pipe *pipe = transfer->GetPipe(); + usb_request_data *requestData = transfer->GetRequestData(); + bool directionIn = (requestData->RequestType & USB_REQTYPE_DEVICE_IN) > 0; + + uhci_td *setupDescriptor = new_descriptor(fStack, pipe, TD_TOKEN_SETUP, + sizeof(usb_request_data), requestData); + + uhci_td *statusDescriptor = new_descriptor(fStack, pipe, + directionIn ? TD_TOKEN_OUT : TD_TOKEN_IN, 0, NULL); + + if (!setupDescriptor || !statusDescriptor) { + TRACE(("usb_uhci: failed to allocate descriptors\n")); + free_descriptor(setupDescriptor, fStack); + free_descriptor(statusDescriptor, fStack); return ENOMEM; } - transferDescriptor->this_phy = (addr_t)physicalAddress; - transferDescriptor->status = TD_STATUS_ACTIVE | TD_STATUS_3_ERRORS; - if (transfer->GetPipe()->Speed() == Pipe::LowSpeed) - transferDescriptor->status |= TD_STATUS_LOWSPEED; - - transferDescriptor->token = ((sizeof(usb_request_data) - 1) << 21) - | (transfer->GetPipe()->EndpointAddress() << 15) - | (transfer->GetPipe()->DeviceAddress() << 8) | 0x2D; - - // Create a physical space for the request - if (fStack->AllocateChunk(&transferDescriptor->buffer_log, - &transferDescriptor->buffer_phy, sizeof(usb_request_data)) < B_OK) { - TRACE(("usb_uhci: unable to allocate space for the request buffer\n")); - fStack->FreeChunk(transferDescriptor, - (void *)transferDescriptor->this_phy, 32); - return ENOMEM; - } - - memcpy(transferDescriptor->buffer_log, transfer->GetRequestData(), - sizeof(usb_request_data)); - - // Create a status transfer descriptor - uhci_td *statusDescriptor; - if (fStack->AllocateChunk((void **)&statusDescriptor, &physicalAddress, 32) < B_OK) { - TRACE(("usb_uhci: failed to allocate the status descriptor\n")); - fStack->FreeChunk(transferDescriptor->buffer_log, - (void *)transferDescriptor->buffer_phy, sizeof(usb_request_data)); - fStack->FreeChunk(transferDescriptor, - (void *)transferDescriptor->this_phy, 32); - return ENOMEM; - } - - statusDescriptor->this_phy = (addr_t)physicalAddress; - statusDescriptor->status = TD_STATUS_IOC | TD_STATUS_ACTIVE | - TD_STATUS_3_ERRORS; - if (transfer->GetPipe()->Speed() == Pipe::LowSpeed) - statusDescriptor->status |= TD_STATUS_LOWSPEED; - - statusDescriptor->token = TD_TOKEN_NULL | TD_TOKEN_DATA1 - | (transfer->GetPipe()->EndpointAddress() << 15) - | (transfer->GetPipe()->DeviceAddress() << 8) | 0x2d; - - statusDescriptor->buffer_phy = statusDescriptor->buffer_log = 0; + statusDescriptor->status |= TD_STATUS_IOC; + statusDescriptor->token |= TD_TOKEN_DATA1; statusDescriptor->link_phy = QH_TERMINATE; statusDescriptor->link_log = 0; if (transfer->GetBuffer() && transfer->GetBufferLength() > 0) { - // ToDo: split up data to maxlen and create / link tds + int32 packetSize = 8; + int32 bufferLength = transfer->GetBufferLength(); + int32 descriptorCount = (bufferLength + packetSize - 1) / packetSize; + + bool dataToggle = true; + uhci_td *lastDescriptor = NULL; + for (int32 i = 0; i < descriptorCount; i++) { + uhci_td *descriptor = new_descriptor(fStack, pipe, + directionIn ? TD_TOKEN_IN : TD_TOKEN_OUT, + min_c(packetSize, bufferLength), NULL); + + if (!descriptor) { + free_descriptor_chain(setupDescriptor, fStack); + free_descriptor(statusDescriptor, fStack); + return ENOMEM; + } + + if (dataToggle) + descriptor->token |= TD_TOKEN_DATA1; + + // link to previous + if (!lastDescriptor) + link_descriptors(setupDescriptor, descriptor); + else + link_descriptors(lastDescriptor, descriptor); + + lastDescriptor = descriptor; + bufferLength -= packetSize; + dataToggle = !dataToggle; + } + + link_descriptors(lastDescriptor, statusDescriptor); } else { // Link transfer and status descriptors directly - transferDescriptor->link_phy = statusDescriptor->this_phy | TD_DEPTH_FIRST; - transferDescriptor->link_log = statusDescriptor; + link_descriptors(setupDescriptor, statusDescriptor); } - status_t result = AppendDescriptor(transferDescriptor); + status_t result = AppendDescriptor(setupDescriptor); if (result < B_OK) { TRACE(("usb_uhci: failed to append descriptors\n")); - fStack->FreeChunk(transferDescriptor->buffer_log, - (void *)transferDescriptor->buffer_phy, sizeof(usb_request_data)); - fStack->FreeChunk(transferDescriptor, - (void *)transferDescriptor->this_phy, 32); - fStack->FreeChunk(statusDescriptor, - (void *)statusDescriptor->this_phy, 32); + free_descriptor_chain(setupDescriptor, fStack); return result; } if (timeout > 0) { - // wait for the transfer to finish + timeout += system_time(); + while (true) { - if ((statusDescriptor->status & TD_STATUS_ACTIVE) == 0) { - TRACE(("usb_uhci: transfer completed successfuly\n")); - return B_OK; - } - if (transferDescriptor->status & 0x7e00) { - TRACE(("usb_uhci: transfer failed: 0x%04x\n", transferDescriptor->status)); - return B_ERROR; +#ifdef TRACE_UHCI + PrintToStream(); +#endif + // check the status of our descriptors + uhci_td *element = setupDescriptor; + while (element) { + if (element->status & TD_STATUS_ACTIVE) { + TRACE(("usb_uhci: transfer in progress\n")); + break; + } + + if (element->status & TD_ERROR_MASK) { + TRACE(("usb_uhci: transfer failed with 0x%04x\n", element->status & TD_ERROR_MASK)); + return B_ERROR; + } + + if (element == statusDescriptor) { + // we appearantly got through to our last descriptor + // without any error so we finished successfully + TRACE(("usb_uhci: transfer successful\n")); + + uint8 *buffer = (uint8 *)transfer->GetBuffer(); + int32 bufferSize = transfer->GetBufferLength(); + size_t *actualLength = transfer->GetActualLength(); + if (buffer && bufferSize > 0) { + *actualLength = read_descriptor_chain( + (uhci_td *)setupDescriptor->link_log, + buffer, bufferSize); + } + + return B_OK; + } + + element = (uhci_td *)element->link_log; } - TRACE(("usb_uhci: in progress\n")); - snooze(1000); + snooze(100); } } @@ -271,20 +444,25 @@ Queue::RemoveInactiveDescriptors() if (fQueueHead->element_phy & QH_TERMINATE) return B_OK; - uhci_td *element = (uhci_td *)fQueueHead->element_log; - while (element) { - if (element->status & TD_STATUS_ACTIVE) + while (fQueueTop) { + if (fQueueTop->status & TD_STATUS_ACTIVE) break; - fStack->FreeChunk(element, (void *)element->this_phy, 32); - - if (element->link_phy & TD_TERMINATE) { + if (fQueueTop->link_phy & TD_TERMINATE) { fQueueHead->element_phy = QH_TERMINATE; fQueueHead->element_log = NULL; + + free_descriptor(fQueueTop, fStack); + fQueueTop = NULL; break; } - element = (uhci_td *)element->link_log; + uhci_td *next = (uhci_td *)fQueueTop->link_log; + free_descriptor(fQueueTop, fStack); + fQueueTop = next; + + if (fQueueTop == fQueueHead->element_log) + break; } return B_OK; @@ -298,6 +476,17 @@ Queue::PhysicalAddress() } +void +Queue::PrintToStream() +{ + dprintf("USB UHCI Queue:\n"); + dprintf("link phy: 0x%08x; link type: %s; terminate: %s\n", fQueueHead->link_phy & 0xfff0, fQueueHead->link_phy & 0x0002 ? "QH" : "TD", fQueueHead->link_phy & 0x0001 ? "yes" : "no"); + dprintf("elem phy: 0x%08x; elem type: %s; terminate: %s\n", fQueueHead->element_phy & 0xfff0, fQueueHead->element_phy & 0x0002 ? "QH" : "TD", fQueueHead->element_phy & 0x0001 ? "yes" : "no"); + dprintf("elements:\n"); + print_descriptor_chain(fQueueTop); +} + + // // #pragma mark - // @@ -353,7 +542,8 @@ UHCI::UHCI(pci_info *info, Stack *stack) WriteReg32(UHCI_FRBASEADD, (uint32)physicalAddress); WriteReg16(UHCI_FRNUM, 0); - for (int32 i = 0; i < 4; i++) { + fQueueCount = 4; + for (int32 i = 0; i < fQueueCount; i++) { fQueues[i] = new Queue(fStack); if (fQueues[i]->InitCheck() < B_OK) { TRACE(("usb_uhci: cannot create queues\n")); @@ -367,7 +557,7 @@ UHCI::UHCI(pci_info *info, Stack *stack) } // Make sure the last queue terminates - fQueues[3]->TerminateByStrayDescriptor(); + fQueues[fQueueCount - 1]->TerminateByStrayDescriptor(); for (int32 i = 0; i < 1024; i++) fFrameList[i] = fQueues[0]->PhysicalAddress() | FRAMELIST_NEXT_IS_QH; @@ -434,7 +624,7 @@ UHCI::SubmitTransfer(Transfer *transfer, bigtime_t timeout) return fRootHub->SubmitTransfer(transfer); if (transfer->GetPipe()->Type() == Pipe::Control) - return fQueues[1]->AddTransfer(transfer, timeout); + return fQueues[1]->AddRequest(transfer, timeout); return B_ERROR; } diff --git a/src/add-ons/kernel/busses/usb/uhci.h b/src/add-ons/kernel/busses/usb/uhci.h index 5d2d36def3..803737388b 100644 --- a/src/add-ons/kernel/busses/usb/uhci.h +++ b/src/add-ons/kernel/busses/usb/uhci.h @@ -28,12 +28,14 @@ public: status_t TerminateByStrayDescriptor(); status_t AppendDescriptor(uhci_td *descriptor); - status_t AddTransfer(Transfer *transfer, + status_t AddRequest(Transfer *transfer, bigtime_t timeout); status_t RemoveInactiveDescriptors(); addr_t PhysicalAddress(); + void PrintToStream(); + private: status_t fStatus; Stack *fStack; @@ -83,7 +85,8 @@ static pci_module_info *sPCIModule; addr_t *fFrameList; // Queues - Queue *fQueues[4]; + int32 fQueueCount; + Queue *fQueues[]; // Maintain a list of transfers Vector fTransfers; diff --git a/src/add-ons/kernel/busses/usb/uhci_hardware.h b/src/add-ons/kernel/busses/usb/uhci_hardware.h index c184a72f0b..afb3375062 100644 --- a/src/add-ons/kernel/busses/usb/uhci_hardware.h +++ b/src/add-ons/kernel/busses/usb/uhci_hardware.h @@ -105,18 +105,28 @@ typedef struct addr_t this_phy; // A physical pointer to this address void * link_log; // Link to the next logical TD/QT void * buffer_log; // Link to the buffer + int32 buffer_size; // Size of the buffer } uhci_td; -#define TD_STATUS_3_ERRORS ( 3 << 27 ) -#define TD_STATUS_LOWSPEED ( 1 << 26 ) -#define TD_STATUS_IOS ( 1 << 25 ) -#define TD_STATUS_IOC ( 1 << 24 ) -#define TD_STATUS_ACTIVE ( 1 << 23 ) -#define TD_TOKEN_DATA1 ( 1 << 19 ) -#define TD_TOKEN_NULL (0x7FF << 21 ) -#define TD_TOKEN_DEVADDR_SHIFT 8 -#define TD_DEPTH_FIRST 0x4 -#define TD_TERMINATE 0x1 +#define TD_STATUS_3_ERRORS (3 << 27) +#define TD_STATUS_LOWSPEED (1 << 26) +#define TD_STATUS_IOS (1 << 25) +#define TD_STATUS_IOC (1 << 24) +#define TD_STATUS_ACTIVE (1 << 23) +#define TD_STATUS_ACTLEN_MASK 0x3ff +#define TD_STATUS_ACTLEN_NULL 0x7ff + +#define TD_TOKEN_DATA1 (1 << 19) +#define TD_TOKEN_NULL (0x7ff << 21) + +#define TD_TOKEN_SETUP 0x2d +#define TD_TOKEN_IN 0x69 +#define TD_TOKEN_OUT 0xe1 + +#define TD_TOKEN_DEVADDR_SHIFT 8 +#define TD_DEPTH_FIRST 0x4 +#define TD_TERMINATE 0x1 +#define TD_ERROR_MASK 0x7e0000 //Represents a Queue Head (QH)