From d83f3c1a45d231f5ec4e3c1127d5f44a9b54fb00 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sun, 13 Aug 2006 14:48:10 +0000 Subject: [PATCH] * Fixed setting configurations (need to use the configuration_value instead of the index of course) * Fixed reported power status of the UHCI Root Hub * Added preservation of the SOF_MODIFY register when doing a global reset of the UHC * Read out the actual length for outgoing transfers too * Made actual length handling for transfers a bit safer and correct in respect to NULL packets * Use short packet detect to handle short packets * First step at implementing the hub interrupt Since the devices are now configured correctly, some bulk transfers actually work. Also my hub now reports its status correctly. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18506 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/bus_managers/usb/Device.cpp | 2 +- src/add-ons/kernel/bus_managers/usb/Hub.cpp | 47 +++++----- .../kernel/bus_managers/usb/Transfer.cpp | 14 +-- src/add-ons/kernel/bus_managers/usb/usb_p.h | 15 +-- .../kernel/bus_managers/usb/usbspec_p.h | 1 + src/add-ons/kernel/busses/usb/uhci.cpp | 94 ++++++++++++++----- src/add-ons/kernel/busses/usb/uhci.h | 7 +- src/add-ons/kernel/busses/usb/uhci_hardware.h | 4 +- src/add-ons/kernel/busses/usb/uhci_rh.cpp | 35 ++++--- 9 files changed, 139 insertions(+), 80 deletions(-) diff --git a/src/add-ons/kernel/bus_managers/usb/Device.cpp b/src/add-ons/kernel/bus_managers/usb/Device.cpp index d9be001e65..e9bbf3ea43 100644 --- a/src/add-ons/kernel/bus_managers/usb/Device.cpp +++ b/src/add-ons/kernel/bus_managers/usb/Device.cpp @@ -325,7 +325,7 @@ Device::SetConfigurationAt(uint8 index) status_t result = SendRequest( USB_REQTYPE_DEVICE_OUT | USB_REQTYPE_STANDARD, // type USB_REQUEST_SET_CONFIGURATION, // request - index, // value + fConfigurations[index].descr->configuration_value, // value 0, // index 0, // length NULL, // buffer diff --git a/src/add-ons/kernel/bus_managers/usb/Hub.cpp b/src/add-ons/kernel/bus_managers/usb/Hub.cpp index 43781eb46b..ceb809cbc1 100644 --- a/src/add-ons/kernel/bus_managers/usb/Hub.cpp +++ b/src/add-ons/kernel/bus_managers/usb/Hub.cpp @@ -34,27 +34,8 @@ Hub::Hub(BusManager *bus, Device *parent, usb_device_descriptor &desc, for (int32 i = 0; i < 8; i++) fChildren[i] = NULL; - if (fDeviceDescriptor.device_class != 9 - || fDeviceDescriptor.device_subclass != 0 - || fDeviceDescriptor.device_protocol != 0) { - TRACE(("USB Hub: wrong class/subclass/protocol! Bailing out\n")); - return; - } - - if (Configuration()->descr->number_interfaces > 1) { - TRACE(("USB Hub: too many interfaces\n")); - return; - } - - fInterruptInterface = Configuration()->interface->active->descr; - if (fInterruptInterface->num_endpoints > 1) { - TRACE(("USB Hub: too many endpoints\n")); - return; - } - - fInterruptEndpoint = Configuration()->interface->active->endpoint[0].descr; - if (fInterruptEndpoint->attributes != 0x03) { // interrupt endpoint - TRACE(("USB Hub: Not an interrupt endpoint\n")); + if (fDeviceDescriptor.device_class != 9) { + TRACE(("USB Hub: wrong class! Bailing out\n")); return; } @@ -78,6 +59,19 @@ Hub::Hub(BusManager *bus, Device *parent, usb_device_descriptor &desc, TRACE(("\tdevice_removeable:...0x%02x\n", fHubDescriptor.device_removeable)); TRACE(("\tpower_control_mask:..0x%02x\n", fHubDescriptor.power_control_mask)); + Pipe *pipe = (Pipe *)Configuration()->interface->active->endpoint[0].handle; + if (!pipe || pipe->Type() != Pipe::Interrupt) { + TRACE(("USB Hub: no interrupt pipe found\n")); + return; + } + + fInterruptPipe = (InterruptPipe *)pipe; + fInterruptPipe->QueueInterrupt(fInterruptStatus, sizeof(fInterruptStatus), + InterruptCallback, this); + + // Wait some time before powering up the ports + snooze(USB_DELAY_HUB_POWER_UP); + // Enable port power on all ports for (int32 i = 0; i < fHubDescriptor.num_ports; i++) { status = SendRequest(USB_REQTYPE_CLASS | USB_REQTYPE_OTHER_OUT, @@ -167,6 +161,9 @@ Hub::Explore() if (result < B_OK) continue; + if (DeviceAddress() > 1) + TRACE(("USB Hub (%d): port %d: status: 0x%04x; change: 0x%04x\n", DeviceAddress(), i, fPortStatus[i].status, fPortStatus[i].change)); + if (fPortStatus[i].change & PORT_STATUS_CONNECTION) { if (fPortStatus[i].status & PORT_STATUS_CONNECTION) { // new device attached! @@ -230,6 +227,14 @@ Hub::Explore() } +void +Hub::InterruptCallback(void *cookie, uint32 status, void *data, + uint32 actualLength) +{ + TRACE(("USB Hub: interrupt callback!\n")); +} + + status_t Hub::GetDescriptor(uint8 descriptorType, uint8 index, uint16 languageID, void *data, size_t dataLength, size_t *actualLength) diff --git a/src/add-ons/kernel/bus_managers/usb/Transfer.cpp b/src/add-ons/kernel/bus_managers/usb/Transfer.cpp index a13f5cc7b8..5abd113063 100644 --- a/src/add-ons/kernel/bus_managers/usb/Transfer.cpp +++ b/src/add-ons/kernel/bus_managers/usb/Transfer.cpp @@ -13,8 +13,8 @@ Transfer::Transfer(Pipe *pipe, bool synchronous) : fPipe(pipe), fData(NULL), fDataLength(0), - fActualLength(NULL), - fOwnActualLength(0), + fActualLengthPointer(NULL), + fActualLength(0), fStatus(B_USB_STATUS_DRIVER_INTERNAL_ERROR), fCallback(NULL), fCallbackCookie(NULL), @@ -22,7 +22,6 @@ Transfer::Transfer(Pipe *pipe, bool synchronous) fHostPrivate(NULL), fRequestData(NULL) { - fActualLength = &fOwnActualLength; if (synchronous) { fSem = create_sem(0, "USB Transfer"); set_sem_owner(fSem, B_SYSTEM_TEAM); @@ -55,7 +54,7 @@ Transfer::SetData(uint8 *data, size_t dataLength) void Transfer::SetActualLength(size_t *actualLength) { - fActualLength = actualLength; + fActualLengthPointer = actualLength; } @@ -92,13 +91,16 @@ Transfer::WaitForFinish() void -Transfer::Finished(uint32 status) +Transfer::Finished(uint32 status, size_t actualLength) { fStatus = status; + fActualLength = actualLength; + if (fActualLengthPointer) + *fActualLengthPointer = actualLength; // Call the callback function ... if (fCallback) { - fCallback(fCallbackCookie, fStatus, fData, *fActualLength); + fCallback(fCallbackCookie, fStatus, fData, fActualLength); return; } diff --git a/src/add-ons/kernel/bus_managers/usb/usb_p.h b/src/add-ons/kernel/bus_managers/usb/usb_p.h index 57eb39a98e..d4fc67132f 100644 --- a/src/add-ons/kernel/bus_managers/usb/usb_p.h +++ b/src/add-ons/kernel/bus_managers/usb/usb_p.h @@ -361,6 +361,9 @@ virtual status_t GetDescriptor(uint8 descriptorType, status_t UpdatePortStatus(uint8 index); status_t ResetPort(uint8 index); void Explore(); +static void InterruptCallback(void *cookie, + uint32 status, void *data, + uint32 actualLength); virtual void ReportDevice( usb_support_descriptor *supportDescriptors, @@ -369,10 +372,10 @@ virtual void ReportDevice( bool added); private: - usb_interface_descriptor *fInterruptInterface; - usb_endpoint_descriptor *fInterruptEndpoint; + InterruptPipe *fInterruptPipe; usb_hub_descriptor fHubDescriptor; + usb_port_status fInterruptStatus[8]; usb_port_status fPortStatus[8]; Device *fChildren[8]; }; @@ -405,7 +408,7 @@ public: size_t DataLength() { return fDataLength; }; void SetActualLength(size_t *actualLength); - size_t *ActualLength() { return fActualLength; }; + size_t *ActualLength() { return fActualLengthPointer; }; void SetHostPrivate(hostcontroller_priv *priv); hostcontroller_priv *HostPrivate() { return fHostPrivate; }; @@ -414,15 +417,15 @@ public: void *cookie); status_t WaitForFinish(); - void Finished(uint32 status); + void Finished(uint32 status, size_t actualLength); private: // Data that is related to the transfer Pipe *fPipe; uint8 *fData; size_t fDataLength; - size_t *fActualLength; - size_t fOwnActualLength; + size_t *fActualLengthPointer; + size_t fActualLength; uint32 fStatus; usb_callback_func fCallback; diff --git a/src/add-ons/kernel/bus_managers/usb/usbspec_p.h b/src/add-ons/kernel/bus_managers/usb/usbspec_p.h index fdfbe469bc..3213d8ac96 100644 --- a/src/add-ons/kernel/bus_managers/usb/usbspec_p.h +++ b/src/add-ons/kernel/bus_managers/usb/usbspec_p.h @@ -17,6 +17,7 @@ #define USB_MAX_AREAS 8 #define USB_DELAY_DEVICE_POWER_UP 300000 +#define USB_DELAY_HUB_POWER_UP 200000 #define USB_DELAY_PORT_RESET 50000 #define USB_DELAY_PORT_RESET_RECOVERY 250000 #define USB_DELAY_SET_ADDRESS_RETRY 200000 diff --git a/src/add-ons/kernel/busses/usb/uhci.cpp b/src/add-ons/kernel/busses/usb/uhci.cpp index 051c18dc9b..217bd9acd5 100644 --- a/src/add-ons/kernel/busses/usb/uhci.cpp +++ b/src/add-ons/kernel/busses/usb/uhci.cpp @@ -751,31 +751,37 @@ UHCI::FinishTransfers() transfer->queue->RemoveDescriptorChain( transfer->first_descriptor, transfer->last_descriptor); + FreeDescriptorChain(transfer->first_descriptor); - transfer->transfer->Finished(callbackStatus); + transfer->transfer->Finished(callbackStatus, 0); transferDone = true; break; } - if (descriptor == transfer->last_descriptor) { + // either all descriptors are done, or we have a short packet + if (descriptor == transfer->last_descriptor + || (descriptor->status & TD_STATUS_ACTLEN_MASK) + < (descriptor->token >> TD_TOKEN_MAXLEN_SHIFT)) { TRACE(("usb_uhci: td (0x%08x) ok\n", descriptor->this_phy)); // we got through without errors so we are finished transfer->queue->RemoveDescriptorChain( transfer->first_descriptor, transfer->last_descriptor); + size_t length = 0; if (transfer->data_descriptor && transfer->incoming) { // data to read out - size_t length = ReadDescriptorChain( + length = ReadDescriptorChain( transfer->data_descriptor, transfer->transfer->Data(), transfer->transfer->DataLength()); - - *(transfer->transfer->ActualLength()) = length; + } else { + // read the actual length that was sent + length = ReadActualLength(transfer->first_descriptor); } FreeDescriptorChain(transfer->first_descriptor); - transfer->transfer->Finished(B_OK); + transfer->transfer->Finished(B_USB_STATUS_SUCCESS, length); transferDone = true; break; } @@ -813,10 +819,14 @@ UHCI::FinishTransfers() void UHCI::GlobalReset() { + uint8 sofValue = ReadReg8(UHCI_SOFMOD); + WriteReg16(UHCI_USBCMD, UHCI_USBCMD_GRESET); snooze(100000); WriteReg16(UHCI_USBCMD, 0); snooze(10000); + + WriteReg8(UHCI_SOFMOD, sofValue); } @@ -1077,7 +1087,7 @@ UHCI::CreateDescriptor(Pipe *pipe, uint8 direction, int32 bufferSize) } result->this_phy = (addr_t)physicalAddress; - result->status = TD_STATUS_ACTIVE | TD_CONTROL_3_ERRORS; + result->status = TD_STATUS_ACTIVE | TD_CONTROL_3_ERRORS | TD_CONTROL_SPD; if (pipe->Speed() == Pipe::LowSpeed) result->status |= TD_CONTROL_LOWSPEED; @@ -1188,20 +1198,20 @@ UHCI::LinkDescriptors(uhci_td *first, uhci_td *second) size_t UHCI::WriteDescriptorChain(uhci_td *topDescriptor, const uint8 *buffer, - int32 bufferSize) + size_t bufferLength) { - size_t actualSize = 0; + size_t actualLength = 0; uhci_td *current = topDescriptor; while (current) { if (!current->buffer_log) break; - int32 length = min_c(current->buffer_size, bufferSize); + size_t length = min_c(current->buffer_size, bufferLength); memcpy(current->buffer_log, buffer, length); - bufferSize -= length; - actualSize += length; + bufferLength -= length; + actualLength += length; buffer += length; if (current->link_phy & TD_TERMINATE) @@ -1210,31 +1220,31 @@ UHCI::WriteDescriptorChain(uhci_td *topDescriptor, const uint8 *buffer, current = (uhci_td *)current->link_log; } - TRACE(("usb_uhci: wrote descriptor chain (%d bytes)\n", actualSize)); - return actualSize; + TRACE(("usb_uhci: wrote descriptor chain (%d bytes)\n", actualLength)); + return actualLength; } size_t UHCI::ReadDescriptorChain(uhci_td *topDescriptor, uint8 *buffer, - int32 bufferSize) + size_t bufferLength) { - size_t actualSize = 0; + size_t actualLength = 0; uhci_td *current = topDescriptor; - while (current) { + while (current && (current->status & TD_STATUS_ACTIVE) == 0) { if (!current->buffer_log) break; - int32 length = (current->status & TD_STATUS_ACTLEN_MASK) + 1; + size_t length = (current->status & TD_STATUS_ACTLEN_MASK) + 1; if (length == TD_STATUS_ACTLEN_NULL + 1) length = 0; - length = min_c(length, bufferSize); + length = min_c(length, bufferLength); memcpy(buffer, current->buffer_log, length); - bufferSize -= length; - actualSize += length; + bufferLength -= length; + actualLength += length; buffer += length; if (current->link_phy & TD_TERMINATE) @@ -1243,8 +1253,39 @@ UHCI::ReadDescriptorChain(uhci_td *topDescriptor, uint8 *buffer, current = (uhci_td *)current->link_log; } - TRACE(("usb_uhci: read descriptor chain (%d bytes)\n", actualSize)); - return actualSize; + TRACE(("usb_uhci: read descriptor chain (%d bytes)\n", actualLength)); + return actualLength; +} + + +size_t +UHCI::ReadActualLength(uhci_td *topDescriptor) +{ + size_t actualLength = 0; + uhci_td *current = topDescriptor; + + while (current && (current->status & TD_STATUS_ACTIVE) == 0) { + TRACE(("usb_uhci: reading actual length from status 0x%08x\n", current->status)); + size_t length = (current->status & TD_STATUS_ACTLEN_MASK) + 1; + if (length == TD_STATUS_ACTLEN_NULL + 1) + length = 0; + + actualLength += length; + if (current->link_phy & TD_TERMINATE) + break; + + current = (uhci_td *)current->link_log; + } + + TRACE(("usb_uhci: read actual length (%d bytes)\n", actualLength)); + return actualLength; +} + + +inline void +UHCI::WriteReg8(uint32 reg, uint8 value) +{ + sPCIModule->write_io_8(fRegisterBase + reg, value); } @@ -1262,6 +1303,13 @@ UHCI::WriteReg32(uint32 reg, uint32 value) } +inline uint8 +UHCI::ReadReg8(uint32 reg) +{ + return sPCIModule->read_io_8(fRegisterBase + reg); +} + + inline uint16 UHCI::ReadReg16(uint32 reg) { diff --git a/src/add-ons/kernel/busses/usb/uhci.h b/src/add-ons/kernel/busses/usb/uhci.h index 6ce08b9795..b35c8b0981 100644 --- a/src/add-ons/kernel/busses/usb/uhci.h +++ b/src/add-ons/kernel/busses/usb/uhci.h @@ -119,13 +119,16 @@ static int32 FinishThread(void *data); uhci_td *second); size_t WriteDescriptorChain(uhci_td *topDescriptor, - const uint8 *buffer, int32 bufferSize); + const uint8 *buffer, size_t bufferLength); size_t ReadDescriptorChain(uhci_td *topDescriptor, - uint8 *buffer, int32 bufferSize); + uint8 *buffer, size_t bufferLength); + size_t ReadActualLength(uhci_td *topDescriptor); // Register functions +inline void WriteReg8(uint32 reg, uint8 value); inline void WriteReg16(uint32 reg, uint16 value); inline void WriteReg32(uint32 reg, uint32 value); +inline uint8 ReadReg8(uint32 reg); inline uint16 ReadReg16(uint32 reg); inline uint32 ReadReg32(uint32 reg); diff --git a/src/add-ons/kernel/busses/usb/uhci_hardware.h b/src/add-ons/kernel/busses/usb/uhci_hardware.h index 5eed58cf40..becc45dbf6 100644 --- a/src/add-ons/kernel/busses/usb/uhci_hardware.h +++ b/src/add-ons/kernel/busses/usb/uhci_hardware.h @@ -95,7 +95,7 @@ typedef struct } uhci_td; // Control and Status -#define TD_CONTROL_SHORT_PACKET (1 << 29) +#define TD_CONTROL_SPD (1 << 29) #define TD_CONTROL_3_ERRORS (3 << 27) #define TD_CONTROL_LOWSPEED (1 << 26) #define TD_CONTROL_ISOCHRONOUS (1 << 25) @@ -110,7 +110,7 @@ typedef struct #define TD_STATUS_ERROR_TIMEOUT (1 << 18) #define TD_STATUS_ERROR_BITSTUFF (1 << 17) -#define TD_STATUS_ACTLEN_MASK 0x03ff +#define TD_STATUS_ACTLEN_MASK 0x07ff #define TD_STATUS_ACTLEN_NULL 0x07ff // Token diff --git a/src/add-ons/kernel/busses/usb/uhci_rh.cpp b/src/add-ons/kernel/busses/usb/uhci_rh.cpp index 00e9664389..ba5d6b151a 100644 --- a/src/add-ons/kernel/busses/usb/uhci_rh.cpp +++ b/src/add-ons/kernel/busses/usb/uhci_rh.cpp @@ -135,9 +135,13 @@ UHCIRootHub::UHCIRootHub(UHCI *uhci, int8 devicenum) status_t UHCIRootHub::SubmitTransfer(Transfer *transfer) { + if (transfer->TransferPipe()->Type() != Pipe::Control) + return B_ERROR; + usb_request_data *request = transfer->RequestData(); TRACE(("usb_uhci_roothub: rh_submit_packet called. request: %u\n", request->Request)); + size_t actualLength = 0; status_t result = B_ERROR; switch (request->Request) { case RH_GET_STATUS: { @@ -155,11 +159,9 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer) // Get port status UpdatePortStatus(); - size_t length = MIN(4, transfer->DataLength()); + actualLength = MIN(4, transfer->DataLength()); memcpy(transfer->Data(), - (void *)&fPortStatus[request->Index - 1], length); - - *(transfer->ActualLength()) = length; + (void *)&fPortStatus[request->Index - 1], actualLength); result = B_OK; break; } @@ -179,21 +181,19 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer) switch (request->Value & 0xff00) { case RH_DEVICE_DESCRIPTOR: { - size_t length = MIN(sizeof(usb_device_descriptor), + actualLength = MIN(sizeof(usb_device_descriptor), transfer->DataLength()); memcpy(transfer->Data(), (void *)&sUHCIRootHubDevice, - length); - *(transfer->ActualLength()) = length; + actualLength); result = B_OK; break; } case RH_CONFIG_DESCRIPTOR: { - size_t length = MIN(sizeof(uhci_root_hub_configuration_s), + actualLength = MIN(sizeof(uhci_root_hub_configuration_s), transfer->DataLength()); memcpy(transfer->Data(), (void *)&sUHCIRootHubConfig, - length); - *(transfer->ActualLength()) = length; + actualLength); result = B_OK; break; } @@ -201,26 +201,23 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer) case RH_STRING_DESCRIPTOR: { uint8 index = request->Value & 0x00ff; if (index > 2) { - *(transfer->ActualLength()) = 0; result = EINVAL; break; } - size_t length = MIN(sUHCIRootHubStrings[index].length, + actualLength = MIN(sUHCIRootHubStrings[index].length, transfer->DataLength()); memcpy(transfer->Data(), (void *)&sUHCIRootHubStrings[index], - length); - *(transfer->ActualLength()) = length; + actualLength); result = B_OK; break; } case RH_HUB_DESCRIPTOR: { - size_t length = MIN(sizeof(usb_hub_descriptor), + actualLength = MIN(sizeof(usb_hub_descriptor), transfer->DataLength()); memcpy(transfer->Data(), (void *)&sUHCIRootHubConfig.hub, - length); - *(transfer->ActualLength()) = length; + actualLength); result = B_OK; break; } @@ -304,7 +301,7 @@ UHCIRootHub::SubmitTransfer(Transfer *transfer) break; } - transfer->Finished(result); + transfer->Finished(result, actualLength); return result; } @@ -340,7 +337,7 @@ UHCIRootHub::UpdatePortStatus() newChange |= PORT_STATUS_RESET; //The port is automagically powered on - newStatus |= PORT_POWER; + newStatus |= PORT_STATUS_POWER; if (portStatus & UHCI_PORTSC_LOWSPEED) newStatus |= PORT_STATUS_LOW_SPEED;