/*
* Copyright 2004-2006, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* Niels S. Reedijk
*/
#include "usb_private.h"
Pipe::Pipe(Object *parent)
: Object(parent),
fDataToggle(false),
fControllerCookie(NULL)
{
// all other init is to be done in InitCommon()
}
Pipe::~Pipe()
{
CancelQueuedTransfers(true);
GetBusManager()->NotifyPipeChange(this, USB_CHANGE_DESTROYED);
}
void
Pipe::InitCommon(int8 deviceAddress, uint8 endpointAddress, usb_speed speed,
pipeDirection direction, size_t maxPacketSize, uint8 interval,
int8 hubAddress, uint8 hubPort)
{
fDeviceAddress = deviceAddress;
fEndpointAddress = endpointAddress;
fSpeed = speed;
fDirection = direction;
fMaxPacketSize = maxPacketSize;
fInterval = interval;
fHubAddress = hubAddress;
fHubPort = hubPort;
fMaxBurst = 0;
fBytesPerInterval = 0;
GetBusManager()->NotifyPipeChange(this, USB_CHANGE_CREATED);
}
void
Pipe::InitSuperSpeed(uint8 maxBurst, uint16 bytesPerInterval)
{
fMaxBurst = maxBurst;
fBytesPerInterval = bytesPerInterval;
}
void
Pipe::SetHubInfo(int8 address, uint8 port)
{
fHubAddress = address;
fHubPort = port;
}
status_t
Pipe::SubmitTransfer(Transfer *transfer)
{
// ToDo: keep track of all submited transfers to be able to cancel them
return GetBusManager()->SubmitTransfer(transfer);
}
status_t
Pipe::CancelQueuedTransfers(bool force)
{
return GetBusManager()->CancelQueuedTransfers(this, force);
}
status_t
Pipe::SetFeature(uint16 selector)
{
TRACE("set feature %u\n", selector);
Device *device = (Device *)Parent();
if (device->InitCheck() != B_OK)
return B_NO_INIT;
return device->DefaultPipe()->SendRequest(
USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
USB_REQUEST_SET_FEATURE,
selector,
fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
: USB_ENDPOINT_ADDR_DIR_OUT),
0,
NULL,
0,
NULL);
}
status_t
Pipe::ClearFeature(uint16 selector)
{
Device *device = (Device *)Parent();
if (device->InitCheck() != B_OK)
return B_NO_INIT;
// clearing a stalled condition resets the data toggle
if (selector == USB_FEATURE_ENDPOINT_HALT)
SetDataToggle(false);
TRACE("clear feature %u\n", selector);
return device->DefaultPipe()->SendRequest(
USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT,
USB_REQUEST_CLEAR_FEATURE,
selector,
fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
: USB_ENDPOINT_ADDR_DIR_OUT),
0,
NULL,
0,
NULL);
}
status_t
Pipe::GetStatus(uint16 *status)
{
TRACE("get status\n");
Device *device = (Device *)Parent();
if (device->InitCheck() != B_OK)
return B_NO_INIT;
return device->DefaultPipe()->SendRequest(
USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_IN,
USB_REQUEST_GET_STATUS,
0,
fEndpointAddress | (fDirection == In ? USB_ENDPOINT_ADDR_DIR_IN
: USB_ENDPOINT_ADDR_DIR_OUT),
2,
(void *)status,
2,
NULL);
}
//
// #pragma mark -
//
InterruptPipe::InterruptPipe(Object *parent)
: Pipe(parent)
{
}
status_t
InterruptPipe::QueueInterrupt(void *data, size_t dataLength,
usb_callback_func callback, void *callbackCookie)
{
if (dataLength > 0 && data == NULL)
return B_BAD_VALUE;
Transfer *transfer = new(std::nothrow) Transfer(this);
if (!transfer)
return B_NO_MEMORY;
transfer->SetData((uint8 *)data, dataLength);
transfer->SetCallback(callback, callbackCookie);
status_t result = GetBusManager()->SubmitTransfer(transfer);
if (result < B_OK)
delete transfer;
return result;
}
//
// #pragma mark -
//
BulkPipe::BulkPipe(Object *parent)
: Pipe(parent)
{
}
void
BulkPipe::InitCommon(int8 deviceAddress, uint8 endpointAddress,
usb_speed speed, pipeDirection direction, size_t maxPacketSize,
uint8 interval, int8 hubAddress, uint8 hubPort)
{
// some devices have bogus descriptors
if (speed == USB_SPEED_HIGHSPEED && maxPacketSize != 512)
maxPacketSize = 512;
Pipe::InitCommon(deviceAddress, endpointAddress, speed, direction,
maxPacketSize, interval, hubAddress, hubPort);
}
status_t
BulkPipe::QueueBulk(void *data, size_t dataLength, usb_callback_func callback,
void *callbackCookie)
{
if (dataLength > 0 && data == NULL)
return B_BAD_VALUE;
Transfer *transfer = new(std::nothrow) Transfer(this);
if (!transfer)
return B_NO_MEMORY;
transfer->SetData((uint8 *)data, dataLength);
transfer->SetCallback(callback, callbackCookie);
status_t result = GetBusManager()->SubmitTransfer(transfer);
if (result < B_OK)
delete transfer;
return result;
}
status_t
BulkPipe::QueueBulkV(iovec *vector, size_t vectorCount,
usb_callback_func callback, void *callbackCookie, bool physical)
{
if (vectorCount > 0 && vector == NULL)
return B_BAD_VALUE;
Transfer *transfer = new(std::nothrow) Transfer(this);
if (!transfer)
return B_NO_MEMORY;
transfer->SetPhysical(physical);
transfer->SetVector(vector, vectorCount);
transfer->SetCallback(callback, callbackCookie);
status_t result = GetBusManager()->SubmitTransfer(transfer);
if (result < B_OK)
delete transfer;
return result;
}
//
// #pragma mark -
//
IsochronousPipe::IsochronousPipe(Object *parent)
: Pipe(parent),
fMaxQueuedPackets(0),
fMaxBufferDuration(0),
fSampleSize(0)
{
}
status_t
IsochronousPipe::QueueIsochronous(void *data, size_t dataLength,
usb_iso_packet_descriptor *packetDesc, uint32 packetCount,
uint32 *startingFrameNumber, uint32 flags, usb_callback_func callback,
void *callbackCookie)
{
if ((dataLength > 0 && data == NULL)
|| (packetCount > 0 && packetDesc == NULL))
return B_BAD_VALUE;
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;
}
status_t
IsochronousPipe::SetPipePolicy(uint8 maxQueuedPackets,
uint16 maxBufferDurationMS, uint16 sampleSize)
{
if (maxQueuedPackets == fMaxQueuedPackets
|| maxBufferDurationMS == fMaxBufferDuration
|| sampleSize == fSampleSize)
return B_OK;
fMaxQueuedPackets = maxQueuedPackets;
fMaxBufferDuration = maxBufferDurationMS;
fSampleSize = sampleSize;
GetBusManager()->NotifyPipeChange(this, USB_CHANGE_PIPE_POLICY_CHANGED);
return B_OK;
}
status_t
IsochronousPipe::GetPipePolicy(uint8 *maxQueuedPackets,
uint16 *maxBufferDurationMS, uint16 *sampleSize)
{
if (maxQueuedPackets)
*maxQueuedPackets = fMaxQueuedPackets;
if (maxBufferDurationMS)
*maxBufferDurationMS = fMaxBufferDuration;
if (sampleSize)
*sampleSize = fSampleSize;
return B_OK;
}
//
// #pragma mark -
//
ControlPipe::ControlPipe(Object *parent)
: Pipe(parent),
fNotifySem(-1)
{
mutex_init(&fSendRequestLock, "control pipe send request");
}
ControlPipe::~ControlPipe()
{
if (fNotifySem >= 0)
delete_sem(fNotifySem);
mutex_lock(&fSendRequestLock);
mutex_destroy(&fSendRequestLock);
}
void
ControlPipe::InitCommon(int8 deviceAddress, uint8 endpointAddress,
usb_speed speed, pipeDirection direction, size_t maxPacketSize,
uint8 interval, int8 hubAddress, uint8 hubPort)
{
// The USB 2.0 spec section 5.5.3 gives fixed max packet sizes for the
// different speeds. The USB 3.1 specs defines the max packet size to a
// fixed 512 for control endpoints in 9.6.6. Some devices ignore these
// values and use bogus ones, so we restrict them here.
switch (speed) {
case USB_SPEED_LOWSPEED:
maxPacketSize = 8;
break;
case USB_SPEED_HIGHSPEED:
maxPacketSize = 64;
break;
case USB_SPEED_SUPER:
maxPacketSize = 512;
break;
default:
break;
}
Pipe::InitCommon(deviceAddress, endpointAddress, speed, direction,
maxPacketSize, interval, hubAddress, hubPort);
}
status_t
ControlPipe::SendRequest(uint8 requestType, uint8 request, uint16 value,
uint16 index, uint16 length, void *data, size_t dataLength,
size_t *actualLength)
{
status_t result = mutex_lock(&fSendRequestLock);
if (result != B_OK)
return result;
if (fNotifySem < 0) {
fNotifySem = create_sem(0, "usb send request notify");
if (fNotifySem < 0) {
mutex_unlock(&fSendRequestLock);
return B_NO_MORE_SEMS;
}
}
result = QueueRequest(requestType, request, value, index, length, data,
dataLength, SendRequestCallback, this);
if (result < B_OK) {
mutex_unlock(&fSendRequestLock);
return result;
}
// The sem will be released unconditionally in the callback after the
// result data was filled in. Use a 2 seconds timeout for control transfers.
if (acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 2000000) < B_OK) {
TRACE_ERROR("timeout waiting for queued request to complete\n");
CancelQueuedTransfers(false);
// After the above cancel returns it is guaranteed that the callback
// has been invoked. Therefore we can simply grab that released
// semaphore again to clean up.
acquire_sem_etc(fNotifySem, 1, B_RELATIVE_TIMEOUT, 0);
if (actualLength)
*actualLength = 0;
mutex_unlock(&fSendRequestLock);
return B_TIMED_OUT;
}
if (actualLength)
*actualLength = fActualLength;
mutex_unlock(&fSendRequestLock);
return fTransferStatus;
}
void
ControlPipe::SendRequestCallback(void *cookie, status_t status, void *data,
size_t actualLength)
{
ControlPipe *pipe = (ControlPipe *)cookie;
pipe->fTransferStatus = status;
pipe->fActualLength = actualLength;
release_sem(pipe->fNotifySem);
}
status_t
ControlPipe::QueueRequest(uint8 requestType, uint8 request, uint16 value,
uint16 index, uint16 length, void *data, size_t dataLength,
usb_callback_func callback, void *callbackCookie)
{
if (dataLength > 0 && data == NULL)
return B_BAD_VALUE;
usb_request_data *requestData = new(std::nothrow) usb_request_data;
if (!requestData)
return B_NO_MEMORY;
requestData->RequestType = requestType;
requestData->Request = request;
requestData->Value = value;
requestData->Index = index;
requestData->Length = length;
Transfer *transfer = new(std::nothrow) Transfer(this);
if (!transfer) {
delete requestData;
return B_NO_MEMORY;
}
transfer->SetRequestData(requestData);
transfer->SetData((uint8 *)data, dataLength);
transfer->SetCallback(callback, callbackCookie);
status_t result = GetBusManager()->SubmitTransfer(transfer);
if (result < B_OK)
delete transfer;
return result;
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fTransferStatus, fActualLength.