* 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
This commit is contained in:
Michael Lotz 2006-08-13 14:48:10 +00:00
parent bfb0aa18d3
commit d83f3c1a45
9 changed files with 139 additions and 80 deletions

View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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;