USB OHCI: Support for isochronous transfers

Finishing and refactoring the draft, initially implemented during April-May 2012

NOTE: startingFrameNumber returned to device contains the number of the
next free frame right after the last packed of submitted data. For more
details please Look into corresponding [haiku-development] discussion
started at 03 Jul 2013.

Partially fixes #1045.
This commit is contained in:
Siarzhuk Zharski 2013-05-25 20:46:44 +02:00
parent 883b8dd0b9
commit cf58476ccc
5 changed files with 818 additions and 75 deletions

View File

@ -62,6 +62,19 @@
#define USB_ENDPOINT_ATTR_INTERRUPT 0x03
#define USB_ENDPOINT_ATTR_MASK 0x03
/* Synchronization - isochrnous endpoints only */
#define USB_ENDPOINT_ATTR_NO_SYNCHRONIZE 0x00
#define USB_ENDPOINT_ATTR_ASYNCRONOUS 0x04
#define USB_ENDPOINT_ATTR_ADAPTIVE_SYNCHRO 0x08
#define USB_ENDPOINT_ATTR_SYNCHRONOUS 0x0C
#define USB_ENDPOINT_ATTR_SYNCHRONIZE_MASK 0x0C
/* Usage Type - isochrnous endpoints only */
#define USB_ENDPOINT_ATTR_DATA_USAGE 0x00
#define USB_ENDPOINT_ATTR_FEEDBACK_USAGE 0x10
#define USB_ENDPOINT_ATTR_IMPLICIT_USAGE 0x20
#define USB_ENDPOINT_ATTR_USAGE_MASK 0x30
#define USB_ENDPOINT_ADDR_DIR_IN 0x80
#define USB_ENDPOINT_ADDR_DIR_OUT 0x00

View File

@ -1,11 +1,12 @@
/*
* Copyright 2005-2008, Haiku Inc. All rights reserved.
* Copyright 2005-2013, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jan-Rixt Van Hoye
* Salvatore Benedetto <salvatore.benedetto@gmail.com>
* Michael Lotz <mmlr@mlotz.ch>
* Siarzhuk Zharski <imker@gmx.li>
*/
#include <module.h>
@ -74,6 +75,7 @@ OHCI::OHCI(pci_info *info, Stack *stack)
fFinishThread(-1),
fStopFinishThread(false),
fProcessingPipe(NULL),
fFrameBandwidth(NULL),
fRootHub(NULL),
fRootHubAddress(0),
fPortCount(0),
@ -312,6 +314,12 @@ OHCI::OHCI(pci_info *info, Stack *stack)
fPortCount = numberOfPorts;
TRACE("port count is %d\n", fPortCount);
// Create the array that will keep bandwidth information
fFrameBandwidth = new(std::nothrow) uint16[NUMBER_OF_FRAMES];
for (int32 i = 0; i < NUMBER_OF_FRAMES; i++)
fFrameBandwidth[i] = MAX_AVAILABLE_BANDWIDTH;
// Create semaphore the finisher thread will wait for
fFinishTransfersSem = create_sem(0, "OHCI Finish Transfers");
if (fFinishTransfersSem < B_OK) {
@ -378,6 +386,7 @@ OHCI::~OHCI()
_FreeEndpoint(fInterruptEndpoints[i]);
}
delete [] fFrameBandwidth;
delete [] fInterruptEndpoints;
delete fRootHub;
@ -459,9 +468,6 @@ OHCI::SubmitTransfer(Transfer *transfer)
status_t
OHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
{
if (pipe->Type() & USB_OBJECT_ISO_PIPE)
return _CancelQueuedIsochronousTransfers(pipe, force);
if (!Lock())
return B_ERROR;
@ -487,6 +493,25 @@ OHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
= current->endpoint->tail_physical_descriptor;
if (!force) {
if (pipe->Type() & USB_OBJECT_ISO_PIPE) {
ohci_isochronous_td *descriptor
= (ohci_isochronous_td *)current->first_descriptor;
while (descriptor) {
uint16 frame = OHCI_ITD_GET_STARTING_FRAME(
descriptor->flags);
_ReleaseIsochronousBandwidth(frame,
OHCI_ITD_GET_FRAME_COUNT(descriptor->flags));
if (descriptor
== (ohci_isochronous_td*)current->last_descriptor)
// this is the last ITD of the transfer
break;
descriptor
= (ohci_isochronous_td *)
descriptor->next_done_descriptor;
}
}
// If the transfer is canceled by force, the one causing the
// cancel is probably not the one who initiated the transfer
// and the callback is likely not safe anymore
@ -914,10 +939,55 @@ OHCI::_AddPendingTransfer(Transfer *transfer,
status_t
OHCI::_CancelQueuedIsochronousTransfers(Pipe *pipe, bool force)
OHCI::_AddPendingIsochronousTransfer(Transfer *transfer,
ohci_endpoint_descriptor *endpoint, ohci_isochronous_td *firstDescriptor,
ohci_isochronous_td *lastDescriptor, bool directionIn)
{
// TODO
return B_ERROR;
if (!transfer || !endpoint || !lastDescriptor)
return B_BAD_VALUE;
transfer_data *data = new(std::nothrow) transfer_data;
if (!data)
return B_NO_MEMORY;
status_t result = transfer->InitKernelAccess();
if (result < B_OK) {
delete data;
return result;
}
data->transfer = transfer;
data->endpoint = endpoint;
data->incoming = directionIn;
data->canceled = false;
data->link = NULL;
// the current tail will become the first descriptor
data->first_descriptor = (ohci_general_td*)endpoint->tail_logical_descriptor;
// the data and first descriptors are the same
data->data_descriptor = data->first_descriptor;
// the last and the first descriptor might be the same
if (lastDescriptor == firstDescriptor)
data->last_descriptor = data->first_descriptor;
else
data->last_descriptor = (ohci_general_td*)lastDescriptor;
if (!Lock()) {
delete data;
return B_ERROR;
}
if (fLastTransfer)
fLastTransfer->link = data;
else
fFirstTransfer = data;
fLastTransfer = data;
Unlock();
return B_OK;
}
@ -957,10 +1027,20 @@ OHCI::_FinishTransfers()
ohci_endpoint_descriptor *endpoint = transfer->endpoint;
status_t callbackStatus = B_OK;
if (endpoint->flags & OHCI_ENDPOINT_ISOCHRONOUS_FORMAT) {
transfer_data *next = transfer->link;
if (_FinishIsochronousTransfer(transfer, &lastTransfer)) {
delete transfer->transfer;
delete transfer;
}
transfer = next;
continue;
}
MutexLocker endpointLocker(endpoint->lock);
if ((endpoint->head_physical_descriptor & OHCI_ENDPOINT_HEAD_MASK)
!= endpoint->tail_physical_descriptor) {
!= endpoint->tail_physical_descriptor) {
// there are still active transfers on this endpoint, we need
// to wait for all of them to complete, otherwise we'd read
// a potentially bogus data toggle value below
@ -991,49 +1071,7 @@ OHCI::_FinishTransfers()
// still ensures that this td was handled before).
TRACE_ERROR("td error: 0x%08" B_PRIx32 "\n", status);
switch (status) {
case OHCI_TD_CONDITION_CRC_ERROR:
case OHCI_TD_CONDITION_BIT_STUFFING:
case OHCI_TD_CONDITION_TOGGLE_MISMATCH:
callbackStatus = B_DEV_CRC_ERROR;
break;
case OHCI_TD_CONDITION_STALL:
callbackStatus = B_DEV_STALLED;
break;
case OHCI_TD_CONDITION_NO_RESPONSE:
callbackStatus = B_TIMED_OUT;
break;
case OHCI_TD_CONDITION_PID_CHECK_FAILURE:
callbackStatus = B_DEV_BAD_PID;
break;
case OHCI_TD_CONDITION_UNEXPECTED_PID:
callbackStatus = B_DEV_UNEXPECTED_PID;
break;
case OHCI_TD_CONDITION_DATA_OVERRUN:
callbackStatus = B_DEV_DATA_OVERRUN;
break;
case OHCI_TD_CONDITION_DATA_UNDERRUN:
callbackStatus = B_DEV_DATA_UNDERRUN;
break;
case OHCI_TD_CONDITION_BUFFER_OVERRUN:
callbackStatus = B_DEV_FIFO_OVERRUN;
break;
case OHCI_TD_CONDITION_BUFFER_UNDERRUN:
callbackStatus = B_DEV_FIFO_UNDERRUN;
break;
default:
callbackStatus = B_ERROR;
break;
}
callbackStatus = _GetStatusOfConditionCode(status);
transferDone = true;
break;
@ -1163,6 +1201,141 @@ OHCI::_FinishTransfers()
}
bool
OHCI::_FinishIsochronousTransfer(transfer_data *transfer,
transfer_data **_lastTransfer)
{
status_t callbackStatus = B_OK;
size_t actualLength = 0;
uint32 packet = 0;
if (transfer->canceled)
callbackStatus = B_CANCELED;
else {
// at first check if ALL ITDs are retired by HC
ohci_isochronous_td *descriptor
= (ohci_isochronous_td *)transfer->first_descriptor;
while (descriptor) {
if (OHCI_TD_GET_CONDITION_CODE(descriptor->flags)
== OHCI_TD_CONDITION_NOT_ACCESSED) {
TRACE("ITD %p still active\n", descriptor);
*_lastTransfer = transfer;
return false;
}
if (descriptor == (ohci_isochronous_td*)transfer->last_descriptor) {
// this is the last ITD of the transfer
descriptor = (ohci_isochronous_td *)transfer->first_descriptor;
break;
}
descriptor
= (ohci_isochronous_td *)descriptor->next_done_descriptor;
}
while (descriptor) {
uint32 status = OHCI_TD_GET_CONDITION_CODE(descriptor->flags);
if (status != OHCI_TD_CONDITION_NO_ERROR) {
TRACE_ERROR("ITD error: 0x%08" B_PRIx32 "\n", status);
// spec says that in most cases condition code
// of retired ITDs is set to NoError, but for the
// time overrun it can be DataOverrun. We assume
// the _first_ occurience of such error as status
// reported to the callback
if (callbackStatus == B_OK)
callbackStatus = _GetStatusOfConditionCode(status);
}
usb_isochronous_data *isochronousData
= transfer->transfer->IsochronousData();
uint32 frameCount = OHCI_ITD_GET_FRAME_COUNT(descriptor->flags);
for (size_t i = 0; i < frameCount; i++, packet++) {
usb_iso_packet_descriptor* packet_descriptor
= &isochronousData->packet_descriptors[packet];
uint16 offset = descriptor->offset[OHCI_ITD_OFFSET_IDX(i)];
uint8 code = OHCI_ITD_GET_BUFFER_CONDITION_CODE(offset);
packet_descriptor->status = _GetStatusOfConditionCode(code);
// not touched by HC - sheduled too late to be processed
// in the requested frame - so we ignore it too
if (packet_descriptor->status == B_DEV_TOO_LATE)
continue;
size_t len = OHCI_ITD_GET_BUFFER_LENGTH(offset);
if (!transfer->incoming)
len = packet_descriptor->request_length - len;
packet_descriptor->actual_length = len;
actualLength += len;
}
uint16 frame = OHCI_ITD_GET_STARTING_FRAME(descriptor->flags);
_ReleaseIsochronousBandwidth(frame,
OHCI_ITD_GET_FRAME_COUNT(descriptor->flags));
TRACE("ITD %p done\n", descriptor);
if (descriptor == (ohci_isochronous_td*)transfer->last_descriptor)
break;
descriptor
= (ohci_isochronous_td *)descriptor->next_done_descriptor;
}
}
// remove the transfer from the list first so we are sure
// it doesn't get canceled while we still process it
if (Lock()) {
if (*_lastTransfer)
(*_lastTransfer)->link = transfer->link;
if (transfer == fFirstTransfer)
fFirstTransfer = transfer->link;
if (transfer == fLastTransfer)
fLastTransfer = *_lastTransfer;
// store the currently processing pipe here so we can wait
// in cancel if we are processing something on the target pipe
if (!transfer->canceled)
fProcessingPipe = transfer->transfer->TransferPipe();
transfer->link = NULL;
Unlock();
}
// break the descriptor chain on the last descriptor
transfer->last_descriptor->next_logical_descriptor = NULL;
TRACE("iso.transfer %p done with status 0x%08" B_PRIx32 " len:%ld\n",
transfer, callbackStatus, actualLength);
// if canceled the callback has already been called
if (!transfer->canceled) {
if (callbackStatus == B_OK && actualLength > 0) {
if (transfer->data_descriptor && transfer->incoming) {
// data to read out
iovec *vector = transfer->transfer->Vector();
size_t vectorCount = transfer->transfer->VectorCount();
transfer->transfer->PrepareKernelAccess();
_ReadIsochronousDescriptorChain(
(ohci_isochronous_td*)transfer->data_descriptor,
vector, vectorCount);
}
}
transfer->transfer->Finished(callbackStatus, actualLength);
fProcessingPipe = NULL;
}
_FreeIsochronousDescriptorChain(
(ohci_isochronous_td*)transfer->first_descriptor);
return true;
}
status_t
OHCI::_SubmitRequest(Transfer *transfer)
{
@ -1307,8 +1480,58 @@ OHCI::_SubmitTransfer(Transfer *transfer)
status_t
OHCI::_SubmitIsochronousTransfer(Transfer *transfer)
{
TRACE_ERROR("isochronous transfers not implemented\n");
return B_ERROR;
Pipe *pipe = transfer->TransferPipe();
bool directionIn = (pipe->Direction() == Pipe::In);
usb_isochronous_data *isochronousData = transfer->IsochronousData();
ohci_isochronous_td *firstDescriptor = NULL;
ohci_isochronous_td *lastDescriptor = NULL;
status_t result = _CreateIsochronousDescriptorChain(&firstDescriptor,
&lastDescriptor, transfer);
if (firstDescriptor == 0 || lastDescriptor == 0)
return B_ERROR;
if (result < B_OK)
return result;
// Set the last descriptor to generate an interrupt
lastDescriptor->flags &= ~OHCI_ITD_INTERRUPT_MASK;
// let the controller retire last ITD
lastDescriptor->flags |= OHCI_ITD_SET_DELAY_INTERRUPT(1);
// If direction is out set every descriptor data
if (pipe->Direction() == Pipe::Out)
_WriteIsochronousDescriptorChain(firstDescriptor,
transfer->Vector(), transfer->VectorCount());
else
// Initialize the packet descriptors
for (uint32 i = 0; i < isochronousData->packet_count; i++) {
isochronousData->packet_descriptors[i].actual_length = 0;
isochronousData->packet_descriptors[i].status = B_NO_INIT;
}
// Add to the transfer list
ohci_endpoint_descriptor *endpoint
= (ohci_endpoint_descriptor *)pipe->ControllerCookie();
MutexLocker endpointLocker(endpoint->lock);
result = _AddPendingIsochronousTransfer(transfer, endpoint,
firstDescriptor, lastDescriptor, directionIn);
if (result < B_OK) {
TRACE_ERROR("failed to add pending iso.transfer:"
"0x%08" B_PRIx32 "\n", result);
_FreeIsochronousDescriptorChain(firstDescriptor);
return result;
}
// Add the descriptor chain to the endpoint
_SwitchIsochronousEndpointTail(endpoint, firstDescriptor, lastDescriptor);
endpointLocker.Unlock();
endpoint->flags &= ~OHCI_ENDPOINT_SKIP;
return B_OK;
}
@ -1352,6 +1575,54 @@ OHCI::_SwitchEndpointTail(ohci_endpoint_descriptor *endpoint,
}
void
OHCI::_SwitchIsochronousEndpointTail(ohci_endpoint_descriptor *endpoint,
ohci_isochronous_td *first, ohci_isochronous_td *last)
{
// fill in the information of the first descriptor into the current tail
ohci_isochronous_td *tail
= (ohci_isochronous_td*)endpoint->tail_logical_descriptor;
tail->flags = first->flags;
tail->buffer_page_byte_0 = first->buffer_page_byte_0;
tail->next_physical_descriptor = first->next_physical_descriptor;
tail->last_byte_address = first->last_byte_address;
tail->buffer_size = first->buffer_size;
tail->buffer_logical = first->buffer_logical;
tail->next_logical_descriptor = first->next_logical_descriptor;
tail->next_done_descriptor = first->next_done_descriptor;
// the first descriptor becomes the new tail
first->flags = 0;
first->buffer_page_byte_0 = 0;
first->next_physical_descriptor = 0;
first->last_byte_address = 0;
first->buffer_size = 0;
first->buffer_logical = NULL;
first->next_logical_descriptor = NULL;
first->next_done_descriptor = NULL;
for (int i = 0; i < OHCI_ITD_NOFFSET; i++) {
tail->offset[i] = first->offset[i];
first->offset[i] = 0;
}
if (first == last)
_LinkIsochronousDescriptors(tail, first, NULL);
else
_LinkIsochronousDescriptors(last, first, NULL);
// update the endpoint tail pointer to reflect the change
endpoint->tail_logical_descriptor = first;
endpoint->tail_physical_descriptor = (uint32)first->physical_address;
TRACE("switched tail from %p to %p\n", tail, first);
#if 0
_PrintEndpoint(endpoint);
_PrintDescriptorChain(tail);
#endif
}
void
OHCI::_RemoveTransferFromEndpoint(transfer_data *transfer)
{
@ -1515,9 +1786,11 @@ OHCI::_InsertEndpointForPipe(Pipe *pipe)
if (pipe->Type() & USB_OBJECT_ISO_PIPE) {
// Set the isochronous bit format
endpoint->flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT;
// TODO
_FreeEndpoint(endpoint);
return B_ERROR;
ohci_isochronous_td *tail = _CreateIsochronousDescriptor(0);
tail->flags = 0;
endpoint->tail_logical_descriptor = tail;
endpoint->head_physical_descriptor = tail->physical_address;
endpoint->tail_physical_descriptor = tail->physical_address;
} else {
ohci_general_td *tail = _CreateGeneralDescriptor(0);
tail->flags = 0;
@ -1691,6 +1964,191 @@ OHCI::_FreeDescriptorChain(ohci_general_td *topDescriptor)
}
ohci_isochronous_td *
OHCI::_CreateIsochronousDescriptor(size_t bufferSize)
{
ohci_isochronous_td *descriptor = NULL;
phys_addr_t physicalAddress;
if (fStack->AllocateChunk((void **)&descriptor, &physicalAddress,
sizeof(ohci_isochronous_td)) != B_OK) {
TRACE_ERROR("failed to allocate isochronous descriptor\n");
return NULL;
}
descriptor->physical_address = (uint32)physicalAddress;
descriptor->next_physical_descriptor = 0;
descriptor->next_logical_descriptor = NULL;
descriptor->next_done_descriptor = NULL;
descriptor->buffer_size = bufferSize;
if (bufferSize == 0) {
descriptor->buffer_page_byte_0 = 0;
descriptor->buffer_logical = NULL;
descriptor->last_byte_address = 0;
return descriptor;
}
if (fStack->AllocateChunk(&descriptor->buffer_logical,
&physicalAddress, bufferSize) != B_OK) {
TRACE_ERROR("failed to allocate space for iso.buffer\n");
fStack->FreeChunk(descriptor, descriptor->physical_address,
sizeof(ohci_isochronous_td));
return NULL;
}
descriptor->buffer_page_byte_0 = (uint32)physicalAddress;
descriptor->last_byte_address
= descriptor->buffer_page_byte_0 + bufferSize - 1;
return descriptor;
}
void
OHCI::_FreeIsochronousDescriptor(ohci_isochronous_td *descriptor)
{
if (!descriptor)
return;
if (descriptor->buffer_logical) {
fStack->FreeChunk(descriptor->buffer_logical,
descriptor->buffer_page_byte_0, descriptor->buffer_size);
}
fStack->FreeChunk((void *)descriptor, descriptor->physical_address,
sizeof(ohci_general_td));
}
status_t
OHCI::_CreateIsochronousDescriptorChain(ohci_isochronous_td **_firstDescriptor,
ohci_isochronous_td **_lastDescriptor, Transfer *transfer)
{
Pipe *pipe = transfer->TransferPipe();
usb_isochronous_data *isochronousData = transfer->IsochronousData();
size_t dataLength = transfer->VectorLength();
size_t packet_count = isochronousData->packet_count;
if (packet_count == 0) {
TRACE_ERROR("isochronous packet_count should not be equal to zero.");
return B_BAD_VALUE;
}
size_t packetSize = dataLength / packet_count;
if (dataLength % packet_count != 0)
packetSize++;
if (packetSize > pipe->MaxPacketSize()) {
TRACE_ERROR("isochronous packetSize %ld is bigger"
" than pipe MaxPacketSize %ld.", packetSize, pipe->MaxPacketSize());
return B_BAD_VALUE;
}
uint16 bandwidth = transfer->Bandwidth() / packet_count;
if (transfer->Bandwidth() % packet_count != 0)
bandwidth++;
ohci_isochronous_td *firstDescriptor = NULL;
ohci_isochronous_td *lastDescriptor = *_firstDescriptor;
// the frame number currently processed by the host controller
uint16 currentFrame = fHcca->current_frame_number & 0xFFFF;
uint16 safeFrames = 5;
// The entry where to start inserting the first Isochronous descriptor
// real frame number may differ in case provided one has not bandwidth
if (isochronousData->flags & USB_ISO_ASAP ||
isochronousData->starting_frame_number == NULL)
// We should stay about 5-10 ms ahead of the controller
// USB1 frame is equal to 1 ms
currentFrame += safeFrames;
else
currentFrame = *isochronousData->starting_frame_number;
uint16 packets = packet_count;
uint16 frameOffset = 0;
while (packets > 0) {
// look for up to 8 continous frames with available bandwidth
uint16 frameCount = 0;
while (frameCount < min_c(OHCI_ITD_NOFFSET, packets)
&& _AllocateIsochronousBandwidth(frameOffset + currentFrame
+ frameCount, bandwidth))
frameCount++;
if (frameCount == 0) {
// starting frame has no bandwidth for our transaction - try next
if (++frameOffset >= 0xFFFF) {
TRACE_ERROR("failed to allocate bandwidth\n");
_FreeIsochronousDescriptorChain(firstDescriptor);
return B_NO_MEMORY;
}
continue;
}
ohci_isochronous_td *descriptor = _CreateIsochronousDescriptor(
packetSize * frameCount);
if (!descriptor) {
TRACE_ERROR("failed to allocate ITD\n");
_ReleaseIsochronousBandwidth(currentFrame + frameOffset, frameCount);
_FreeIsochronousDescriptorChain(firstDescriptor);
return B_NO_MEMORY;
}
uint16 pageOffset = descriptor->buffer_page_byte_0 & 0xfff;
descriptor->buffer_page_byte_0 &= ~0xfff;
for (uint16 i = 0; i < frameCount; i++) {
descriptor->offset[OHCI_ITD_OFFSET_IDX(i)]
= OHCI_ITD_MK_OFFS(pageOffset + packetSize * i);
}
descriptor->flags = OHCI_ITD_SET_FRAME_COUNT(frameCount)
| OHCI_ITD_SET_CONDITION_CODE(OHCI_ITD_CONDITION_NOT_ACCESSED)
| OHCI_ITD_SET_DELAY_INTERRUPT(OHCI_ITD_INTERRUPT_NONE)
| OHCI_ITD_SET_STARTING_FRAME(currentFrame + frameOffset);
// the last packet may be shorter than other ones in this transfer
if (packets <= OHCI_ITD_NOFFSET)
descriptor->last_byte_address
+= dataLength - packetSize * (packet_count);
// link to previous
if (lastDescriptor)
_LinkIsochronousDescriptors(lastDescriptor, descriptor, descriptor);
lastDescriptor = descriptor;
if (!firstDescriptor)
firstDescriptor = descriptor;
packets -= frameCount;
frameOffset += frameCount;
if (packets == 0 && isochronousData->starting_frame_number)
*isochronousData->starting_frame_number = currentFrame + frameOffset;
}
*_firstDescriptor = firstDescriptor;
*_lastDescriptor = lastDescriptor;
return B_OK;
}
void
OHCI::_FreeIsochronousDescriptorChain(ohci_isochronous_td *topDescriptor)
{
ohci_isochronous_td *current = topDescriptor;
ohci_isochronous_td *next = NULL;
while (current) {
next = (ohci_isochronous_td *)current->next_done_descriptor;
_FreeIsochronousDescriptor(current);
current = next;
}
}
size_t
OHCI::_WriteDescriptorChain(ohci_general_td *topDescriptor, iovec *vector,
size_t vectorCount)
@ -1746,6 +2204,61 @@ OHCI::_WriteDescriptorChain(ohci_general_td *topDescriptor, iovec *vector,
}
size_t
OHCI::_WriteIsochronousDescriptorChain(ohci_isochronous_td *topDescriptor,
iovec *vector, size_t vectorCount)
{
ohci_isochronous_td *current = topDescriptor;
size_t actualLength = 0;
size_t vectorIndex = 0;
size_t vectorOffset = 0;
size_t bufferOffset = 0;
while (current) {
if (!current->buffer_logical)
break;
while (true) {
size_t length = min_c(current->buffer_size - bufferOffset,
vector[vectorIndex].iov_len - vectorOffset);
TRACE("copying %ld bytes to bufferOffset %ld from"
" vectorOffset %ld at index %ld of %ld\n", length, bufferOffset,
vectorOffset, vectorIndex, vectorCount);
memcpy((uint8 *)current->buffer_logical + bufferOffset,
(uint8 *)vector[vectorIndex].iov_base + vectorOffset, length);
actualLength += length;
vectorOffset += length;
bufferOffset += length;
if (vectorOffset >= vector[vectorIndex].iov_len) {
if (++vectorIndex >= vectorCount) {
TRACE("wrote descriptor chain (%ld bytes, no"
" more vectors)\n", actualLength);
return actualLength;
}
vectorOffset = 0;
}
if (bufferOffset >= current->buffer_size) {
bufferOffset = 0;
break;
}
}
if (!current->next_logical_descriptor)
break;
current = (ohci_isochronous_td *)current->next_logical_descriptor;
}
TRACE("wrote descriptor chain (%ld bytes)\n", actualLength);
return actualLength;
}
size_t
OHCI::_ReadDescriptorChain(ohci_general_td *topDescriptor, iovec *vector,
size_t vectorCount)
@ -1805,6 +2318,59 @@ OHCI::_ReadDescriptorChain(ohci_general_td *topDescriptor, iovec *vector,
}
void
OHCI::_ReadIsochronousDescriptorChain(ohci_isochronous_td *topDescriptor,
iovec *vector, size_t vectorCount)
{
ohci_isochronous_td *current = topDescriptor;
size_t actualLength = 0;
size_t vectorIndex = 0;
size_t vectorOffset = 0;
size_t bufferOffset = 0;
while (current && OHCI_ITD_GET_CONDITION_CODE(current->flags)
!= OHCI_ITD_CONDITION_NOT_ACCESSED) {
size_t bufferSize = current->buffer_size;
if (current->buffer_logical != NULL && bufferSize > 0) {
while (true) {
size_t length = min_c(bufferSize - bufferOffset,
vector[vectorIndex].iov_len - vectorOffset);
TRACE("copying %ld bytes to vectorOffset %ld from bufferOffset"
" %ld at index %ld of %ld\n", length, vectorOffset,
bufferOffset, vectorIndex, vectorCount);
memcpy((uint8 *)vector[vectorIndex].iov_base + vectorOffset,
(uint8 *)current->buffer_logical + bufferOffset, length);
actualLength += length;
vectorOffset += length;
bufferOffset += length;
if (vectorOffset >= vector[vectorIndex].iov_len) {
if (++vectorIndex >= vectorCount) {
TRACE("read descriptor chain (%ld bytes, "
"no more vectors)\n", actualLength);
return;
}
vectorOffset = 0;
}
if (bufferOffset >= bufferSize) {
bufferOffset = 0;
break;
}
}
}
current = (ohci_isochronous_td *)current->next_done_descriptor;
}
TRACE("read descriptor chain (%ld bytes)\n", actualLength);
return;
}
size_t
OHCI::_ReadActualLength(ohci_general_td *topDescriptor)
{
@ -1836,18 +2402,85 @@ OHCI::_LinkDescriptors(ohci_general_td *first, ohci_general_td *second)
}
ohci_isochronous_td *
OHCI::_CreateIsochronousDescriptor()
void
OHCI::_LinkIsochronousDescriptors(ohci_isochronous_td *first,
ohci_isochronous_td *second, ohci_isochronous_td *nextDone)
{
// TODO
return NULL;
first->next_physical_descriptor = second->physical_address;
first->next_logical_descriptor = second;
first->next_done_descriptor = nextDone;
}
bool
OHCI::_AllocateIsochronousBandwidth(uint16 frame, uint16 size)
{
frame %= NUMBER_OF_FRAMES;
if (size > fFrameBandwidth[frame])
return false;
fFrameBandwidth[frame]-= size;
return true;
}
void
OHCI::_FreeIsochronousDescriptor(ohci_isochronous_td *descriptor)
OHCI::_ReleaseIsochronousBandwidth(uint16 startFrame, uint16 frameCount)
{
// TODO
for (size_t index = 0; index < frameCount; index++) {
uint16 frame = (startFrame + index) % NUMBER_OF_FRAMES;
fFrameBandwidth[frame] = MAX_AVAILABLE_BANDWIDTH;
}
}
status_t
OHCI::_GetStatusOfConditionCode(uint8 conditionCode)
{
switch (conditionCode) {
case OHCI_TD_CONDITION_NO_ERROR:
return B_OK;
case OHCI_TD_CONDITION_CRC_ERROR:
case OHCI_TD_CONDITION_BIT_STUFFING:
case OHCI_TD_CONDITION_TOGGLE_MISMATCH:
return B_DEV_CRC_ERROR;
case OHCI_TD_CONDITION_STALL:
return B_DEV_STALLED;
case OHCI_TD_CONDITION_NO_RESPONSE:
return B_TIMED_OUT;
case OHCI_TD_CONDITION_PID_CHECK_FAILURE:
return B_DEV_BAD_PID;
case OHCI_TD_CONDITION_UNEXPECTED_PID:
return B_DEV_UNEXPECTED_PID;
case OHCI_TD_CONDITION_DATA_OVERRUN:
return B_DEV_DATA_OVERRUN;
case OHCI_TD_CONDITION_DATA_UNDERRUN:
return B_DEV_DATA_UNDERRUN;
case OHCI_TD_CONDITION_BUFFER_OVERRUN:
return B_DEV_FIFO_OVERRUN;
case OHCI_TD_CONDITION_BUFFER_UNDERRUN:
return B_DEV_FIFO_UNDERRUN;
case OHCI_TD_CONDITION_NOT_ACCESSED:
return B_DEV_PENDING;
case 0x0E:
return B_DEV_TOO_LATE; // PSW: _NOT_ACCESSED
default:
break;
}
return B_ERROR;
}
@ -1882,7 +2515,7 @@ OHCI::_ReadReg(uint32 reg)
void
OHCI::_PrintEndpoint(ohci_endpoint_descriptor *endpoint)
{
TRACE_ALWAYS("endpoint %p\n", endpoint);
dprintf("endpoint %p\n", endpoint);
dprintf("\tflags........... 0x%08" B_PRIx32 "\n", endpoint->flags);
dprintf("\ttail_physical... 0x%08" B_PRIx32 "\n", endpoint->tail_physical_descriptor);
dprintf("\thead_physical... 0x%08" B_PRIx32 "\n", endpoint->head_physical_descriptor);
@ -1897,7 +2530,7 @@ void
OHCI::_PrintDescriptorChain(ohci_general_td *topDescriptor)
{
while (topDescriptor) {
TRACE_ALWAYS("descriptor %p\n", topDescriptor);
dprintf("descriptor %p\n", topDescriptor);
dprintf("\tflags........... 0x%08" B_PRIx32 "\n", topDescriptor->flags);
dprintf("\tbuffer_physical. 0x%08" B_PRIx32 "\n", topDescriptor->buffer_physical);
dprintf("\tnext_physical... 0x%08" B_PRIx32 "\n", topDescriptor->next_physical_descriptor);
@ -1910,3 +2543,30 @@ OHCI::_PrintDescriptorChain(ohci_general_td *topDescriptor)
topDescriptor = (ohci_general_td *)topDescriptor->next_logical_descriptor;
}
}
void
OHCI::_PrintDescriptorChain(ohci_isochronous_td *topDescriptor)
{
while (topDescriptor) {
dprintf("iso.descriptor %p\n", topDescriptor);
dprintf("\tflags........... 0x%08" B_PRIx32 "\n", topDescriptor->flags);
dprintf("\tbuffer_pagebyte0 0x%08" B_PRIx32 "\n", topDescriptor->buffer_page_byte_0);
dprintf("\tnext_physical... 0x%08" B_PRIx32 "\n", topDescriptor->next_physical_descriptor);
dprintf("\tlast_byte....... 0x%08" B_PRIx32 "\n", topDescriptor->last_byte_address);
dprintf("\toffset:\n\t0x%04x 0x%04x 0x%04x 0x%04x\n"
"\t0x%04x 0x%04x 0x%04x 0x%04x\n",
topDescriptor->offset[0], topDescriptor->offset[1],
topDescriptor->offset[2], topDescriptor->offset[3],
topDescriptor->offset[4], topDescriptor->offset[5],
topDescriptor->offset[6], topDescriptor->offset[7]);
dprintf("\tphysical........ 0x%08" B_PRIx32 "\n", topDescriptor->physical_address);
dprintf("\tbuffer_size..... %lu\n", topDescriptor->buffer_size);
dprintf("\tbuffer_logical.. %p\n", topDescriptor->buffer_logical);
dprintf("\tnext_logical.... %p\n", topDescriptor->next_logical_descriptor);
dprintf("\tnext_done....... %p\n", topDescriptor->next_done_descriptor);
topDescriptor = (ohci_isochronous_td *)topDescriptor->next_done_descriptor;
}
}

View File

@ -1,11 +1,12 @@
/*
* Copyright 2005-2008, Haiku Inc. All rights reserved.
* Copyright 2005-2013, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jan-Rixt Van Hoye
* Salvatore Benedetto <salvatore.benedetto@gmail.com>
* Michael Lotz <mmlr@mlotz.ch>
* Siarzhuk Zharski <imker@gmx.li>
*/
#ifndef OHCI_H
#define OHCI_H
@ -69,12 +70,20 @@ static int32 _InterruptHandler(void *data);
ohci_general_td *dataDescriptor,
ohci_general_td *lastDescriptor,
bool directionIn);
status_t _CancelQueuedIsochronousTransfers(
Pipe *pipe, bool force);
status_t _AddPendingIsochronousTransfer(
Transfer *transfer,
ohci_endpoint_descriptor *endpoint,
ohci_isochronous_td *firstDescriptor,
ohci_isochronous_td *lastDescriptor,
bool directionIn);
status_t _UnlinkTransfer(transfer_data *transfer);
static int32 _FinishThread(void *data);
void _FinishTransfers();
bool _FinishIsochronousTransfer(
transfer_data *transfer,
transfer_data **_lastTransfer);
status_t _SubmitRequest(Transfer *transfer);
status_t _SubmitTransfer(Transfer *transfer);
@ -85,6 +94,11 @@ static int32 _FinishThread(void *data);
ohci_endpoint_descriptor *endpoint,
ohci_general_td *first,
ohci_general_td *last);
void _SwitchIsochronousEndpointTail(
ohci_endpoint_descriptor *endpoint,
ohci_isochronous_td *first,
ohci_isochronous_td *last);
void _RemoveTransferFromEndpoint(
transfer_data *transfer);
@ -101,7 +115,6 @@ static int32 _FinishThread(void *data);
size_t bufferSize);
void _FreeGeneralDescriptor(
ohci_general_td *descriptor);
status_t _CreateDescriptorChain(
ohci_general_td **firstDescriptor,
ohci_general_td **lastDescriptor,
@ -110,22 +123,48 @@ static int32 _FinishThread(void *data);
void _FreeDescriptorChain(
ohci_general_td *topDescriptor);
ohci_isochronous_td * _CreateIsochronousDescriptor(
size_t bufferSize);
void _FreeIsochronousDescriptor(
ohci_isochronous_td *descriptor);
status_t _CreateIsochronousDescriptorChain(
ohci_isochronous_td **firstDescriptor,
ohci_isochronous_td **lastDescriptor,
Transfer *transfer);
void _FreeIsochronousDescriptorChain(
ohci_isochronous_td *topDescriptor);
size_t _WriteDescriptorChain(
ohci_general_td *topDescriptor,
iovec *vector, size_t vectorCount);
size_t _ReadDescriptorChain(
ohci_general_td *topDescriptor,
iovec *vector, size_t vectorCount);
size_t _WriteIsochronousDescriptorChain(
ohci_isochronous_td *topDescriptor,
iovec *vector, size_t vectorCount);
void _ReadIsochronousDescriptorChain(
ohci_isochronous_td *topDescriptor,
iovec *vector, size_t vectorCount);
size_t _ReadActualLength(
ohci_general_td *topDescriptor);
void _LinkDescriptors(ohci_general_td *first,
ohci_general_td *second);
void _LinkIsochronousDescriptors(
ohci_isochronous_td *first,
ohci_isochronous_td *second,
ohci_isochronous_td *nextDone);
ohci_isochronous_td * _CreateIsochronousDescriptor();
void _FreeIsochronousDescriptor(
ohci_isochronous_td *descriptor);
bool _AllocateIsochronousBandwidth(uint16 frame,
uint16 size);
void _ReleaseIsochronousBandwidth(
uint16 startFrame, uint16 count);
status_t _GetStatusOfConditionCode(
uint8 conditionCode);
// Private locking
bool _LockEndpoints();
void _UnlockEndpoints();
@ -139,6 +178,8 @@ inline uint32 _ReadReg(uint32 reg);
ohci_endpoint_descriptor *endpoint);
void _PrintDescriptorChain(
ohci_general_td *topDescriptor);
void _PrintDescriptorChain(
ohci_isochronous_td *topDescriptor);
static pci_module_info * sPCIModule;
static pci_x86_module_info * sPCIx86Module;
@ -167,6 +208,8 @@ static pci_x86_module_info * sPCIx86Module;
thread_id fFinishThread;
bool fStopFinishThread;
Pipe * fProcessingPipe;
// frame bandwidth watchdogs array
uint16 * fFrameBandwidth;
// Root Hub
OHCIRootHub * fRootHub;

View File

@ -12,6 +12,6 @@ resource app_version {
variety = 0,
internal = 0,
short_info = "OHCI host controller driver",
long_info = "Haiku OHCI HCD - Copyright 2005-2009, Haiku Inc."
long_info = "Haiku OHCI HCD - Copyright 2005-2013, Haiku Inc."
};

View File

@ -1,11 +1,12 @@
/*
* Copyright 2005-2008, Haiku Inc. All rights reserved.
* Copyright 2005-2013, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jan-Rixt Van Hoye
* Salvatore Benedetto <salvatore.benedetto@gmail.com>
* Michael Lotz <mmlr@mlotz.ch>
* Siarzhuk Zharski <imker@gmx.li>
*/
#ifndef OHCI_HARDWARE_H
@ -396,6 +397,8 @@ typedef struct {
uint16 offset[OHCI_ITD_NOFFSET]; // Buffer offsets
// Software part
uint32 physical_address; // Physical address of this descriptor
size_t buffer_size; // Size of the buffer
void *buffer_logical; // Logical pointer to the buffer
void *next_logical_descriptor; // Logical pointer next descriptor
void *next_done_descriptor; // Used for collision in the hash table
} ohci_isochronous_td;
@ -404,11 +407,31 @@ typedef struct {
#define OHCI_ITD_SET_STARTING_FRAME(x) ((x) & 0xffff)
#define OHCI_ITD_GET_DELAY_INTERRUPT(x) (((x) >> 21) & 7)
#define OHCI_ITD_SET_DELAY_INTERRUPT(x) ((x) << 21)
#define OHCI_ITD_NO_INTERRUPT 0x00e00000
#define OHCI_ITD_INTERRUPT_MASK 0x00e00000
#define OHCI_ITD_GET_FRAME_COUNT(x) ((((x) >> 24) & 7) + 1)
#define OHCI_ITD_SET_FRAME_COUNT(x) (((x) - 1) << 24)
#define OHCI_ITD_GET_CONDITION_CODE(x) ((x) >> 28)
#define OHCI_ITD_NO_CONDITION_CODE 0xf0000000
#define OHCI_ITD_SET_CONDITION_CODE(x) ((x) << 28)
#define OHCI_ITD_CONDITION_CODE_MASK 0xf0000000
#define OHCI_ITD_OFFSET_IDX(x) (x)
#define OHCI_ITD_INTERRUPT_IMMEDIATE 0x00
#define OHCI_ITD_INTERRUPT_NONE 0x07
#define OHCI_ITD_CONDITION_NO_ERROR 0x00
#define OHCI_ITD_CONDITION_CRC_ERROR 0x01
#define OHCI_ITD_CONDITION_BIT_STUFFING 0x02
#define OHCI_ITD_CONDITION_TOGGLE_MISMATCH 0x03
#define OHCI_ITD_CONDITION_STALL 0x04
#define OHCI_ITD_CONDITION_NO_RESPONSE 0x05
#define OHCI_ITD_CONDITION_PID_CHECK_FAILURE 0x06
#define OHCI_ITD_CONDITION_UNEXPECTED_PID 0x07
#define OHCI_ITD_CONDITION_DATA_OVERRUN 0x08
#define OHCI_ITD_CONDITION_DATA_UNDERRUN 0x09
#define OHCI_ITD_CONDITION_BUFFER_OVERRUN 0x0c
#define OHCI_ITD_CONDITION_BUFFER_UNDERRUN 0x0d
#define OHCI_ITD_CONDITION_NOT_ACCESSED 0x0f
// TO FIX
#define itd_pswn itd_offset // Packet Status Word
@ -419,6 +442,10 @@ typedef struct {
#define OHCI_ISOCHRONOUS_TD_ALIGN 32
// Number of frames
#define NUMBER_OF_FRAMES 1024
#define MAX_AVAILABLE_BANDWIDTH 900 // Microseconds
// --------------------------------
// Completion Codes (section 4.3.3)
// --------------------------------