Commiting patch by Salvatore Benedetto. This adds isochronous handling to the USB bus manager and enables inbound isochronous support in the UHCI driver. Thanks!

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21503 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2007-06-25 19:51:12 +00:00
parent 0f5e6b41e7
commit ac9d119566
8 changed files with 643 additions and 44 deletions

View File

@ -74,11 +74,14 @@ struct usb_configuration_info {
};
typedef struct {
int16 req_len;
int16 act_len;
int16 request_length;
int16 actual_length;
status_t status;
} usb_iso_packet_descriptor;
// Flags for queue_isochronous
#define USB_ISO_ASAP 0x01
typedef void (*usb_callback_func)(void *cookie, status_t status, void *data,
size_t actualLength);

View File

@ -198,7 +198,32 @@ IsochronousPipe::QueueIsochronous(void *data, size_t dataLength,
uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback,
void *callbackCookie)
{
return B_ERROR;
// TODO: Check if values of input parameters are set correctely
usb_isochronous_data *isochronousData
= new(std::nothrow) usb_isochronous_data;
if (!isochronousData)
return B_NO_MEMORY;
isochronousData->packet_descriptors = packetDesc;
isochronousData->packet_count = packetCount;
isochronousData->starting_frame_number = startingFrameNumber;
isochronousData->flags = flags;
Transfer *transfer = new(std::nothrow) Transfer(this);
if (!transfer) {
delete isochronousData;
return B_NO_MEMORY;
}
transfer->SetData((uint8 *)data, dataLength);
transfer->SetCallback(callback, callbackCookie);
transfer->SetIsochronousData(isochronousData);
status_t result = GetBusManager()->SubmitTransfer(transfer);
if (result < B_OK)
delete transfer;
return result;
}
@ -259,7 +284,9 @@ ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
uint16 index, uint16 length, void *data, size_t dataLength,
size_t *actualLength)
{
transfer_result_data *transferResult = (transfer_result_data *)malloc(sizeof(transfer_result_data));
transfer_result_data *transferResult
= new(std::nothrow) transfer_result_data;
transferResult->notify_sem = create_sem(0, "usb send request notify");
if (transferResult->notify_sem < B_OK)
return B_NO_MORE_SEMS;
@ -290,7 +317,7 @@ ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
*actualLength = transferResult->actual_length;
result = transferResult->status;
free(transferResult);
delete transferResult;
return result;
}
@ -304,7 +331,7 @@ ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
transferResult->actual_length = actualLength;
if (release_sem(transferResult->notify_sem) < B_OK) {
// the request has timed out already - cleanup after us
free(transferResult);
delete transferResult;
}
}

View File

@ -6,7 +6,6 @@
* Michael Lotz <mmlr@mlotz.ch>
* Niels S. Reedijk
*/
#include "usb_p.h"
@ -21,7 +20,9 @@ Transfer::Transfer(Pipe *pipe)
fClonedArea(-1),
fCallback(NULL),
fCallbackCookie(NULL),
fRequestData(NULL)
fRequestData(NULL),
fIsochronousData(NULL),
fBandwidth(0)
{
}
@ -47,6 +48,13 @@ Transfer::SetRequestData(usb_request_data *data)
}
void
Transfer::SetIsochronousData(usb_isochronous_data *data)
{
fIsochronousData = data;
}
void
Transfer::SetData(uint8 *data, size_t dataLength)
{
@ -56,6 +64,12 @@ Transfer::SetData(uint8 *data, size_t dataLength)
if (data && dataLength > 0)
fVectorCount = 1;
// Calculate the bandwidth (only if it is not a bulk transfer)
if (!(fPipe->Type() & USB_OBJECT_BULK_PIPE)) {
if (_CalculateBandwidth() < B_OK)
TRACE_ERROR(("USB Transfer: can't calculate bandwidth\n"));
}
}
@ -187,3 +201,75 @@ Transfer::Finished(uint32 status, size_t actualLength)
fCallback(fCallbackCookie, status, fBaseAddress,
fActualLength + actualLength);
}
/*
* USB 2.0 Spec function, pag 64.
* This function sets fBandwidth in microsecond
* to the bandwidth needed to transfer fData.iov_len bytes.
* The calculation is based on
* 1. Speed of the transfer
* 2. Pipe direction
* 3. Type of pipe
* 4. Number of bytes to transfer
*/
status_t
Transfer::_CalculateBandwidth()
{
uint16 bandwidthNS;
uint32 type = fPipe->Type();
switch (fPipe->Speed()) {
case USB_SPEED_HIGHSPEED:
{
// Direction doesn't matter for highspeed
if (type & USB_OBJECT_ISO_PIPE)
bandwidthNS = (uint16)((38 * 8 * 2.083)
+ (2.083 * ((uint32)(3.167 * (1.1667 * 8 * fData.iov_len))))
+ USB_BW_HOST_DELAY);
else
bandwidthNS = (uint16)((55 * 8 * 2.083)
+ (2.083 * ((uint32)(3.167 * (1.1667 * 8 * fData.iov_len))))
+ USB_BW_HOST_DELAY);
break;
}
case USB_SPEED_FULLSPEED:
{
// Direction does matter this time for isochronous
if (type & USB_OBJECT_ISO_PIPE)
bandwidthNS = (uint16)
(((fPipe->Direction() == Pipe::In) ? 7268 : 6265)
+ (83.54 * ((uint32)(3.167 + (1.1667 * 8 * fData.iov_len))))
+ USB_BW_HOST_DELAY);
else
bandwidthNS = (uint16)(9107
+ (83.54 * ((uint32)(3.167 + (1.1667 * 8 * fData.iov_len))))
+ USB_BW_HOST_DELAY);
break;
}
case USB_SPEED_LOWSPEED:
{
if (fPipe->Direction() == Pipe::In)
bandwidthNS = (uint16) (64060 + (2 * USB_BW_SETUP_LOW_SPEED_PORT_DELAY)
+ (676.67 * ((uint32)(3.167 + (1.1667 * 8 * fData.iov_len))))
+ USB_BW_HOST_DELAY);
else
bandwidthNS = (uint16)(64107 + (2 * USB_BW_SETUP_LOW_SPEED_PORT_DELAY)
+ (667.0 * ((uint32)(3.167 + (1.1667 * 8 * fData.iov_len))))
+ USB_BW_HOST_DELAY);
break;
}
default:
// We should never get here
TRACE(("USB Transfer: speed unknown"));
return B_ERROR;
}
// Round up and set the value in microseconds
fBandwidth = (bandwidthNS + 500) / 1000;
// For debugging purposes
TRACE(("USB Transfer: bandwidth neded %d\n", fBandwidth));
return B_OK;
}

View File

@ -85,7 +85,6 @@ typedef enum {
#define USB_OBJECT_DEVICE 0x00000040
#define USB_OBJECT_HUB 0x00000080
#define USB_MAX_FRAGMENT_SIZE B_PAGE_SIZE * 96
class Stack {
public:
@ -521,6 +520,9 @@ public:
void SetRequestData(usb_request_data *data);
usb_request_data *RequestData() { return fRequestData; };
void SetIsochronousData(usb_isochronous_data *data);
usb_isochronous_data *IsochronousData() { return fIsochronousData; };
void SetData(uint8 *buffer, size_t length);
uint8 *Data() { return (uint8 *)fData.iov_base; };
size_t DataLength() { return fData.iov_len; };
@ -530,6 +532,8 @@ public:
size_t VectorCount() { return fVectorCount; };
size_t VectorLength();
uint16 Bandwidth() { return fBandwidth; };
bool IsFragmented() { return fFragmented; };
void AdvanceByFragment(size_t actualLength);
@ -542,6 +546,8 @@ public:
void Finished(uint32 status, size_t actualLength);
private:
status_t _CalculateBandwidth();
// Data that is related to the transfer
Pipe *fPipe;
iovec fData;
@ -558,6 +564,15 @@ private:
// For control transfers
usb_request_data *fRequestData;
// For isochronous transfers
usb_isochronous_data *fIsochronousData;
// For bandwidth management.
// It contains the bandwidth necessary in microseconds
// for either isochronous, interrupt or control transfers.
// Not used for bulk transactions.
uint16 fBandwidth;
};
#endif

View File

@ -16,6 +16,7 @@
#include <util/kernel_cpp.h>
#define USB_MAX_AREAS 8
#define USB_MAX_FRAGMENT_SIZE B_PAGE_SIZE * 96
#define USB_DELAY_BUS_RESET 100000
#define USB_DELAY_DEVICE_POWER_UP 300000
@ -28,6 +29,11 @@
#define USB_DELAY_FIRST_EXPLORE 5000000
#define USB_DELAY_HUB_EXPLORE 1000000
// For bandwidth calculation
#define USB_BW_HOST_DELAY 1000
#define USB_BW_SETUP_LOW_SPEED_PORT_DELAY 333
/*
Important data from the USB spec (not interesting for drivers)
*/
@ -49,6 +55,14 @@ struct usb_request_data
};
struct usb_isochronous_data {
usb_iso_packet_descriptor *packet_descriptors;
uint32 packet_count;
uint32 *starting_frame_number;
uint32 flags;
};
struct usb_hub_descriptor
{
uint8 length;

View File

@ -5,6 +5,7 @@
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* Niels S. Reedijk
* Salvatore Benedetto <salvatore.benedetto@gmail.com>
*/
#include <module.h>
@ -296,11 +297,15 @@ UHCI::UHCI(pci_info *info, Stack *stack)
fFrameArea(-1),
fFrameList(NULL),
fQueueCount(0),
fQueues(NULL),
fQueues(NULL),
fFirstTransfer(NULL),
fLastTransfer(NULL),
fFinishThread(-1),
fStopFinishThread(false),
fFirstIsochronousTransfer(NULL),
fLastIsochronousTransfer(NULL),
fFinishIsochronousThread(-1),
fStopFinishIsochronousThread(false),
fRootHub(NULL),
fRootHubAddress(0),
fPortResetChange(0)
@ -391,7 +396,7 @@ UHCI::UHCI(pci_info *info, Stack *stack)
fQueues[fQueueCount - 1]->TerminateByStrayDescriptor();
// Create the array that will keep bandwidth information
fFrameBandwidth = new(std::nothrow) int32[NUMBER_OF_FRAMES];
fFrameBandwidth = new(std::nothrow) uint16[NUMBER_OF_FRAMES];
for (int32 i = 0; i < NUMBER_OF_FRAMES; i++) {
fFrameList[i] = fQueues[UHCI_INTERRUPT_QUEUE]->PhysicalAddress()
@ -399,6 +404,10 @@ UHCI::UHCI(pci_info *info, Stack *stack)
fFrameBandwidth[i] = MAX_AVAILABLE_BANDWIDTH;
}
// create lists for managing isochronous transfer descriptors
fFirstIsochronousDescriptor = new(std::nothrow) uhci_td *[NUMBER_OF_FRAMES];
fLastIsochronousDescriptor = new(std::nothrow) uhci_td *[NUMBER_OF_FRAMES];
// create semaphore the finisher thread will wait for
fFinishTransfersSem = create_sem(0, "UHCI Finish Transfers");
if (fFinishTransfersSem < B_OK) {
@ -411,6 +420,18 @@ UHCI::UHCI(pci_info *info, Stack *stack)
"uhci finish thread", B_URGENT_DISPLAY_PRIORITY, (void *)this);
resume_thread(fFinishThread);
// Create a lock for the isochronous transfer list
if (benaphore_init(&fIsochronousLock, "UHCI isochronous lock") < B_OK) {
TRACE_ERROR(("usb_uhci: failed to create isochronous lock\n"));
return;
}
// Create the isochronous finisher service thread
fFinishIsochronousThread = spawn_kernel_thread(FinishIsochronousThread,
"uhci isochronous finish thread", B_URGENT_DISPLAY_PRIORITY,
(void *)this);
resume_thread(fFinishIsochronousThread);
// Install the interrupt handler
TRACE(("usb_uhci: installing interrupt handler\n"));
install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line,
@ -429,8 +450,19 @@ UHCI::~UHCI()
{
int32 result = 0;
fStopFinishThread = true;
fStopFinishIsochronousThread = true;
delete_sem(fFinishTransfersSem);
wait_for_thread(fFinishThread, &result);
wait_for_thread(fFinishIsochronousThread, &result);
LockIsochronous();
isochronous_transfer_data *isoTransfer = fFirstIsochronousTransfer;
while (isoTransfer) {
isochronous_transfer_data *next = isoTransfer->link;
delete isoTransfer;
isoTransfer = next;
}
benaphore_destroy(&fIsochronousLock);
Lock();
transfer_data *transfer = fFirstTransfer;
@ -445,6 +477,8 @@ UHCI::~UHCI()
delete [] fQueues;
delete [] fFrameBandwidth;
delete [] fFirstIsochronousDescriptor;
delete [] fLastIsochronousDescriptor;
delete fRootHub;
delete_area(fFrameArea);
@ -551,6 +585,9 @@ UHCI::SubmitTransfer(Transfer *transfer)
status_t
UHCI::CancelQueuedTransfers(Pipe *pipe)
{
if (pipe->Type() & USB_OBJECT_ISO_PIPE)
return CancelQueuedIsochronousTransfers(pipe);
if (!Lock())
return B_ERROR;
@ -569,7 +606,7 @@ UHCI::CancelQueuedTransfers(Pipe *pipe)
last->link = next;
else
fFirstTransfer = next;
if (fLastTransfer == current)
fLastTransfer = last;
@ -582,11 +619,36 @@ UHCI::CancelQueuedTransfers(Pipe *pipe)
}
Unlock();
return B_OK;
}
status_t
UHCI::CancelQueuedIsochronousTransfers(Pipe *pipe)
{
isochronous_transfer_data *current = fFirstIsochronousTransfer;
while (current) {
if (current->transfer->TransferPipe() == pipe) {
int32 packetCount
= current->transfer->IsochronousData()->packet_count;
// Set the active bit off on every descriptor in order to prevent
// the controller from processing them. Then set off the is_active
// field of the transfer in order to make the finisher thread skip
// the transfer. The FinishIsochronousThread will do the rest.
for (int32 i = 0; i < packetCount; i++)
current->descriptors[i]->status &= ~TD_STATUS_ACTIVE;
current->is_active = false;
}
current = current->link;
}
TRACE_ERROR(("usb_uhci: no isochronous transfer found!\n"));
return B_ERROR;
}
status_t
UHCI::SubmitRequest(Transfer *transfer)
{
@ -685,7 +747,6 @@ UHCI::AddPendingTransfer(Transfer *transfer, Queue *queue,
data->transfer_queue = transferQueue;
data->first_descriptor = firstDescriptor;
data->data_descriptor = dataDescriptor;
data->user_area = -1;
data->incoming = directionIn;
data->link = NULL;
@ -705,23 +766,215 @@ UHCI::AddPendingTransfer(Transfer *transfer, Queue *queue,
}
status_t
UHCI::AddPendingIsochronousTransfer(Transfer *transfer, uhci_td **isoRequest,
bool directionIn)
{
if (!transfer || !isoRequest)
return B_BAD_VALUE;
isochronous_transfer_data *data
= new(std::nothrow) isochronous_transfer_data;
if (!data)
return B_NO_MEMORY;
status_t result = transfer->InitKernelAccess();
if (result < B_OK)
return result;
data->transfer = transfer;
data->descriptors = isoRequest;
data->last_to_process = transfer->IsochronousData()->packet_count - 1;
data->incoming = directionIn;
data->is_active = true;
// Put in the isochronous transfer list
if (!LockIsochronous()) {
delete data;
return B_ERROR;
}
if (fLastIsochronousTransfer)
fLastIsochronousTransfer->link = data;
if (!fFirstIsochronousTransfer)
fFirstIsochronousTransfer = data;
fLastIsochronousTransfer = data;
UnlockIsochronous();
return B_OK;
}
status_t
UHCI::SubmitIsochronous(Transfer *transfer)
{
/*
* This is the main isochronous method.
* Here we attach one Transfer Descriptor (TD) per frame by starting
* at the position specified by StartingFrameNumber. If there is
* not enought bandwidth at that entry number, we find the next
* available one.
* The TD is obviously appended to the latest existing isochronous
* TD (if any) in that frame.
* Everytime a TD is added, fFrameBandiwidth[i] is decremented by
* the TD bandwidth, while it is incremented once the TD has been
* processed by the controller
*/
Pipe *pipe = transfer->TransferPipe();
bool directionIn = (pipe->Direction() == Pipe::In);
usb_isochronous_data *isochronousData = transfer->IsochronousData();
size_t packetSize = transfer->DataLength();
size_t restSize = packetSize % isochronousData->packet_count;
packetSize /= isochronousData->packet_count;
uint16 currentFrame;
return B_ERROR;
// Ignore the fact that the last descriptor might need less bandwidth.
// The overhead is not worthy.
uint16 bandwidth = transfer->Bandwidth() / isochronousData->packet_count;
TRACE(("usb_uhci: isochronous transfer descriptor bandwdith = %d\n",
bandwidth));
// TODO: If direction is out set every descriptor data
if (!directionIn)
return B_ERROR;
// The following holds the list of transfer descriptor of the
// isochronous request. It is used to quickly remove all the isochronous
// descriptors from the frame list, as descriptors are not link to each
// other in a queue like for every other transfer.
uhci_td **isoRequest
= new(std::nothrow) uhci_td *[isochronousData->packet_count];
// Create the list of transfer descriptors
for (uint32 i = 0; i < (isochronousData->packet_count - 1); i++) {
isoRequest[i] = CreateDescriptor(pipe,
directionIn ? TD_TOKEN_IN : TD_TOKEN_OUT, packetSize);
// Make sure data toggle is set to zero
isoRequest[i]->token &= ~TD_TOKEN_DATA1;
}
// Create the last transfer descriptor which should be of smaller size
// and set the IOC bit
isoRequest[isochronousData->packet_count - 1] = CreateDescriptor(pipe,
directionIn ? TD_TOKEN_IN : TD_TOKEN_OUT, restSize);
isoRequest[isochronousData->packet_count - 1]->token &= ~TD_TOKEN_DATA1;
isoRequest[isochronousData->packet_count - 1]->status |= TD_CONTROL_IOC;
TRACE(("usb_uhci: isochronous submitted size=%ld bytes, TDs=%ld, "
"packetSize=%ld, restSize=%ld\n", transfer->DataLength(),
isochronousData->packet_count, packetSize, restSize));
// 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;
}
// Find the entry where to start inserting the first Isochronous descriptor
if (isochronousData->flags & USB_ISO_ASAP ||
isochronousData->starting_frame_number == NULL) {
// find the first available frame with enough bandwidth.
// This should always be the case, as defining the starting frame
// number in the driver makes no sense for many reason, one of which
// is that frame numbers value are host controller specific, and the
// driver does not know which host controller is running.
currentFrame = ReadReg16(UHCI_FRNUM);
// Make sure that:
// 1. We are at least 5ms ahead the controller
// 2. We stay in the range 0-1023
// 3. There is enough bandwidth in the first entry
currentFrame = (currentFrame + 5) % NUMBER_OF_FRAMES;
} else {
// Find out if the frame number specified has enough bandwidth,
// otherwise find the first next available frame with enough bandwidth
currentFrame = *isochronousData->starting_frame_number;
}
// Find the first entry with enough bandwidth
// TODO: should we also check the bandwidth of the following packet_count frames?
uint16 startSeekingFromFrame = currentFrame;
while (fFrameBandwidth[currentFrame] < bandwidth) {
currentFrame = (currentFrame + 1) % NUMBER_OF_FRAMES;
if (currentFrame == startSeekingFromFrame) {
TRACE_ERROR(("usb_uhci: Not enough bandwidth to queue the"
" isochronous request. Try again later!\n"));
return B_ERROR;
}
}
if (isochronousData->starting_frame_number)
*isochronousData->starting_frame_number = currentFrame;
// Add transfer to the list
status_t result = AddPendingIsochronousTransfer(transfer, isoRequest,
directionIn);
if (result < B_OK) {
TRACE_ERROR(("usb_uhci: failed to add pending isochronous transfer\n"));
for (uint32 i = 0; i < isochronousData->packet_count; i++) {
FreeDescriptor(isoRequest[i]);
delete [] isoRequest;
}
return result;
}
TRACE(("usb_uhci: appended isochronous transfer by starting at frame"
" number %d\n", currentFrame));
// Insert the Transfer Descriptor by starting at
// the starting_frame_number entry
// TODO: We don't consider bInterval, and assume it's 1!
for (uint32 i = 0; i < isochronousData->packet_count; i++) {
LinkIsochronousDescriptor(isoRequest[i], currentFrame);
fFrameBandwidth[currentFrame] -= bandwidth;
currentFrame = (currentFrame + 1) % NUMBER_OF_FRAMES;
}
return B_OK;
}
isochronous_transfer_data *
UHCI::FindIsochronousTransfer(uhci_td *descriptor)
{
// Simply check every last descriptor of the isochronous transfer list
LockIsochronous();
isochronous_transfer_data *transfer = fFirstIsochronousTransfer;
while (transfer->descriptors[transfer->last_to_process] != descriptor) {
transfer = transfer->link;
if (!transfer)
break;
}
UnlockIsochronous();
return transfer;
}
void
UHCI::LinkIsochronousDescriptor(uhci_td *descriptor, uint16 frame)
{
// The transfer descriptor is appended to the last
// existing isochronous transfer descriptor (if any)
// in that frame.
if (fFrameList[frame] & FRAMELIST_NEXT_IS_QH) {
// Insert the transfer descriptor in the first position
descriptor->link_phy = fFrameList[frame];
// No need to set the link_log as it is already NULL
fFrameList[frame] = descriptor->this_phy & ~FRAMELIST_NEXT_IS_QH;
fFirstIsochronousDescriptor[frame] = descriptor;
fLastIsochronousDescriptor[frame] = descriptor;
} else {
// Append to the last transfer descriptor
descriptor->link_phy = fLastIsochronousDescriptor[frame]->link_phy;
fLastIsochronousDescriptor[frame]->link_log = descriptor;
fLastIsochronousDescriptor[frame]->link_phy
= descriptor->this_phy & ~TD_NEXT_IS_QH;
fLastIsochronousDescriptor[frame] = descriptor;
}
}
void
UHCI::UnlinkIsochronousDescriptor(uhci_td *descriptor, uint16 frame)
{
// The pointer to the descriptor is in descriptors[frame] and it will be
// freed later.
fFrameList[frame] = descriptor->link_phy;
if (fFrameList[frame] & FRAMELIST_NEXT_IS_QH) {
fFirstIsochronousDescriptor[frame] = NULL;
fLastIsochronousDescriptor[frame] = NULL;
} else
fFirstIsochronousDescriptor[frame] = (uhci_td *)descriptor->link_log;
}
@ -749,7 +1002,9 @@ UHCI::FinishTransfers()
if (!Lock())
continue;
TRACE(("usb_uhci: finishing transfers (first transfer: 0x%08lx; last transfer: 0x%08lx)\n", (uint32)fFirstTransfer, (uint32)fLastTransfer));
TRACE(("usb_uhci: finishing transfers (first transfer: 0x%08lx; last"
" transfer: 0x%08lx)\n", (uint32)fFirstTransfer,
(uint32)fLastTransfer));
transfer_data *lastTransfer = NULL;
transfer_data *transfer = fFirstTransfer;
Unlock();
@ -767,7 +1022,9 @@ UHCI::FinishTransfers()
}
if (status & TD_ERROR_MASK) {
TRACE_ERROR(("usb_uhci: td (0x%08lx) error: status: 0x%08lx; token: 0x%08lx;\n", descriptor->this_phy, status, descriptor->token));
TRACE_ERROR(("usb_uhci: td (0x%08lx) error: status: 0x%08lx;"
" token: 0x%08lx;\n", descriptor->this_phy, status,
descriptor->token));
// an error occured. we have to remove the
// transfer from the queue and clean up
@ -903,6 +1160,92 @@ UHCI::FinishTransfers()
}
int32
UHCI::FinishIsochronousThread(void *data)
{
((UHCI *)data)->FinishIsochronousTransfers();
return B_OK;
}
void
UHCI::FinishIsochronousTransfers()
{
/* This thread stays one position behind the controller and processes every
* isochronous descriptor. Once it finds the last isochronous descriptor
* of a transfer, it processes the entire transfer.
*/
uint16 currentFrame = ReadReg16(UHCI_FRNUM);
while (!fStopFinishIsochronousThread) {
// wait 1ms in order to be sure to be one position behind
// the controller
if (currentFrame == ReadReg16(UHCI_FRNUM))
snooze(1000);
// Process the frame till it has isochronous descriptors in it.
while (!(fFrameList[currentFrame] & FRAMELIST_NEXT_IS_QH)) {
uhci_td *current = fFirstIsochronousDescriptor[currentFrame];
UnlinkIsochronousDescriptor(current, currentFrame);
// Process the transfer if we found the last descriptor
if (current->status & TD_CONTROL_IOC) {
isochronous_transfer_data *transfer
= FindIsochronousTransfer(current);
// The following should NEVER happen
if (!transfer)
TRACE_ERROR(("usb_uhci: Isochronous transfer not found in"
" the finisher thread!\n"));
// Process the transfer only if it is still active and belongs
// to an INPUT transfer. If the transfer is not active, it
// means the request has been removed, so simply remove the
// descriptors.
else if (transfer->is_active && (current->status & TD_TOKEN_IN)) {
iovec *vector = transfer->transfer->Vector();
transfer->transfer->PrepareKernelAccess();
ReadIsochronousDescriptorChain(transfer, vector);
// Remove the transfer
if (LockIsochronous()) {
if (transfer == fFirstIsochronousTransfer)
fFirstIsochronousTransfer = transfer->link;
else {
isochronous_transfer_data *temp
= fFirstIsochronousTransfer;
while (transfer != temp->link)
temp = temp->link;
if (transfer == fLastIsochronousTransfer)
fLastIsochronousTransfer = temp;
temp->link = temp->link->link;
}
UnlockIsochronous();
}
transfer->transfer->Finished(B_OK, 0);
}
uint32 packetCount =
transfer->transfer->IsochronousData()->packet_count;
for (uint32 i = 0; i < packetCount; i++)
FreeDescriptor(transfer->descriptors[i]);
delete [] transfer->descriptors;
delete transfer->transfer;
delete transfer;
}
}
// Make sure to reset the frame bandwidth
fFrameBandwidth[currentFrame] = MAX_AVAILABLE_BANDWIDTH;
currentFrame = (currentFrame + 1) % NUMBER_OF_FRAMES;
}
}
void
UHCI::GlobalReset()
{
@ -1292,9 +1635,14 @@ UHCI::CreateDescriptor(Pipe *pipe, uint8 direction, size_t bufferSize)
}
result->this_phy = (addr_t)physicalAddress;
result->status = TD_STATUS_ACTIVE | TD_CONTROL_3_ERRORS;
if (direction == TD_TOKEN_IN)
result->status |= TD_CONTROL_SPD;
result->status = TD_STATUS_ACTIVE;
if (pipe->Type() & USB_OBJECT_ISO_PIPE)
result->status |= TD_CONTROL_ISOCHRONOUS;
else {
result->status |= TD_CONTROL_3_ERRORS;
if (direction == TD_TOKEN_IN)
result->status |= TD_CONTROL_SPD;
}
if (pipe->Speed() == USB_SPEED_LOWSPEED)
result->status |= TD_CONTROL_LOWSPEED;
@ -1315,7 +1663,7 @@ UHCI::CreateDescriptor(Pipe *pipe, uint8 direction, size_t bufferSize)
return result;
}
if (fStack->AllocateChunk(&result->buffer_log, &result->buffer_phy,
if (fStack->AllocateChunk(&result->buffer_log, (void **)&result->buffer_phy,
bufferSize) < B_OK) {
TRACE_ERROR(("usb_uhci: unable to allocate space for the buffer\n"));
fStack->FreeChunk(result, (void *)result->this_phy, sizeof(uhci_td));
@ -1422,7 +1770,9 @@ UHCI::WriteDescriptorChain(uhci_td *topDescriptor, iovec *vector,
size_t length = min_c(current->buffer_size - bufferOffset,
vector[vectorIndex].iov_len - vectorOffset);
TRACE(("usb_uhci: copying %ld bytes to bufferOffset %ld from vectorOffset %ld at index %ld of %ld\n", length, bufferOffset, vectorOffset, vectorIndex, vectorCount));
TRACE(("usb_uhci: copying %ld bytes to bufferOffset %ld from"
" vectorOffset %ld at index %ld of %ld\n", length, bufferOffset,
vectorOffset, vectorIndex, vectorCount));
memcpy((uint8 *)current->buffer_log + bufferOffset,
(uint8 *)vector[vectorIndex].iov_base + vectorOffset, length);
@ -1432,7 +1782,8 @@ UHCI::WriteDescriptorChain(uhci_td *topDescriptor, iovec *vector,
if (vectorOffset >= vector[vectorIndex].iov_len) {
if (++vectorIndex >= vectorCount) {
TRACE(("usb_uhci: wrote descriptor chain (%ld bytes, no more vectors)\n", actualLength));
TRACE(("usb_uhci: wrote descriptor chain (%ld bytes, no"
" more vectors)\n", actualLength));
return actualLength;
}
@ -1480,7 +1831,9 @@ UHCI::ReadDescriptorChain(uhci_td *topDescriptor, iovec *vector,
size_t length = min_c(bufferSize - bufferOffset,
vector[vectorIndex].iov_len - vectorOffset);
TRACE(("usb_uhci: copying %ld bytes to vectorOffset %ld from bufferOffset %ld at index %ld of %ld\n", length, vectorOffset, bufferOffset, vectorIndex, vectorCount));
TRACE(("usb_uhci: 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_log + bufferOffset, length);
@ -1548,6 +1901,52 @@ UHCI::ReadActualLength(uhci_td *topDescriptor, uint8 *lastDataToggle)
}
void
UHCI::ReadIsochronousDescriptorChain(isochronous_transfer_data *transfer,
iovec *vector)
{
size_t vectorOffset = 0;
usb_isochronous_data *isochronousData
= transfer->transfer->IsochronousData();
for (uint32 i = 0; i < isochronousData->packet_count; i++) {
uhci_td *current = transfer->descriptors[i];
size_t bufferSize
= isochronousData->packet_descriptors[i].request_length;
int16 actualLength = (current->status & TD_STATUS_ACTLEN_MASK) + 1;
if (actualLength == TD_STATUS_ACTLEN_NULL + 1)
actualLength = 0;
isochronousData->packet_descriptors[i].actual_length = actualLength;
if (actualLength > 0)
isochronousData->packet_descriptors[i].status = B_OK;
else
isochronousData->packet_descriptors[i].status = B_ERROR;
memcpy((uint8 *)vector->iov_base + vectorOffset,
(uint8 *)current->buffer_log, bufferSize);
vectorOffset += bufferSize;
}
}
bool
UHCI::LockIsochronous()
{
return (benaphore_lock(&fIsochronousLock) == B_OK);
}
void
UHCI::UnlockIsochronous()
{
benaphore_unlock(&fIsochronousLock);
}
inline void
UHCI::WriteReg8(uint32 reg, uint8 value)
{

View File

@ -5,6 +5,7 @@
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* Niels S. Reedijk
* Salvatore Benedetto <salvatore.benedetto@gmail.com>
*/
#ifndef UHCI_H
@ -61,12 +62,27 @@ typedef struct transfer_data_s {
uhci_qh *transfer_queue;
uhci_td *first_descriptor;
uhci_td *data_descriptor;
area_id user_area;
bool incoming;
transfer_data_s *link;
} transfer_data;
// This structure is used to create a list of
// descriptors per isochronous transfer
typedef struct isochronous_transfer_data_s {
Transfer *transfer;
// The next field is used to keep track
// of every isochronous descriptor as they are NOT
// linked to each other in a queue like in every other
// transfer type
uhci_td **descriptors;
uint16 last_to_process;
bool incoming;
bool is_active;
isochronous_transfer_data_s *link;
} isochronous_transfer_data;
class UHCI : public BusManager {
public:
UHCI(pci_info *info, Stack *stack);
@ -75,6 +91,7 @@ public:
status_t Start();
virtual status_t SubmitTransfer(Transfer *transfer);
virtual status_t CancelQueuedTransfers(Pipe *pipe);
status_t CancelQueuedIsochronousTransfers(Pipe *pipe);
status_t SubmitRequest(Transfer *transfer);
status_t SubmitIsochronous(Transfer *transfer);
@ -103,6 +120,11 @@ static int32 InterruptHandler(void *data);
uhci_td *firstDescriptor,
uhci_td *dataDescriptor,
bool directionIn);
status_t AddPendingIsochronousTransfer(
Transfer *transfer,
uhci_td **isoRequest,
bool directionIn);
static int32 FinishThread(void *data);
void FinishTransfers();
@ -110,10 +132,25 @@ static int32 FinishThread(void *data);
uhci_td **_firstDescriptor,
uhci_qh **_transferQueue);
// Isochronous transfer functions
static int32 FinishIsochronousThread(void *data);
void FinishIsochronousTransfers();
isochronous_transfer_data *FindIsochronousTransfer(uhci_td *descriptor);
void LinkIsochronousDescriptor(
uhci_td *descriptor,
uint16 frame);
void UnlinkIsochronousDescriptor(
uhci_td *descriptor,
uint16 frame);
// Transfer queue functions
uhci_qh *CreateTransferQueue(uhci_td *descriptor);
void FreeTransferQueue(uhci_qh *queueHead);
bool LockIsochronous();
void UnlockIsochronous();
// Descriptor functions
uhci_td *CreateDescriptor(Pipe *pipe,
uint8 direction,
@ -137,6 +174,9 @@ static int32 FinishThread(void *data);
uint8 *lastDataToggle);
size_t ReadActualLength(uhci_td *topDescriptor,
uint8 *lastDataToggle);
void ReadIsochronousDescriptorChain(
isochronous_transfer_data *transfer,
iovec *vector);
// Register functions
inline void WriteReg8(uint32 reg, uint8 value);
@ -154,11 +194,17 @@ static pci_module_info *sPCIModule;
// Frame list memory
area_id fFrameArea;
addr_t *fFrameList;
uint32 *fFrameList;
// fFrameBandwidth[n] holds the available bandwidth
// fFrameBandwidth[n] holds the available bandwidth
// of the nth frame in microseconds
int32 *fFrameBandwidth;
uint16 *fFrameBandwidth;
// fFirstIsochronousTransfer[n] and fLastIsochronousDescriptor[n]
// keeps track of the first and last isochronous transfer descriptor
// in the nth frame
uhci_td **fFirstIsochronousDescriptor;
uhci_td **fLastIsochronousDescriptor;
// Queues
int32 fQueueCount;
@ -171,6 +217,13 @@ static pci_module_info *sPCIModule;
thread_id fFinishThread;
bool fStopFinishThread;
// Maintain a linked list of isochronous transfers
isochronous_transfer_data *fFirstIsochronousTransfer;
isochronous_transfer_data *fLastIsochronousTransfer;
thread_id fFinishIsochronousThread;
benaphore fIsochronousLock;
bool fStopFinishIsochronousThread;
// Root hub
UHCIRootHub *fRootHub;
uint8 fRootHubAddress;

View File

@ -87,10 +87,10 @@
typedef struct
{
// Hardware part
addr_t link_phy; // Link to the next TD/QH
uint32 link_phy; // Link to the next TD/QH
uint32 status; // Status field
uint32 token; // Contains the packet header (where it needs to be sent)
void *buffer_phy; // A pointer to the buffer with the actual packet
uint32 buffer_phy; // A pointer to the buffer with the actual packet
// Software part
addr_t this_phy; // A physical pointer to this address
void *link_log; // Pointer to the next logical TD/QT
@ -98,6 +98,8 @@ typedef struct
size_t buffer_size; // Size of the buffer
} uhci_td;
#define TD_NEXT_IS_QH 0x02
// Control and Status
#define TD_CONTROL_SPD (1 << 29)
#define TD_CONTROL_3_ERRORS (3 << 27)
@ -142,8 +144,8 @@ typedef struct
typedef struct
{
// Hardware part
addr_t link_phy; // Link to the next TD/QH
addr_t element_phy; // Pointer to the first element in the queue
uint32 link_phy; // Link to the next TD/QH
uint32 element_phy; // Pointer to the first element in the queue
// Software part
addr_t this_phy; // The physical pointer to this address
void *link_log; // Pointer to the next logical TD/QH