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 {
|
typedef struct {
|
||||||
int16 req_len;
|
int16 request_length;
|
||||||
int16 act_len;
|
int16 actual_length;
|
||||||
status_t status;
|
status_t status;
|
||||||
} usb_iso_packet_descriptor;
|
} 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,
|
typedef void (*usb_callback_func)(void *cookie, status_t status, void *data,
|
||||||
size_t actualLength);
|
size_t actualLength);
|
||||||
|
|
||||||
|
@ -198,7 +198,32 @@ IsochronousPipe::QueueIsochronous(void *data, size_t dataLength,
|
|||||||
uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback,
|
uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback,
|
||||||
void *callbackCookie)
|
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,
|
uint16 index, uint16 length, void *data, size_t dataLength,
|
||||||
size_t *actualLength)
|
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");
|
transferResult->notify_sem = create_sem(0, "usb send request notify");
|
||||||
if (transferResult->notify_sem < B_OK)
|
if (transferResult->notify_sem < B_OK)
|
||||||
return B_NO_MORE_SEMS;
|
return B_NO_MORE_SEMS;
|
||||||
@ -290,7 +317,7 @@ ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
|
|||||||
*actualLength = transferResult->actual_length;
|
*actualLength = transferResult->actual_length;
|
||||||
|
|
||||||
result = transferResult->status;
|
result = transferResult->status;
|
||||||
free(transferResult);
|
delete transferResult;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,7 +331,7 @@ ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
|
|||||||
transferResult->actual_length = actualLength;
|
transferResult->actual_length = actualLength;
|
||||||
if (release_sem(transferResult->notify_sem) < B_OK) {
|
if (release_sem(transferResult->notify_sem) < B_OK) {
|
||||||
// the request has timed out already - cleanup after us
|
// the request has timed out already - cleanup after us
|
||||||
free(transferResult);
|
delete transferResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
* Michael Lotz <mmlr@mlotz.ch>
|
* Michael Lotz <mmlr@mlotz.ch>
|
||||||
* Niels S. Reedijk
|
* Niels S. Reedijk
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "usb_p.h"
|
#include "usb_p.h"
|
||||||
|
|
||||||
|
|
||||||
@ -21,7 +20,9 @@ Transfer::Transfer(Pipe *pipe)
|
|||||||
fClonedArea(-1),
|
fClonedArea(-1),
|
||||||
fCallback(NULL),
|
fCallback(NULL),
|
||||||
fCallbackCookie(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
|
void
|
||||||
Transfer::SetData(uint8 *data, size_t dataLength)
|
Transfer::SetData(uint8 *data, size_t dataLength)
|
||||||
{
|
{
|
||||||
@ -56,6 +64,12 @@ Transfer::SetData(uint8 *data, size_t dataLength)
|
|||||||
|
|
||||||
if (data && dataLength > 0)
|
if (data && dataLength > 0)
|
||||||
fVectorCount = 1;
|
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,
|
fCallback(fCallbackCookie, status, fBaseAddress,
|
||||||
fActualLength + actualLength);
|
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_DEVICE 0x00000040
|
||||||
#define USB_OBJECT_HUB 0x00000080
|
#define USB_OBJECT_HUB 0x00000080
|
||||||
|
|
||||||
#define USB_MAX_FRAGMENT_SIZE B_PAGE_SIZE * 96
|
|
||||||
|
|
||||||
class Stack {
|
class Stack {
|
||||||
public:
|
public:
|
||||||
@ -521,6 +520,9 @@ public:
|
|||||||
void SetRequestData(usb_request_data *data);
|
void SetRequestData(usb_request_data *data);
|
||||||
usb_request_data *RequestData() { return fRequestData; };
|
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);
|
void SetData(uint8 *buffer, size_t length);
|
||||||
uint8 *Data() { return (uint8 *)fData.iov_base; };
|
uint8 *Data() { return (uint8 *)fData.iov_base; };
|
||||||
size_t DataLength() { return fData.iov_len; };
|
size_t DataLength() { return fData.iov_len; };
|
||||||
@ -530,6 +532,8 @@ public:
|
|||||||
size_t VectorCount() { return fVectorCount; };
|
size_t VectorCount() { return fVectorCount; };
|
||||||
size_t VectorLength();
|
size_t VectorLength();
|
||||||
|
|
||||||
|
uint16 Bandwidth() { return fBandwidth; };
|
||||||
|
|
||||||
bool IsFragmented() { return fFragmented; };
|
bool IsFragmented() { return fFragmented; };
|
||||||
void AdvanceByFragment(size_t actualLength);
|
void AdvanceByFragment(size_t actualLength);
|
||||||
|
|
||||||
@ -542,6 +546,8 @@ public:
|
|||||||
void Finished(uint32 status, size_t actualLength);
|
void Finished(uint32 status, size_t actualLength);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
status_t _CalculateBandwidth();
|
||||||
|
|
||||||
// Data that is related to the transfer
|
// Data that is related to the transfer
|
||||||
Pipe *fPipe;
|
Pipe *fPipe;
|
||||||
iovec fData;
|
iovec fData;
|
||||||
@ -558,6 +564,15 @@ private:
|
|||||||
|
|
||||||
// For control transfers
|
// For control transfers
|
||||||
usb_request_data *fRequestData;
|
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
|
#endif
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <util/kernel_cpp.h>
|
#include <util/kernel_cpp.h>
|
||||||
|
|
||||||
#define USB_MAX_AREAS 8
|
#define USB_MAX_AREAS 8
|
||||||
|
#define USB_MAX_FRAGMENT_SIZE B_PAGE_SIZE * 96
|
||||||
|
|
||||||
#define USB_DELAY_BUS_RESET 100000
|
#define USB_DELAY_BUS_RESET 100000
|
||||||
#define USB_DELAY_DEVICE_POWER_UP 300000
|
#define USB_DELAY_DEVICE_POWER_UP 300000
|
||||||
@ -28,6 +29,11 @@
|
|||||||
#define USB_DELAY_FIRST_EXPLORE 5000000
|
#define USB_DELAY_FIRST_EXPLORE 5000000
|
||||||
#define USB_DELAY_HUB_EXPLORE 1000000
|
#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)
|
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
|
struct usb_hub_descriptor
|
||||||
{
|
{
|
||||||
uint8 length;
|
uint8 length;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* Authors:
|
* Authors:
|
||||||
* Michael Lotz <mmlr@mlotz.ch>
|
* Michael Lotz <mmlr@mlotz.ch>
|
||||||
* Niels S. Reedijk
|
* Niels S. Reedijk
|
||||||
|
* Salvatore Benedetto <salvatore.benedetto@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <module.h>
|
#include <module.h>
|
||||||
@ -296,11 +297,15 @@ UHCI::UHCI(pci_info *info, Stack *stack)
|
|||||||
fFrameArea(-1),
|
fFrameArea(-1),
|
||||||
fFrameList(NULL),
|
fFrameList(NULL),
|
||||||
fQueueCount(0),
|
fQueueCount(0),
|
||||||
fQueues(NULL),
|
fQueues(NULL),
|
||||||
fFirstTransfer(NULL),
|
fFirstTransfer(NULL),
|
||||||
fLastTransfer(NULL),
|
fLastTransfer(NULL),
|
||||||
fFinishThread(-1),
|
fFinishThread(-1),
|
||||||
fStopFinishThread(false),
|
fStopFinishThread(false),
|
||||||
|
fFirstIsochronousTransfer(NULL),
|
||||||
|
fLastIsochronousTransfer(NULL),
|
||||||
|
fFinishIsochronousThread(-1),
|
||||||
|
fStopFinishIsochronousThread(false),
|
||||||
fRootHub(NULL),
|
fRootHub(NULL),
|
||||||
fRootHubAddress(0),
|
fRootHubAddress(0),
|
||||||
fPortResetChange(0)
|
fPortResetChange(0)
|
||||||
@ -391,7 +396,7 @@ UHCI::UHCI(pci_info *info, Stack *stack)
|
|||||||
fQueues[fQueueCount - 1]->TerminateByStrayDescriptor();
|
fQueues[fQueueCount - 1]->TerminateByStrayDescriptor();
|
||||||
|
|
||||||
// Create the array that will keep bandwidth information
|
// 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++) {
|
for (int32 i = 0; i < NUMBER_OF_FRAMES; i++) {
|
||||||
fFrameList[i] = fQueues[UHCI_INTERRUPT_QUEUE]->PhysicalAddress()
|
fFrameList[i] = fQueues[UHCI_INTERRUPT_QUEUE]->PhysicalAddress()
|
||||||
@ -399,6 +404,10 @@ UHCI::UHCI(pci_info *info, Stack *stack)
|
|||||||
fFrameBandwidth[i] = MAX_AVAILABLE_BANDWIDTH;
|
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
|
// create semaphore the finisher thread will wait for
|
||||||
fFinishTransfersSem = create_sem(0, "UHCI Finish Transfers");
|
fFinishTransfersSem = create_sem(0, "UHCI Finish Transfers");
|
||||||
if (fFinishTransfersSem < B_OK) {
|
if (fFinishTransfersSem < B_OK) {
|
||||||
@ -411,6 +420,18 @@ UHCI::UHCI(pci_info *info, Stack *stack)
|
|||||||
"uhci finish thread", B_URGENT_DISPLAY_PRIORITY, (void *)this);
|
"uhci finish thread", B_URGENT_DISPLAY_PRIORITY, (void *)this);
|
||||||
resume_thread(fFinishThread);
|
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
|
// Install the interrupt handler
|
||||||
TRACE(("usb_uhci: installing interrupt handler\n"));
|
TRACE(("usb_uhci: installing interrupt handler\n"));
|
||||||
install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line,
|
install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line,
|
||||||
@ -429,8 +450,19 @@ UHCI::~UHCI()
|
|||||||
{
|
{
|
||||||
int32 result = 0;
|
int32 result = 0;
|
||||||
fStopFinishThread = true;
|
fStopFinishThread = true;
|
||||||
|
fStopFinishIsochronousThread = true;
|
||||||
delete_sem(fFinishTransfersSem);
|
delete_sem(fFinishTransfersSem);
|
||||||
wait_for_thread(fFinishThread, &result);
|
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();
|
Lock();
|
||||||
transfer_data *transfer = fFirstTransfer;
|
transfer_data *transfer = fFirstTransfer;
|
||||||
@ -445,6 +477,8 @@ UHCI::~UHCI()
|
|||||||
|
|
||||||
delete [] fQueues;
|
delete [] fQueues;
|
||||||
delete [] fFrameBandwidth;
|
delete [] fFrameBandwidth;
|
||||||
|
delete [] fFirstIsochronousDescriptor;
|
||||||
|
delete [] fLastIsochronousDescriptor;
|
||||||
delete fRootHub;
|
delete fRootHub;
|
||||||
delete_area(fFrameArea);
|
delete_area(fFrameArea);
|
||||||
|
|
||||||
@ -551,6 +585,9 @@ UHCI::SubmitTransfer(Transfer *transfer)
|
|||||||
status_t
|
status_t
|
||||||
UHCI::CancelQueuedTransfers(Pipe *pipe)
|
UHCI::CancelQueuedTransfers(Pipe *pipe)
|
||||||
{
|
{
|
||||||
|
if (pipe->Type() & USB_OBJECT_ISO_PIPE)
|
||||||
|
return CancelQueuedIsochronousTransfers(pipe);
|
||||||
|
|
||||||
if (!Lock())
|
if (!Lock())
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|
||||||
@ -569,7 +606,7 @@ UHCI::CancelQueuedTransfers(Pipe *pipe)
|
|||||||
last->link = next;
|
last->link = next;
|
||||||
else
|
else
|
||||||
fFirstTransfer = next;
|
fFirstTransfer = next;
|
||||||
|
|
||||||
if (fLastTransfer == current)
|
if (fLastTransfer == current)
|
||||||
fLastTransfer = last;
|
fLastTransfer = last;
|
||||||
|
|
||||||
@ -582,11 +619,36 @@ UHCI::CancelQueuedTransfers(Pipe *pipe)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Unlock();
|
Unlock();
|
||||||
|
|
||||||
return B_OK;
|
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
|
status_t
|
||||||
UHCI::SubmitRequest(Transfer *transfer)
|
UHCI::SubmitRequest(Transfer *transfer)
|
||||||
{
|
{
|
||||||
@ -685,7 +747,6 @@ UHCI::AddPendingTransfer(Transfer *transfer, Queue *queue,
|
|||||||
data->transfer_queue = transferQueue;
|
data->transfer_queue = transferQueue;
|
||||||
data->first_descriptor = firstDescriptor;
|
data->first_descriptor = firstDescriptor;
|
||||||
data->data_descriptor = dataDescriptor;
|
data->data_descriptor = dataDescriptor;
|
||||||
data->user_area = -1;
|
|
||||||
data->incoming = directionIn;
|
data->incoming = directionIn;
|
||||||
data->link = NULL;
|
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
|
status_t
|
||||||
UHCI::SubmitIsochronous(Transfer *transfer)
|
UHCI::SubmitIsochronous(Transfer *transfer)
|
||||||
{
|
{
|
||||||
/*
|
Pipe *pipe = transfer->TransferPipe();
|
||||||
* This is the main isochronous method.
|
bool directionIn = (pipe->Direction() == Pipe::In);
|
||||||
* Here we attach one Transfer Descriptor (TD) per frame by starting
|
usb_isochronous_data *isochronousData = transfer->IsochronousData();
|
||||||
* at the position specified by StartingFrameNumber. If there is
|
size_t packetSize = transfer->DataLength();
|
||||||
* not enought bandwidth at that entry number, we find the next
|
size_t restSize = packetSize % isochronousData->packet_count;
|
||||||
* available one.
|
packetSize /= isochronousData->packet_count;
|
||||||
* The TD is obviously appended to the latest existing isochronous
|
uint16 currentFrame;
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
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())
|
if (!Lock())
|
||||||
continue;
|
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 *lastTransfer = NULL;
|
||||||
transfer_data *transfer = fFirstTransfer;
|
transfer_data *transfer = fFirstTransfer;
|
||||||
Unlock();
|
Unlock();
|
||||||
@ -767,7 +1022,9 @@ UHCI::FinishTransfers()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status & TD_ERROR_MASK) {
|
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
|
// an error occured. we have to remove the
|
||||||
// transfer from the queue and clean up
|
// 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
|
void
|
||||||
UHCI::GlobalReset()
|
UHCI::GlobalReset()
|
||||||
{
|
{
|
||||||
@ -1292,9 +1635,14 @@ UHCI::CreateDescriptor(Pipe *pipe, uint8 direction, size_t bufferSize)
|
|||||||
}
|
}
|
||||||
|
|
||||||
result->this_phy = (addr_t)physicalAddress;
|
result->this_phy = (addr_t)physicalAddress;
|
||||||
result->status = TD_STATUS_ACTIVE | TD_CONTROL_3_ERRORS;
|
result->status = TD_STATUS_ACTIVE;
|
||||||
if (direction == TD_TOKEN_IN)
|
if (pipe->Type() & USB_OBJECT_ISO_PIPE)
|
||||||
result->status |= TD_CONTROL_SPD;
|
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)
|
if (pipe->Speed() == USB_SPEED_LOWSPEED)
|
||||||
result->status |= TD_CONTROL_LOWSPEED;
|
result->status |= TD_CONTROL_LOWSPEED;
|
||||||
|
|
||||||
@ -1315,7 +1663,7 @@ UHCI::CreateDescriptor(Pipe *pipe, uint8 direction, size_t bufferSize)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fStack->AllocateChunk(&result->buffer_log, &result->buffer_phy,
|
if (fStack->AllocateChunk(&result->buffer_log, (void **)&result->buffer_phy,
|
||||||
bufferSize) < B_OK) {
|
bufferSize) < B_OK) {
|
||||||
TRACE_ERROR(("usb_uhci: unable to allocate space for the buffer\n"));
|
TRACE_ERROR(("usb_uhci: unable to allocate space for the buffer\n"));
|
||||||
fStack->FreeChunk(result, (void *)result->this_phy, sizeof(uhci_td));
|
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,
|
size_t length = min_c(current->buffer_size - bufferOffset,
|
||||||
vector[vectorIndex].iov_len - vectorOffset);
|
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,
|
memcpy((uint8 *)current->buffer_log + bufferOffset,
|
||||||
(uint8 *)vector[vectorIndex].iov_base + vectorOffset, length);
|
(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 (vectorOffset >= vector[vectorIndex].iov_len) {
|
||||||
if (++vectorIndex >= vectorCount) {
|
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;
|
return actualLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1480,7 +1831,9 @@ UHCI::ReadDescriptorChain(uhci_td *topDescriptor, iovec *vector,
|
|||||||
size_t length = min_c(bufferSize - bufferOffset,
|
size_t length = min_c(bufferSize - bufferOffset,
|
||||||
vector[vectorIndex].iov_len - vectorOffset);
|
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,
|
memcpy((uint8 *)vector[vectorIndex].iov_base + vectorOffset,
|
||||||
(uint8 *)current->buffer_log + bufferOffset, length);
|
(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
|
inline void
|
||||||
UHCI::WriteReg8(uint32 reg, uint8 value)
|
UHCI::WriteReg8(uint32 reg, uint8 value)
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* Authors:
|
* Authors:
|
||||||
* Michael Lotz <mmlr@mlotz.ch>
|
* Michael Lotz <mmlr@mlotz.ch>
|
||||||
* Niels S. Reedijk
|
* Niels S. Reedijk
|
||||||
|
* Salvatore Benedetto <salvatore.benedetto@gmail.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef UHCI_H
|
#ifndef UHCI_H
|
||||||
@ -61,12 +62,27 @@ typedef struct transfer_data_s {
|
|||||||
uhci_qh *transfer_queue;
|
uhci_qh *transfer_queue;
|
||||||
uhci_td *first_descriptor;
|
uhci_td *first_descriptor;
|
||||||
uhci_td *data_descriptor;
|
uhci_td *data_descriptor;
|
||||||
area_id user_area;
|
|
||||||
bool incoming;
|
bool incoming;
|
||||||
transfer_data_s *link;
|
transfer_data_s *link;
|
||||||
} transfer_data;
|
} 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 {
|
class UHCI : public BusManager {
|
||||||
public:
|
public:
|
||||||
UHCI(pci_info *info, Stack *stack);
|
UHCI(pci_info *info, Stack *stack);
|
||||||
@ -75,6 +91,7 @@ public:
|
|||||||
status_t Start();
|
status_t Start();
|
||||||
virtual status_t SubmitTransfer(Transfer *transfer);
|
virtual status_t SubmitTransfer(Transfer *transfer);
|
||||||
virtual status_t CancelQueuedTransfers(Pipe *pipe);
|
virtual status_t CancelQueuedTransfers(Pipe *pipe);
|
||||||
|
status_t CancelQueuedIsochronousTransfers(Pipe *pipe);
|
||||||
status_t SubmitRequest(Transfer *transfer);
|
status_t SubmitRequest(Transfer *transfer);
|
||||||
status_t SubmitIsochronous(Transfer *transfer);
|
status_t SubmitIsochronous(Transfer *transfer);
|
||||||
|
|
||||||
@ -103,6 +120,11 @@ static int32 InterruptHandler(void *data);
|
|||||||
uhci_td *firstDescriptor,
|
uhci_td *firstDescriptor,
|
||||||
uhci_td *dataDescriptor,
|
uhci_td *dataDescriptor,
|
||||||
bool directionIn);
|
bool directionIn);
|
||||||
|
status_t AddPendingIsochronousTransfer(
|
||||||
|
Transfer *transfer,
|
||||||
|
uhci_td **isoRequest,
|
||||||
|
bool directionIn);
|
||||||
|
|
||||||
static int32 FinishThread(void *data);
|
static int32 FinishThread(void *data);
|
||||||
void FinishTransfers();
|
void FinishTransfers();
|
||||||
|
|
||||||
@ -110,10 +132,25 @@ static int32 FinishThread(void *data);
|
|||||||
uhci_td **_firstDescriptor,
|
uhci_td **_firstDescriptor,
|
||||||
uhci_qh **_transferQueue);
|
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
|
// Transfer queue functions
|
||||||
uhci_qh *CreateTransferQueue(uhci_td *descriptor);
|
uhci_qh *CreateTransferQueue(uhci_td *descriptor);
|
||||||
void FreeTransferQueue(uhci_qh *queueHead);
|
void FreeTransferQueue(uhci_qh *queueHead);
|
||||||
|
|
||||||
|
bool LockIsochronous();
|
||||||
|
void UnlockIsochronous();
|
||||||
|
|
||||||
// Descriptor functions
|
// Descriptor functions
|
||||||
uhci_td *CreateDescriptor(Pipe *pipe,
|
uhci_td *CreateDescriptor(Pipe *pipe,
|
||||||
uint8 direction,
|
uint8 direction,
|
||||||
@ -137,6 +174,9 @@ static int32 FinishThread(void *data);
|
|||||||
uint8 *lastDataToggle);
|
uint8 *lastDataToggle);
|
||||||
size_t ReadActualLength(uhci_td *topDescriptor,
|
size_t ReadActualLength(uhci_td *topDescriptor,
|
||||||
uint8 *lastDataToggle);
|
uint8 *lastDataToggle);
|
||||||
|
void ReadIsochronousDescriptorChain(
|
||||||
|
isochronous_transfer_data *transfer,
|
||||||
|
iovec *vector);
|
||||||
|
|
||||||
// Register functions
|
// Register functions
|
||||||
inline void WriteReg8(uint32 reg, uint8 value);
|
inline void WriteReg8(uint32 reg, uint8 value);
|
||||||
@ -154,11 +194,17 @@ static pci_module_info *sPCIModule;
|
|||||||
|
|
||||||
// Frame list memory
|
// Frame list memory
|
||||||
area_id fFrameArea;
|
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
|
// 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
|
// Queues
|
||||||
int32 fQueueCount;
|
int32 fQueueCount;
|
||||||
@ -171,6 +217,13 @@ static pci_module_info *sPCIModule;
|
|||||||
thread_id fFinishThread;
|
thread_id fFinishThread;
|
||||||
bool fStopFinishThread;
|
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
|
// Root hub
|
||||||
UHCIRootHub *fRootHub;
|
UHCIRootHub *fRootHub;
|
||||||
uint8 fRootHubAddress;
|
uint8 fRootHubAddress;
|
||||||
|
@ -87,10 +87,10 @@
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
// Hardware part
|
// 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 status; // Status field
|
||||||
uint32 token; // Contains the packet header (where it needs to be sent)
|
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
|
// Software part
|
||||||
addr_t this_phy; // A physical pointer to this address
|
addr_t this_phy; // A physical pointer to this address
|
||||||
void *link_log; // Pointer to the next logical TD/QT
|
void *link_log; // Pointer to the next logical TD/QT
|
||||||
@ -98,6 +98,8 @@ typedef struct
|
|||||||
size_t buffer_size; // Size of the buffer
|
size_t buffer_size; // Size of the buffer
|
||||||
} uhci_td;
|
} uhci_td;
|
||||||
|
|
||||||
|
#define TD_NEXT_IS_QH 0x02
|
||||||
|
|
||||||
// Control and Status
|
// Control and Status
|
||||||
#define TD_CONTROL_SPD (1 << 29)
|
#define TD_CONTROL_SPD (1 << 29)
|
||||||
#define TD_CONTROL_3_ERRORS (3 << 27)
|
#define TD_CONTROL_3_ERRORS (3 << 27)
|
||||||
@ -142,8 +144,8 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
// Hardware part
|
// Hardware part
|
||||||
addr_t link_phy; // Link to the next TD/QH
|
uint32 link_phy; // Link to the next TD/QH
|
||||||
addr_t element_phy; // Pointer to the first element in the queue
|
uint32 element_phy; // Pointer to the first element in the queue
|
||||||
// Software part
|
// Software part
|
||||||
addr_t this_phy; // The physical pointer to this address
|
addr_t this_phy; // The physical pointer to this address
|
||||||
void *link_log; // Pointer to the next logical TD/QH
|
void *link_log; // Pointer to the next logical TD/QH
|
||||||
|
Loading…
Reference in New Issue
Block a user