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:
parent
0f5e6b41e7
commit
ac9d119566
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Authors:
|
||||
* Michael Lotz <mmlr@mlotz.ch>
|
||||
* Niels S. Reedijk
|
||||
* Salvatore Benedetto <salvatore.benedetto@gmail.com>
|
||||
*/
|
||||
|
||||
#include <module.h>
|
||||
@ -301,6 +302,10 @@ UHCI::UHCI(pci_info *info, Stack *stack)
|
||||
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;
|
||||
|
||||
@ -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;
|
||||
|
||||
// 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;
|
||||
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)
|
||||
{
|
||||
|
@ -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
|
||||
// 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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user