#include <libkern/OSByteOrder.h>
#include <IOKit/IOService.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/usb/IOUSBController.h>
#include <IOKit/usb/IOUSBControllerV2.h>
#include <IOKit/usb/USBHub.h>
#include <IOKit/usb/IOUSBDevice.h>
#include <IOKit/usb/IOUSBPipe.h>
#include <IOKit/usb/IOUSBNub.h>
#include <IOKit/usb/IOUSBLog.h>
#include "IOUSBInterfaceUserClient.h"
#include "USBTracepoints.h"
#define super OSObject
#ifndef kIOUserClientCrossEndianCompatibleKey
#define kIOUserClientCrossEndianCompatibleKey "IOUserClientCrossEndianCompatible"
#endif
#define _DEVICE _expansionData->_device
#define _CORRECTSTATUS _expansionData->_correctStatus
#define _SPEED _expansionData->_speed
#define _INTERFACE _expansionData->_interface
#define _CROSSENDIANCOMPATIBLE _expansionData->_crossEndianCompatible
#define _LOCATIONID _expansionData->_locationID
#define _OUTOFSPECMPSOK _status // if non-zero, then we should ignore an out of spec MPS
#ifndef IOUSBPIPE_USE_KPRINTF
#define IOUSBPIPE_USE_KPRINTF 0
#endif
#if IOUSBPIPE_USE_KPRINTF
#undef USBLog
#undef USBError
void kprintf(const char *format, ...) __attribute__((format(printf, 1, 2)));
#define USBLog( LEVEL, FORMAT, ARGS... ) if ((LEVEL) <= IOUSBPIPE_USE_KPRINTF) { kprintf( FORMAT "\n", ## ARGS ) ; }
#define USBError( LEVEL, FORMAT, ARGS... ) { kprintf( FORMAT "\n", ## ARGS ) ; }
#endif
OSDefineMetaClassAndStructors(IOUSBPipe, OSObject)
#pragma mark Intializers
bool
IOUSBPipe::InitToEndpoint(const IOUSBEndpointDescriptor *ed, UInt8 speed, USBDeviceAddress address, IOUSBController * controller, IOUSBDevice * device, IOUSBInterface * interface)
{
IOReturn err;
USBTrace_Start( kUSBTPipe, kTPPipeInitToEndpoint, (uintptr_t)this, (uintptr_t)ed, speed, address);
if ( !super::init() || ed == 0)
return (false);
if (!_expansionData)
{
_expansionData = (ExpansionData *)IOMalloc(sizeof(ExpansionData));
if (!_expansionData)
return false;
bzero(_expansionData, sizeof(ExpansionData));
}
_controller = controller;
_descriptor = ed;
_endpoint.number = ed->bEndpointAddress & kUSBPipeIDMask;
_endpoint.transferType = ed->bmAttributes & 0x03;
if (_endpoint.transferType == kUSBControl)
_endpoint.direction = kUSBAnyDirn;
else
_endpoint.direction = (ed->bEndpointAddress & 0x80) ? kUSBIn : kUSBOut;
_endpoint.maxPacketSize = mungeMaxPacketSize(USBToHostWord(ed->wMaxPacketSize));
_endpoint.interval = ed->bInterval;
_address = address;
_SPEED = speed;
_DEVICE = device;
_INTERFACE = interface;
if ( _DEVICE )
{
OSNumber * location = OSDynamicCast(OSNumber, _DEVICE->getProperty(kUSBDevicePropertyLocationID));
if (location)
{
_LOCATIONID = location->unsigned32BitValue();
}
}
USBTrace(kUSBTPipe, kTPPipeInitToEndpoint, (uintptr_t)this, (uintptr_t)_controller, _endpoint.transferType, _endpoint.maxPacketSize);
if ( (_SPEED == kUSBDeviceSpeedHigh) && (_endpoint.transferType == kUSBBulk) && (_endpoint.maxPacketSize != 512) )
{
if ( _OUTOFSPECMPSOK == 0 )
{
USBError(1, "Endpoint 0x%x of the USB device \"%s\" at location 0x%x: converting Bulk MPS from %d to 512 (USB 2.0 Spec section 5.8.3)", ed->bEndpointAddress, _DEVICE ? _DEVICE->getName() : "Unnamed", (uint32_t)_LOCATIONID, _endpoint.maxPacketSize);
_endpoint.maxPacketSize = 512;
}
else
{
USBLog(5, "IOUSBPipe[%p]::InitToEndpoint: High Speed Bulk pipe with %d MPS, but property says we should still use it.", this, _endpoint.maxPacketSize);
}
}
err = _controller->OpenPipe(_address, speed, &_endpoint);
if ((err == kIOReturnNoBandwidth) && (_endpoint.transferType == kUSBIsoc))
{
USBError(1,"There is not enough USB isochronous bandwidth to allow the device \"%s\" at location 0x%x to function in its current configuration (requested %d bytes for endpoint 0x%x )", _DEVICE ? _DEVICE->getName() : "Unnamed", (uint32_t)_LOCATIONID, _endpoint.maxPacketSize, ed->bEndpointAddress);
_endpoint.maxPacketSize = 0;
USBLog(6, "IOUSBPipe[%p]::InitToEndpoint can't get bandwidth for isoc pipe - creating 0 bandwidth pipe", this);
err = _controller->OpenPipe(_address, speed, &_endpoint);
}
if ( err != kIOReturnSuccess)
{
USBLog(3,"IOUSBPipe[%p]::InitToEndpoint Could not create pipe for endpoint (Addr: %d, numb: %d, type: %d). Error 0x%x", this, _address, _endpoint.number, _endpoint.transferType, err);
return false;
}
USBTrace_End( kUSBTPipe, kTPPipeInitToEndpoint, (uintptr_t)this, 0, 0, 0);
return true;
}
IOUSBPipe *
IOUSBPipe::ToEndpoint(const IOUSBEndpointDescriptor *ed, IOUSBDevice * device, IOUSBController *controller, IOUSBInterface * interface)
{
OSObject * propertyObj = NULL;
OSBoolean * boolObj = NULL;
IOUSBPipe * me = new IOUSBPipe;
USBLog(6, "IOUSBPipe[%p]::ToEndpoint device %p, interface %p, ep: 0x%x,0x%x,%d,%d", me, device, interface, ed->bEndpointAddress, ed->bmAttributes, ed->wMaxPacketSize, ed->bInterval);
if ( me && interface)
{
propertyObj = interface->copyProperty(kUSBOutOfSpecMPSOK);
boolObj = OSDynamicCast( OSBoolean, propertyObj );
if ( boolObj && boolObj->isTrue())
{
USBLog(6,"IOUSBPipe[%p]::ToEndpoint Device reports Out of spec MPS property and is TRUE", me);
me->_OUTOFSPECMPSOK = 0xFF;
}
if (propertyObj)
propertyObj->release();
}
if ( !me->InitToEndpoint(ed, device->GetSpeed(), device->GetAddress(), controller, device, interface) )
{
me->release();
return NULL;
}
if ( me->_INTERFACE )
{
OSObject * propertyObj = me->_INTERFACE->copyProperty(kIOUserClientCrossEndianCompatibleKey);
OSBoolean * boolObj = OSDynamicCast( OSBoolean, propertyObj );
if ( boolObj )
{
if (boolObj->isTrue() )
{
USBLog(6,"IOUSBPipe[%p]::ToEndpoint CrossEndianProperty exists and is TRUE", me);
me->_CROSSENDIANCOMPATIBLE = true;
}
else
{
USBLog(6,"IOUSBPipe[%p]::ToEndpoint CrossEndianProperty exists and is FALSE", me);
me->_CROSSENDIANCOMPATIBLE = false;
}
}
if (propertyObj)
propertyObj->release();
}
return me;
}
void
IOUSBPipe::free()
{
if (_expansionData)
{
IOFree(_expansionData, sizeof(ExpansionData));
_expansionData = NULL;
}
super::free();
}
#pragma mark IOUSBPipe State
IOReturn
IOUSBPipe::Abort(void)
{
USBLog(5,"IOUSBPipe[%p]::AbortPipe",this);
if (_CORRECTSTATUS != 0)
{
USBLog(2, "IOUSBPipe[%p]::Abort setting status to 0", this);
}
_CORRECTSTATUS = 0;
return _controller->AbortPipe(_address, &_endpoint);
}
IOReturn
IOUSBPipe::Reset(void)
{
USBLog(5,"+IOUSBPipe[%p]::ResetPipe",this);
if (_CORRECTSTATUS != 0)
{
USBLog(2, "IOUSBPipe[%p]::ResetPipe setting status to 0", this);
}
_CORRECTSTATUS = 0;
return _controller->ResetPipe(_address, &_endpoint);
}
IOReturn
IOUSBPipe::ClosePipe(void)
{
return _controller->ClosePipe(_address, &_endpoint);
}
IOReturn
IOUSBPipe::ClearStall(void)
{
return ClearPipeStall(false);
}
IOReturn
IOUSBPipe::ClearPipeStall(bool withDeviceRequest)
{
IOReturn err;
USBLog(5,"IOUSBPipe[%p]::ClearPipeStall",this);
if (_CORRECTSTATUS != 0)
{
USBLog(2, "IOUSBPipe[%p]::ClearPipeStall setting status to 0", this);
}
_CORRECTSTATUS = 0;
if ( _endpoint.transferType == kUSBIsoc )
{
USBLog(2, "IOUSBPipe[%p]::ClearPipeStall Isoch pipes never stall. Returning success", this);
return kIOReturnSuccess;
}
if ( !_DEVICE )
{
USBLog(2, "IOUSBPipe[%p]::ClearPipeStall no _DEVICE", this);
return kIOReturnNotPermitted;
}
if ( _DEVICE->isInactive() )
{
USBLog(6, "IOUSBPipe[%p]::ClearPipeStall _DEVICE is inActive(), so returning success", this);
return kIOReturnSuccess;
}
_DEVICE->retain();
err = _controller->ClearPipeStall(_address, &_endpoint);
if (err == kIOUSBClearPipeStallNotRecursive)
{
USBLog(1,"IOUSBPipe[%p]::ClearPipeStall - tried to call recursively, err = %p", this, (void*)err);
}
if (_DEVICE->GetSpeed() == kUSBDeviceSpeedHigh)
{
USBLog(5,"IOUSBPipe[%p]::ClearPipeStall - High Speed Device, no clear TT needed",this);
}
else if (!err && ( (_endpoint.transferType == kUSBBulk) || (_endpoint.transferType == kUSBControl) ) )
{
USBLog(5,"IOUSBPipe[%p]::ClearPipeStall Bulk or Control endpoint, clear TT",this);
if ( (_DEVICE != NULL) && (_DEVICE->_expansionData->_usbPlaneParent ) )
{
IOUSBHubPortClearTTParam params;
UInt8 deviceAddress; UInt8 endpointNum; UInt8 endpointType; UInt8 in;
params.portNumber = _DEVICE->_expansionData->_portNumber;
deviceAddress = _DEVICE->GetAddress();
endpointNum = _endpoint.number;
if (_endpoint.transferType == kUSBControl)
{
endpointType = 0; in = 0; }
else
{
endpointType = 2; if (_endpoint.direction == kUSBIn)
{
in = 1; }
else
{
in = 0;
}
}
params.options = deviceAddress + (endpointNum <<8) + (endpointType << 16) + (in << 24);
USBLog(6, "IOUSBPipe[%p]::ClearPipeStall calling device messageClients (kIOUSBMessageHubPortClearTT) with options: 0x%x", this, (uint32_t)params.options);
_DEVICE->_expansionData->_usbPlaneParent->retain();
(void) _DEVICE->_expansionData->_usbPlaneParent->messageClients(kIOUSBMessageHubPortClearTT, ¶ms, sizeof(params));
_DEVICE->_expansionData->_usbPlaneParent->release();
}
}
else
{
USBLog(5,"IOUSBPipe[%p]::ClearPipeStall Int or Isoc endpoint, don't clear TT (or err: 0x%x)",this, err);
}
if (!err && withDeviceRequest)
{
IOUSBDevRequest request;
IOUSBCompletion tap;
tap.target = NULL;
tap.action = &IOUSBSyncCompletion;
tap.parameter = &request.wLenDone;
USBLog(7,"IOUSBPipe[%p]::ClearPipeStall - sending request to the device", this);
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBStandard, kUSBEndpoint);
request.bRequest = kUSBRqClearFeature;
request.wValue = kUSBFeatureEndpointStall;
request.wIndex = _endpoint.number | ((_endpoint.direction == kUSBIn) ? 0x80 : 0);
request.wLenDone = request.wLength = 0;
request.pData = NULL;
err = _controller->DeviceRequest(&request, &tap, _address, 0, kUSBDefaultControlNoDataTimeoutMS, kUSBDefaultControlCompletionTimeoutMS);
}
_DEVICE->release();
return err;
}
IOReturn
IOUSBPipe::SetPipePolicy(UInt16 maxPacketSize, UInt8 maxInterval)
{
UInt8 oldInterval = _endpoint.interval;
UInt16 oldsize = _endpoint.maxPacketSize;
IOReturn err = kIOReturnSuccess;
bool change = false;
USBLog(6, "IOUSBPipe[%p]::SetPipePolicy (addr %d:%d dir: %d, type: %d) - maxPacketSize: %d, maxInterval: %d", this, _address, _endpoint.number, _endpoint.direction, _endpoint.transferType, maxPacketSize, maxInterval);
switch (_endpoint.transferType)
{
case kUSBIsoc:
if (maxPacketSize <= mungeMaxPacketSize(USBToHostWord(_descriptor->wMaxPacketSize)))
{
USBLog(6, "IOUSBPipe[%p]::SetPipePolicy - trying to change isoch pipe from %d to %d bytes", this, oldsize, maxPacketSize);
_endpoint.maxPacketSize = maxPacketSize;
err = _controller->OpenPipe(_address, _SPEED, &_endpoint);
if (err)
{
USBLog(2, "IOUSBPipe[%p]::SetPipePolicy - new OpenPipe failed with 0x%x (%s) - returning old settings", this, err, USBStringFromReturn(err));
_endpoint.maxPacketSize = oldsize;
}
}
else
{
USBLog(2, "IOUSBPipe[%p]::SetPipePolicy - requested size (%d) larger than maxPacketSize in descriptor (%d)", this, maxPacketSize, mungeMaxPacketSize(USBToHostWord(_descriptor->wMaxPacketSize)));
err = kIOReturnBadArgument;
}
break;
case kUSBInterrupt:
if ( maxInterval == 0 )
{
USBLog(2, "IOUSBPipe[%p]::SetPipePolicy - requested a maxInterval of 0, which is not legal", this);
err = kIOReturnBadArgument;
}
else
{
if ( maxInterval != _endpoint.interval )
{
USBLog(3, "IOUSBPipe[%p]::SetPipePolicy - trying to change interrupt pollingRate from %d to %d", this, oldInterval, maxInterval);
_endpoint.interval = maxInterval;
change = true;
}
else
{
USBLog(6, "IOUSBPipe[%p]::SetPipePolicy - requested maxInterval size is the same as before", this);
}
if (maxPacketSize <= mungeMaxPacketSize(USBToHostWord(_descriptor->wMaxPacketSize)))
{
USBLog(3, "IOUSBPipe[%p]::SetPipePolicy - trying to change interrupt maxPacketSize from %d to %d bytes", this, oldsize, maxPacketSize);
_endpoint.maxPacketSize = maxPacketSize;
change = true;
}
else
{
USBLog(2, "IOUSBPipe[%p]::SetPipePolicy - requested size (%d) larger than maxPacketSize in descriptor (%d)", this, maxPacketSize, mungeMaxPacketSize(USBToHostWord(_descriptor->wMaxPacketSize)));
err = kIOReturnBadArgument;
change = false;
}
if ( change )
{
err = _controller->OpenPipe(_address, _SPEED, &_endpoint);
if (err)
{
USBLog(2, "IOUSBPipe[%p]::SetPipePolicy - changing parameters failed with err = 0x%x (%s), trying to restore old settings", this, err, USBStringFromReturn(err));
_endpoint.interval = oldInterval;
_endpoint.maxPacketSize = oldsize;
err = _controller->OpenPipe(_address, _SPEED, &_endpoint);
if (err)
{
USBLog(2, "IOUSBPipe[%p]::SetPipePolicy - changing parameters failed second time with error 0x%x (%s)", this, err, USBStringFromReturn(err));
}
}
}
}
break;
default:
USBLog(2, "IOUSBPipe[%p]::SetPipePolicy - wrong type of pipe - returning kIOReturnBadArgument", this);
err = kIOReturnBadArgument;
}
return err;
}
#pragma mark Bulk Read
IOReturn
IOUSBPipe::Read(IOMemoryDescriptor *buffer, IOUSBCompletion *completion, IOByteCount *bytesRead)
{
USBLog(7, "IOUSBPipe[%p]::Read #1", this);
return Read(buffer, 0, 0, completion, bytesRead);
}
IOReturn
IOUSBPipe::Read(IOMemoryDescriptor *buffer, UInt32 noDataTimeout, UInt32 completionTimeout, IOUSBCompletion *completion, IOByteCount *bytesRead)
{
USBLog(7, "IOUSBPipe[%p]::Read #2", this);
if (!buffer)
{
USBLog(5, "IOUSBPipe[%p]::Read - NULL buffer!", this);
return kIOReturnBadArgument;
}
return Read(buffer, noDataTimeout, completionTimeout, buffer->getLength(), completion, bytesRead);
}
IOReturn
IOUSBPipe::Read(IOMemoryDescriptor *buffer, UInt32 noDataTimeout, UInt32 completionTimeout, IOByteCount reqCount, IOUSBCompletion *completion, IOByteCount *bytesRead)
{
IOReturn err = kIOReturnSuccess;
USBLog(7, "IOUSBPipe[%p]::Read #3 (addr %d:%d type %d) - reqCount = %qd", this, _address, _endpoint.number , _endpoint.transferType, (uint64_t)reqCount);
if ((_endpoint.transferType != kUSBBulk) && (noDataTimeout || completionTimeout))
{
USBLog(5, "IOUSBPipe[%p]::Read - bad arguments: (EP type: %d != kUSBBulk(%d)) && ( dataTimeout: %d || completionTimeout: %d)", this, _endpoint.transferType, kUSBBulk, (uint32_t)noDataTimeout, (uint32_t)completionTimeout);
return kIOReturnBadArgument;
}
if (!buffer || (buffer->getLength() < reqCount))
{
USBLog(5, "IOUSBPipe[%p]::Write - bad buffer: (buffer %p) || ( length %qd < reqCount %qd)", this, buffer, buffer ? (uint64_t)buffer->getLength() : 0, (uint64_t)reqCount);
return kIOReturnBadArgument;
}
if (_CORRECTSTATUS == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipe[%p]::Read - invalid read on a stalled pipe", this);
return kIOUSBPipeStalled;
}
if (completion == NULL)
{
IOUSBCompletion tap;
if (bytesRead)
*bytesRead = reqCount;
tap.target = NULL;
tap.action = &IOUSBSyncCompletion;
tap.parameter = bytesRead;
err = _controller->Read(buffer, _address, &_endpoint, &tap, noDataTimeout, completionTimeout, reqCount);
if (err != kIOReturnSuccess)
{
if (err && (err != kIOUSBTransactionTimeout))
{
USBLog(2, "IOUSBPipe[%p]::Read - i/o err (0x%x) on sync call - stalling pipe", this, err);
_CORRECTSTATUS = kIOUSBPipeStalled;
}
}
}
else
{
if (completion->action == NULL)
{
USBLog(1, "IOUSBPipe[%p]::Read - completion has NULL action - returning kIOReturnBadArgument(%p)", this, (void*)kIOReturnBadArgument);
USBTrace(kUSBTPipe, kTPBulkPipeRead, (uintptr_t)this, kIOReturnBadArgument, 0, 1 );
return kIOReturnBadArgument;
}
err = _controller->Read(buffer, _address, &_endpoint, completion, noDataTimeout, completionTimeout, reqCount);
}
if (err == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipe[%p]::Read - controller returned stalled pipe, changing status", this);
_CORRECTSTATUS = kIOUSBPipeStalled;
}
return(err);
}
IOReturn
IOUSBPipe::Read(IOMemoryDescriptor *buffer, UInt32 noDataTimeout, UInt32 completionTimeout, IOByteCount reqCount, IOUSBCompletionWithTimeStamp *completionWithTimeStamp, IOByteCount *bytesRead)
{
IOReturn err = kIOReturnSuccess;
IOUSBControllerV2 * controllerV2;
controllerV2 = OSDynamicCast(IOUSBControllerV2, _controller);
if ( controllerV2 == NULL )
{
USBLog(2,"IOUSBPipe[%p]:Read #4 -- Requested a Read with time stamp, but this IOUSBController does not support it", this);
return kIOReturnUnsupported;
}
USBLog(7, "IOUSBPipe[%p]::Read #4 (addr %d:%d type %d) - reqCount = %qd", this, _address, _endpoint.number , _endpoint.transferType, (uint64_t)reqCount);
if ((_endpoint.transferType != kUSBBulk) && (noDataTimeout || completionTimeout))
{
USBLog(5, "IOUSBPipe[%p]::Read #4 - bad arguments: (EP type: %d != kUSBBulk(%d)) && ( dataTimeout: %d || completionTimeout: %d)", this, _endpoint.transferType, kUSBBulk, (uint32_t)noDataTimeout, (uint32_t)completionTimeout);
return kIOReturnBadArgument;
}
if (!buffer || (buffer->getLength() < reqCount))
{
USBLog(5, "IOUSBPipe[%p]::Read #4- bad buffer: (buffer %p) || ( length %qd < reqCount %qd)", this, buffer, buffer ? (uint64_t)buffer->getLength() : 0, (uint64_t)reqCount);
return kIOReturnBadArgument;
}
if (_CORRECTSTATUS == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipe[%p]::Read #4 - invalid read on a stalled pipe", this);
return kIOUSBPipeStalled;
}
if (completionWithTimeStamp == NULL)
{
IOUSBCompletion tap;
if (bytesRead)
*bytesRead = reqCount;
tap.target = NULL;
tap.action = &IOUSBSyncCompletion;
tap.parameter = bytesRead;
err = controllerV2->ReadV2(buffer, _address, &_endpoint, (IOUSBCompletionWithTimeStamp *) &tap, noDataTimeout, completionTimeout, reqCount);
if (err != kIOReturnSuccess)
{
if (err && (err != kIOUSBTransactionTimeout))
{
USBLog(2, "IOUSBPipe[%p]::Read - i/o err (0x%x) on sync call - stalling pipe", this, err);
_CORRECTSTATUS = kIOUSBPipeStalled;
}
}
}
else
{
if (completionWithTimeStamp->action == NULL)
{
USBLog(1, "IOUSBPipe[%p]::Read - completionWithTimeStamp has NULL action - returning kIOReturnBadArgument(%p)", this, (void*)kIOReturnBadArgument);
USBTrace(kUSBTPipe, kTPIBulkReadTS, (uintptr_t)this, kIOReturnBadArgument, 0, 0);
return kIOReturnBadArgument;
}
err = controllerV2->ReadV2(buffer, _address, &_endpoint, completionWithTimeStamp, noDataTimeout, completionTimeout, reqCount);
}
if (err == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipe[%p]::Read - controller returned stalled pipe, changing status", this);
_CORRECTSTATUS = kIOUSBPipeStalled;
}
return(err);
}
#pragma mark Bulk Write
IOReturn
IOUSBPipe::Write(IOMemoryDescriptor *buffer, IOUSBCompletion *completion)
{
USBLog(7, "IOUSBPipe[%p]::Write #1", this);
return Write(buffer, 0, 0, completion);
}
IOReturn
IOUSBPipe::Write(IOMemoryDescriptor *buffer, UInt32 noDataTimeout, UInt32 completionTimeout, IOUSBCompletion *completion)
{
USBLog(7, "IOUSBPipe[%p]::Write #2", this);
if (!buffer)
{
USBLog(5, "IOUSBPipe[%p]::Write - NULL buffer!", this);
return kIOReturnBadArgument;
}
return Write(buffer, noDataTimeout, completionTimeout, buffer->getLength(), completion);
}
IOReturn
IOUSBPipe::Write(IOMemoryDescriptor *buffer, UInt32 noDataTimeout, UInt32 completionTimeout, IOByteCount reqCount, IOUSBCompletion *completion)
{
IOReturn err = kIOReturnSuccess;
USBLog(7, "IOUSBPipe[%p]::Write #3 (addr %d:%d type %d) - reqCount = %qd", this, _address, _endpoint.number , _endpoint.transferType, (uint64_t)reqCount);
if ((_endpoint.transferType != kUSBBulk) && (noDataTimeout || completionTimeout))
{
USBLog(5, "IOUSBPipe[%p]::Write - bad arguments: (EP type: %d != kUSBBulk(%d)) && ( dataTimeout: %d || completionTimeout: %d)", this, _endpoint.transferType, kUSBBulk, (uint32_t)noDataTimeout, (uint32_t)completionTimeout);
return kIOReturnBadArgument;
}
if (!buffer || (buffer->getLength() < reqCount))
{
USBLog(5, "IOUSBPipe[%p]::Write - bad buffer: (buffer %p) || ( length %qd < reqCount %qd)", this, buffer, buffer ? (uint64_t)buffer->getLength() : 0, (uint64_t)reqCount);
return kIOReturnBadArgument;
}
if (_CORRECTSTATUS == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipe[%p]::Write - invalid write on a stalled pipe", this);
return kIOUSBPipeStalled;
}
if (completion == NULL)
{
IOUSBCompletion tap;
tap.target = NULL;
tap.action = &IOUSBSyncCompletion;
tap.parameter = NULL;
err = _controller->Write(buffer, _address, &_endpoint, &tap, noDataTimeout, completionTimeout, reqCount);
if (err != kIOReturnSuccess)
{
if (err && (err != kIOUSBTransactionTimeout))
{
USBLog(2, "IOUSBPipe[%p]::Write - i/o err (0x%x) on sync call - stalling pipe", this, err);
_CORRECTSTATUS = kIOUSBPipeStalled;
}
}
}
else
{
if (completion->action == NULL)
{
USBLog(1, "IOUSBPipe[%p]::Write - completion has NULL action - returning kIOReturnBadArgument(%p)", this, (void*)kIOReturnBadArgument);
USBTrace(kUSBTPipe, kTPBulkPipeWrite, (uintptr_t)this, kIOReturnBadArgument, 0, 0 );
return kIOReturnBadArgument;
}
err = _controller->Write(buffer, _address, &_endpoint, completion, noDataTimeout, completionTimeout, reqCount);
}
if (err == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipe[%p]::Write - controller returned stalled pipe, changing status", this);
_CORRECTSTATUS = kIOUSBPipeStalled;
}
return err;
}
#pragma mark Isochronous
IOReturn
IOUSBPipe::Read(IOMemoryDescriptor * buffer, UInt64 frameStart, UInt32 numFrames, IOUSBIsocFrame *pFrames, IOUSBIsocCompletion *completion)
{
IOReturn err = kIOReturnSuccess;
if (_CORRECTSTATUS == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipe[%p]::Read - invalid read on a stalled isoch pipe", this);
return kIOUSBPipeStalled;
}
if (_endpoint.maxPacketSize == 0)
{
USBLog(2, "IOUSBPipe[%p]::Read - no bandwidth on an isoc pipe", this);
return kIOReturnNoBandwidth;
}
if ( _CROSSENDIANCOMPATIBLE )
_endpoint.direction |= 0x80;
if (completion == NULL)
{
IOUSBIsocCompletion tap;
tap.target = NULL;
tap.action = &IOUSBSyncIsoCompletion;
tap.parameter = NULL;
USBLog(3, "IOUSBPipe[%p]::Read Sync (Isoc) completion: %p", this, tap.action);
err = _controller->IsocIO(buffer, frameStart, numFrames, pFrames, _address, &_endpoint, &tap);
}
else
{
if (completion->action == NULL)
{
USBLog(1, "IOUSBPipe[%p]::Read - completion has NULL action - returning kIOReturnBadArgument(%p)", this, (void*)kIOReturnBadArgument);
USBTrace(kUSBTPipe, kTPIsocPipeRead, (uintptr_t)this, kIOReturnBadArgument, 0, 1 );
return kIOReturnBadArgument;
}
err = _controller->IsocIO(buffer, frameStart, numFrames, pFrames, _address, &_endpoint, completion);
}
return err;
}
IOReturn
IOUSBPipe::Write(IOMemoryDescriptor * buffer, UInt64 frameStart, UInt32 numFrames, IOUSBIsocFrame *pFrames, IOUSBIsocCompletion *completion)
{
IOReturn err = kIOReturnSuccess;
if (_CORRECTSTATUS == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipe[%p]::Write - invalid write on a stalled isoch pipe", this);
return kIOUSBPipeStalled;
}
if (_endpoint.maxPacketSize == 0)
{
USBLog(2, "IOUSBPipe[%p]::Write - no bandwidth on an isoc pipe", this);
return kIOReturnNoBandwidth;
}
if ( _CROSSENDIANCOMPATIBLE )
_endpoint.direction |= 0x80;
if (completion == NULL)
{
IOUSBIsocCompletion tap;
tap.target = NULL;
tap.action = &IOUSBSyncIsoCompletion;
tap.parameter = NULL;
USBLog(3, "IOUSBPipe[%p]::Write Sync (Isoc) completion: %p", this, tap.action);
err = _controller->IsocIO(buffer, frameStart, numFrames, pFrames, _address, &_endpoint, &tap);
}
else
{
if (completion->action == NULL)
{
USBLog(1, "IOUSBPipe[%p]::Write - completion has NULL action - returning kIOReturnBadArgument(%p)", this, (void*)kIOReturnBadArgument);
USBTrace(kUSBTPipe, kTPIsocPipeWrite, (uintptr_t)this, kIOReturnBadArgument, 0, 1 );
return kIOReturnBadArgument;
}
err = _controller->IsocIO(buffer, frameStart, numFrames, pFrames, _address, &_endpoint, completion);
}
return(err);
}
#pragma mark Low Latency Isochronous
IOReturn
IOUSBPipe::Read(IOMemoryDescriptor * buffer,
UInt64 frameStart, UInt32 numFrames, IOUSBLowLatencyIsocFrame *pFrames,
IOUSBLowLatencyIsocCompletion * completion, UInt32 updateFrequency)
{
IOReturn err = kIOReturnSuccess;
USBLog(7, "IOUSBPipe[%p]::Read (Low Latency Isoc) buffer: %p, completion: %p, numFrames: %d, update: %d", this, buffer, completion, (uint32_t)numFrames, (uint32_t)updateFrequency);
if (_CORRECTSTATUS == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipe[%p]::Read (Low Latency Isoc) - invalid read on a stalled low latency isoch pipe", this);
return kIOUSBPipeStalled;
}
if (_endpoint.maxPacketSize == 0)
{
USBLog(2, "IOUSBPipe[%p]::Read (Low Latency Isoc) - no bandwidth on an low latency isoc pipe", this);
return kIOReturnNoBandwidth;
}
if ( _CROSSENDIANCOMPATIBLE )
_endpoint.direction |= 0x80;
if (completion == NULL)
{
IOUSBLowLatencyIsocCompletion tap;
tap.target = NULL;
tap.action = (IOUSBLowLatencyIsocCompletionAction) &IOUSBSyncIsoCompletion;
tap.parameter = NULL;
USBLog(3, "IOUSBPipe[%p]::Read Sync (Low Latency Isoc) completion: %p", this, tap.action);
err = _controller->IsocIO(buffer, frameStart, numFrames, pFrames, _address, &_endpoint, &tap, updateFrequency);
}
else
{
if (completion->action == NULL)
{
USBLog(1, "IOUSBPipe[%p]::Read - completion has NULL action - returning kIOReturnBadArgument(%p)", this, (void*)kIOReturnBadArgument);
USBTrace(kUSBTPipe, kTPIsocPipeReadLL, (uintptr_t)this, kIOReturnBadArgument, 0, 0 );
return kIOReturnBadArgument;
}
err = _controller->IsocIO(buffer, frameStart, numFrames, pFrames, _address, &_endpoint, completion, updateFrequency);
}
return err;
}
IOReturn
IOUSBPipe::Write(IOMemoryDescriptor * buffer, UInt64 frameStart, UInt32 numFrames, IOUSBLowLatencyIsocFrame *pFrames, IOUSBLowLatencyIsocCompletion *completion, UInt32 updateFrequency)
{
IOReturn err = kIOReturnSuccess;
USBLog(7, "IOUSBPipe[%p]::Write (Low Latency Isoc) buffer: %p, completion: %p, numFrames: %d, update: %d", this, buffer, completion, (uint32_t)numFrames, (uint32_t)updateFrequency);
if (_CORRECTSTATUS == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipe[%p]::Write (Low Latency Isoc) - invalid write on a stalled isoch pipe", this);
return kIOUSBPipeStalled;
}
if (_endpoint.maxPacketSize == 0)
{
USBLog(2, "IOUSBPipe[%p]::Write (Low Latency Isoc) - no bandwidth on an isoc pipe", this);
return kIOReturnNoBandwidth;
}
if ( _CROSSENDIANCOMPATIBLE )
_endpoint.direction |= 0x80;
if (completion == NULL)
{
IOUSBLowLatencyIsocCompletion tap;
tap.target = NULL;
tap.action = (IOUSBLowLatencyIsocCompletionAction)&IOUSBSyncIsoCompletion;
tap.parameter = NULL;
USBLog(3, "IOUSBPipe[%p]::Write Sync (Low Latency Isoc) completion: %p", this, tap.action);
err = _controller->IsocIO(buffer, frameStart, numFrames, pFrames, _address, &_endpoint, &tap, updateFrequency);
}
else
{
if (completion->action == NULL)
{
USBLog(1, "IOUSBPipe[%p]::Write - completion has NULL action - returning kIOReturnBadArgument(%p)", this, (void*)kIOReturnBadArgument);
USBTrace(kUSBTPipe, kTPIsocPipeWriteLL, (uintptr_t)this, kIOReturnBadArgument, 0, 0);
return kIOReturnBadArgument;
}
err = _controller->IsocIO(buffer, frameStart, numFrames, pFrames, _address, &_endpoint, completion, updateFrequency);
}
return err;
}
#pragma mark Control Requests
IOReturn
IOUSBPipe::ControlRequest(IOUSBDevRequest *request, IOUSBCompletion *completion)
{
return ControlRequest(request, _endpoint.number ? 0 : kUSBDefaultControlNoDataTimeoutMS, _endpoint.number ? 0 : kUSBDefaultControlCompletionTimeoutMS, completion);
}
IOReturn
IOUSBPipe::ControlRequest(IOUSBDevRequest *request, UInt32 noDataTimeout, UInt32 completionTimeout, IOUSBCompletion *completion)
{
IOReturn err = kIOReturnSuccess;
if ( _DEVICE && request && (_DEVICE->GetVendorID() == kAppleVendorID) && (request->bmRequestType == USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice)) && (request->bRequest == 0x40) && (request->wLength == 0) )
{
USBLog(1, "IOUSBPipe[%p]::ControlRequest Possible charging command sent to %s @ 0x%x, operating: %d extra, sleep: %d", this, _DEVICE->getName(), (uint32_t)_DEVICE->_expansionData->_locationID, request->wIndex, request->wValue );
USBTrace(kUSBTPipe, kTPPipeControlRequest, (uintptr_t)this, (uintptr_t)_DEVICE->_expansionData->_locationID, (uintptr_t)(request->wIndex<< 16 | request->wValue), 1 );
}
if (completion == NULL)
{
IOUSBCompletion tap;
request->wLenDone = request->wLength;
tap.target = NULL;
tap.action = &IOUSBSyncCompletion;
tap.parameter = &request->wLenDone;
err = _controller->DeviceRequest(request, &tap, _address, _endpoint.number, noDataTimeout, completionTimeout);
}
else
{
if (completion->action == NULL)
{
USBLog(1, "IOUSBPipe[%p]::ControlRequest - completion has NULL action - returning kIOReturnBadArgument(%p)", this, (void*)kIOReturnBadArgument);
USBTrace(kUSBTPipe, kTPPipeControlRequestMemDesc, (uintptr_t)this, kIOReturnBadArgument, 0, 2 );
return kIOReturnBadArgument;
}
err = _controller->DeviceRequest(request, completion, _address, _endpoint.number, noDataTimeout, completionTimeout);
}
return(err);
}
IOReturn
IOUSBPipe::ControlRequest(IOUSBDevRequestDesc *request, IOUSBCompletion *completion)
{
return ControlRequest(request, _endpoint.number ? 0 : kUSBDefaultControlNoDataTimeoutMS, _endpoint.number ? 0 : kUSBDefaultControlCompletionTimeoutMS, completion);
}
IOReturn
IOUSBPipe::ControlRequest(IOUSBDevRequestDesc *request, UInt32 noDataTimeout, UInt32 completionTimeout, IOUSBCompletion *completion)
{
IOReturn err = kIOReturnSuccess;
if ( _DEVICE && request && (_DEVICE->GetVendorID() == kAppleVendorID) && (request->bmRequestType == USBmakebmRequestType(kUSBOut, kUSBVendor, kUSBDevice)) && (request->bRequest == 0x40) && (request->wLength == 0) )
{
USBLog(1, "IOUSBPipe[%p]::ControlRequest Possible charging command sent to %s @ 0x%x, operating: %d extra, sleep: %d", this, _DEVICE->getName(), (uint32_t)_DEVICE->_expansionData->_locationID, request->wIndex, request->wValue );
USBTrace(kUSBTPipe, kTPPipeControlRequest, (uintptr_t)this, (uintptr_t)_DEVICE->_expansionData->_locationID, (uintptr_t)(request->wIndex<< 16 | request->wValue), 2 );
}
if (completion == NULL)
{
IOUSBCompletion tap;
request->wLenDone = request->wLength;
tap.target = NULL;
tap.action = &IOUSBSyncCompletion;
tap.parameter = &request->wLenDone;
err = _controller->DeviceRequest(request, &tap, _address, _endpoint.number, noDataTimeout, completionTimeout);
}
else
{
if (completion->action == NULL)
{
USBLog(1, "IOUSBPipe[%p]::ControlRequest - completion has NULL action - returning kIOReturnBadArgument(%p)", this, (void*)kIOReturnBadArgument);
USBTrace(kUSBTPipe, kTPPipeControlRequestMemDesc, (uintptr_t)this, kIOReturnBadArgument, 0, 1 );
return kIOReturnBadArgument;
}
err = _controller->DeviceRequest(request, completion, _address, _endpoint.number, noDataTimeout, completionTimeout);
}
return(err);
}
#pragma mark Accessors
const IOUSBController::Endpoint *
IOUSBPipe::GetEndpoint()
{
return(&_endpoint);
}
const IOUSBEndpointDescriptor *
IOUSBPipe::GetEndpointDescriptor()
{
return(_descriptor);
}
UInt8
IOUSBPipe::GetDirection()
{
return(_endpoint.direction);
}
UInt8
IOUSBPipe::GetType()
{
return(_endpoint.transferType);
}
UInt8
IOUSBPipe::GetEndpointNumber()
{
return(_endpoint.number);
}
USBDeviceAddress
IOUSBPipe::GetAddress()
{
return(_address);
}
UInt16
IOUSBPipe::GetMaxPacketSize()
{
return _endpoint.maxPacketSize;
}
UInt8
IOUSBPipe::GetInterval()
{
return (_endpoint.interval);
}
UInt8
IOUSBPipe::GetStatus(void)
{
return(0);
}
IOReturn
IOUSBPipe::GetPipeStatus(void)
{
return _CORRECTSTATUS;
}
#pragma mark Obsolete Methods
bool
IOUSBPipe::InitToEndpoint(const IOUSBEndpointDescriptor *ed, UInt8 speed, USBDeviceAddress address, IOUSBController * controller)
{
#pragma unused (ed, speed, address, controller)
USBLog(1, "IOUSBPipe::InitToEndpoint, obsolete method 1 called");
return NULL;
}
IOUSBPipe *
IOUSBPipe::ToEndpoint(const IOUSBEndpointDescriptor *ed, UInt8 speed, USBDeviceAddress address, IOUSBController *controller)
{
#pragma unused (ed, speed, address, controller)
USBLog(1, "IOUSBPipe::ToEndpoint, obsolete method 1 called");
return NULL;
}
IOUSBPipe *
IOUSBPipe::ToEndpoint(const IOUSBEndpointDescriptor *ed, IOUSBDevice * device, IOUSBController *controller)
{
#pragma unused (ed, device, controller)
USBLog(1, "IOUSBPipe::ToEndpoint, obsolete method 2 called");
return NULL;
}
#pragma mark Padding Slots
OSMetaClassDefineReservedUsed(IOUSBPipe, 0);
OSMetaClassDefineReservedUsed(IOUSBPipe, 1);
OSMetaClassDefineReservedUsed(IOUSBPipe, 2);
OSMetaClassDefineReservedUsed(IOUSBPipe, 3);
OSMetaClassDefineReservedUsed(IOUSBPipe, 4);
OSMetaClassDefineReservedUsed(IOUSBPipe, 5);
OSMetaClassDefineReservedUsed(IOUSBPipe, 6);
OSMetaClassDefineReservedUsed(IOUSBPipe, 7);
OSMetaClassDefineReservedUsed(IOUSBPipe, 8);
OSMetaClassDefineReservedUsed(IOUSBPipe, 9);
OSMetaClassDefineReservedUsed(IOUSBPipe, 10);
OSMetaClassDefineReservedUsed(IOUSBPipe, 11);
OSMetaClassDefineReservedUsed(IOUSBPipe, 12);
OSMetaClassDefineReservedUnused(IOUSBPipe, 13);
OSMetaClassDefineReservedUnused(IOUSBPipe, 14);
OSMetaClassDefineReservedUnused(IOUSBPipe, 15);
OSMetaClassDefineReservedUnused(IOUSBPipe, 16);
OSMetaClassDefineReservedUnused(IOUSBPipe, 17);
OSMetaClassDefineReservedUnused(IOUSBPipe, 18);
OSMetaClassDefineReservedUnused(IOUSBPipe, 19);