* Moved the transfer descriptor handling to separate functions

* Added debugging facilities
* Implemented the data stage of control messages

The device descriptor can now be successfully retrieved. Now interrupt, bulk and isochronous data transfers need to be implemented.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17623 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2006-05-28 20:33:25 +00:00
parent f0ed203a59
commit 64312bc94c
4 changed files with 308 additions and 100 deletions

View File

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

View File

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

View File

@ -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<Transfer *> fTransfers;

View File

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