#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/IOUSBControllerV3.h>
#include <IOKit/usb/USBHub.h>
#include <IOKit/usb/IOUSBDevice.h>
#include <IOKit/usb/IOUSBPipeV2.h>
#include <IOKit/usb/IOUSBNub.h>
#include <IOKit/usb/IOUSBLog.h>
#include "IOUSBInterfaceUserClient.h"
#include "USBTracepoints.h"
#define super IOUSBPipe
#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 _UASUSAGEID _expansionData->_uasUsageID
#define _USAGETYPE _expansionData->_usageType
#define _SYNCTYPE _expansionData->_syncType
#define _OUTOFSPECMPSOK _status // if non-zero, then we should ignore an out of spec MPS
#ifndef IOUSBPIPEV2_USE_KPRINTF
#define IOUSBPIPEV2_USE_KPRINTF 0
#endif
#if IOUSBPIPEV2_USE_KPRINTF
#undef USBLog
#undef USBError
void kprintf(const char *format, ...) __attribute__((format(printf, 1, 2)));
#define USBLog( LEVEL, FORMAT, ARGS... ) if ((LEVEL) <= IOUSBPIPEV2_USE_KPRINTF) { kprintf( FORMAT "\n", ## ARGS ) ; }
#define USBError( LEVEL, FORMAT, ARGS... ) { kprintf( FORMAT "\n", ## ARGS ) ; }
#endif
OSDefineMetaClassAndStructors(IOUSBPipeV2, IOUSBPipe)
#pragma mark Intializers
bool
IOUSBPipeV2::InitToEndpoint(const IOUSBEndpointDescriptor *ed, IOUSBSuperSpeedEndpointCompanionDescriptor *sscd, UInt8 speed, USBDeviceAddress address, IOUSBController * controller, IOUSBDevice * device, IOUSBInterface * interface)
{
IOReturn err;
IOUSBControllerV3 *controllerV3;
UInt16 maxStreamID = 0;
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;
controllerV3 = OSDynamicCast(IOUSBControllerV3, _controller);
if ( controllerV3 == NULL )
{
if(sscd != NULL)
{
USBError(1,"IOUSBPipeV2[%p]::InitToEndpoint (%s, EP: 0x%x) -- Requested a pipe with stream ID, but this IOUSBController does not support it", this, device->getName(), (uint32_t)ed->bEndpointAddress);
return false;
}
}
_descriptor = ed;
_sscd = sscd;
_endpoint.number = ed->bEndpointAddress & kUSBPipeIDMask;
_endpoint.transferType = (ed->bmAttributes & kUSB_EPDesc_bmAttributes_TranType_Mask) >> kUSB_EPDesc_bmAttributes_TranType_Shift;
if (_endpoint.transferType == kUSBControl)
_endpoint.direction = kUSBAnyDirn;
else
_endpoint.direction = (ed->bEndpointAddress & 0x80) ? kUSBIn : kUSBOut;
_endpoint.maxPacketSize = (ed->wMaxPacketSize & kUSB_EPDesc_wMaxPacketSize_MPS_Mask) >> kUSB_EPDesc_wMaxPacketSize_MPS_Shift;
if ((sscd == NULL) && ((_endpoint.transferType == kUSBIsoc) || (_endpoint.transferType == kUSBInterrupt)))
{
_mult = (ed->wMaxPacketSize & kUSB_HSFSEPDesc_wMaxPacketSize_Mult_Mask) >> kUSB_HSFSEPDesc_wMaxPacketSize_Mult_Shift;
if (_mult > 2)
{
_mult = 2;
USBLog(1,"IOUSBPipeV2[%p]::InitToEndpoint (%s, EP: 0x%x) -- HS mult for wMaxPacketSize is illegal (3), setting to 2", this, device ? device->getName() : "Unnamed Device", (uint32_t)ed->bEndpointAddress);
}
}
_endpoint.interval = ed->bInterval;
if ( _endpoint.transferType == kUSBInterrupt)
{
_USAGETYPE = (ed->bmAttributes & kUSB_EPDesc_bmAttributes_UsageType_Mask) >> kUSB_EPDesc_bmAttributes_UsageType_Shift;
_SYNCTYPE = 0;
}
else if ( _endpoint.transferType == kUSBIsoc)
{
_USAGETYPE = (ed->bmAttributes & kUSB_EPDesc_bmAttributes_UsageType_Mask) >> kUSB_EPDesc_bmAttributes_UsageType_Shift;
_SYNCTYPE = (ed->bmAttributes & kUSB_EPDesc_bmAttributes_SyncType_Mask) >> kUSB_EPDesc_bmAttributes_SyncType_Shift;
}
else
{
_USAGETYPE = 0;
_SYNCTYPE = 0;
}
_maxBurst = 0;
if (_sscd != NULL)
{
_maxBurst = _sscd->bMaxBurst;
_bytesPerInterval = _sscd->wBytesPerInterval;
if (_endpoint.transferType == kUSBBulk)
{
_maxStream = (_sscd->bmAttributes & kUSB_SSCompDesc_Bulk_MaxStreams_Mask) >> kUSB_SSCompDesc_Bulk_MaxStreams_Shift;
_configuredStreams = 0;
if(_maxStream > 0)
{
UInt32 maxSupported;
maxStreamID = (1 << _maxStream) - 1;
if(maxStreamID > kUSBMaxStream)
{
maxStreamID = kUSBMaxStream;
}
maxSupported = controllerV3->UIMMaxSupportedStream();
if( (maxStreamID > 0) && (maxSupported == 0) )
{
USBLog(1,"IOUSBPipeV2[%p]::InitToEndpoint (%s, EP: 0x%x) -- Requested a pipe with maxStreamID %d, but this IOUSBController does not support streams", this, device ? device->getName() : "Unnamed Device", (uint32_t)ed->bEndpointAddress, (uint32_t)maxStreamID);
_maxStream = 0;
return false;
}
if(maxStreamID > maxSupported)
{
USBLog(1,"IOUSBPipeV2[%p]::InitToEndpoint (%s, EP: 0x%x) -- Requested a pipe with max stream of %d, but this IOUSBController only supports %d streams", this, device ? device->getName() : "Unnamed Device", (uint32_t)ed->bEndpointAddress, (uint32_t)maxStreamID, (uint32_t)maxSupported);
maxStreamID = maxSupported;
}
}
if (_endpoint.maxPacketSize != kUSB_EPDesc_MaxMPS)
{
USBLog(1,"IOUSBPipeV2[%p]::InitToEndpoint (%s, EP: 0x%x) -- SuperSpeed Bulk EP with illegal wMaxPacketSize of %d, setting to kUSB_EPDesc_MaxMPS", this, device ? device->getName() : "Unnamed Device", (uint32_t)ed->bEndpointAddress, (uint32_t)_endpoint.maxPacketSize);
_endpoint.maxPacketSize = kUSB_EPDesc_MaxMPS;
}
USBLog(3, "IOUSBPipeV2[%p]::InitToEndpoint: SuperSpeed Bulk pipe maxStreams: %d", this, (uint32_t)maxStreamID);
}
if (_endpoint.transferType == kUSBControl)
{
if (_endpoint.maxPacketSize != 512)
{
USBLog(1,"IOUSBPipeV2[%p]::InitToEndpoint (%s, EP: 0x%x) -- SuperSpeed Control EP with illegal wMaxPacketSize of %d, setting to 512", this, device ? device->getName() : "Unnamed Device", (uint32_t)ed->bEndpointAddress, (uint32_t)_endpoint.maxPacketSize);
_endpoint.maxPacketSize = 512;
}
_maxBurst = 0;
}
if (_endpoint.transferType == kUSBIsoc)
{
_mult = (sscd->bmAttributes & kUSB_SSCompDesc_Isoc_Mult_Mask) >> kUSB_SSCompDesc_Isoc_Mult_Shift; if (_mult > 2)
{
_mult = 2;
USBLog(1,"IOUSBPipeV2[%p]::InitToEndpoint (%s, EP: 0x%x) -- SS mult for wMaxPacketSize is illegal (3), setting to 2", this, device ? device->getName() : "Unnamed Device", (uint32_t)ed->bEndpointAddress);
}
if ( (_maxBurst > 0) || (_mult > 0))
{
if (_endpoint.maxPacketSize != kUSB_EPDesc_MaxMPS)
{
USBLog(1,"IOUSBPipeV2[%p]::InitToEndpoint (%s, EP: 0x%x) -- SuperSpeed Isoch EP (bMaxBurst: %d mult: %d) with illegal wMaxPacketSize of %d, setting to kUSB_EPDesc_MaxMPS", this, device ? device->getName() : "Unnamed Device", (uint32_t)ed->bEndpointAddress, (uint32_t)_maxBurst, (uint32_t)_mult, (uint32_t)_endpoint.maxPacketSize);
_endpoint.maxPacketSize = kUSB_EPDesc_MaxMPS;
}
}
USBLog(6, "IOUSBPipeV2[%p]::InitToEndpoint: SuperSpeed Isoch pipe bytesPerInterval: %d, maxPacketSize: %d, maxBurst: %d, mult: %d", this, (uint32_t)_bytesPerInterval, (uint32_t)_endpoint.maxPacketSize,(uint32_t)_maxBurst,(uint32_t)_mult);
}
if (_endpoint.transferType == kUSBInterrupt)
{
if ( _maxBurst > 0)
{
if (_endpoint.maxPacketSize != kUSB_EPDesc_MaxMPS)
{
USBLog(1,"IOUSBPipeV2[%p]::InitToEndpoint (%s, EP: 0x%x) -- SuperSpeed Interrupt EP (bMaxBurst: %d) with illegal wMaxPacketSize of %d, setting to kUSB_EPDesc_MaxMPS", this, device ? device->getName() : "Unnamed Device", (uint32_t)ed->bEndpointAddress, (uint32_t)_maxBurst, (uint32_t)_endpoint.maxPacketSize);
_endpoint.maxPacketSize = kUSB_EPDesc_MaxMPS;
}
}
else
{
if ( _endpoint.maxPacketSize == 0)
{
USBLog(1,"IOUSBPipeV2[%p]::InitToEndpoint (%s, EP: 0x%x) -- SuperSpeed Interrupt EP (bMaxBurst of 0) with illegal wMaxPacketSize of 0, setting to 1", this, device ? device->getName() : "Unnamed Device", (uint32_t)ed->bEndpointAddress);
_endpoint.maxPacketSize = 1;
}
}
}
}
if ((_endpoint.transferType == kUSBIsoc) || (_endpoint.transferType == kUSBInterrupt))
{
_endpoint.maxPacketSize = _endpoint.maxPacketSize * (_mult + 1) * (_maxBurst + 1);
}
_address = address;
_SPEED = speed;
_DEVICE = device;
_INTERFACE = interface;
if (_INTERFACE && _INTERFACE->GetInterfaceClass() == kUSBMassStorageInterfaceClass && _INTERFACE->GetInterfaceSubClass() == kUSBMassStorageSCSISubClass && _INTERFACE->GetInterfaceProtocol() == kMSCProtocolUSBAttachedSCSI)
{
const UASPipeDescriptor *desc = NULL; if (_sscd != NULL)
{
desc = (UASPipeDescriptor *)_INTERFACE->FindNextAssociatedDescriptor(sscd, kUSBAnyDesc);
}
else
{
desc = (UASPipeDescriptor *)_INTERFACE->FindNextAssociatedDescriptor(ed, kUSBAnyDesc);
}
if (desc && (desc->bDescriptorType == kUSBClassSpecificDescriptor))
{
_UASUSAGEID = desc->bPipeID;
}
}
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, "IOUSBPipeV2[%p]::InitToEndpoint: High Speed Bulk pipe with %d MPS, but property says we should still use it.", this, _endpoint.maxPacketSize);
}
}
if( (maxStreamID == 0) && (_maxBurst == 0) )
{
err = _controller->OpenPipe(_address, speed, &_endpoint);
}
else
{
err = controllerV3->OpenPipe(_address, speed, &_endpoint, maxStreamID, _maxBurst | (_mult << 8));
}
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, "IOUSBPipeV2[%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,"IOUSBPipeV2[%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;
}
IOUSBPipeV2 *
IOUSBPipeV2::ToEndpoint(const IOUSBEndpointDescriptor *ed, IOUSBSuperSpeedEndpointCompanionDescriptor *sscd, IOUSBDevice * device, IOUSBController *controller, IOUSBInterface * interface)
{
OSObject * propertyObj = NULL;
OSBoolean * boolObj = NULL;
IOUSBPipeV2 * me = new IOUSBPipeV2;
USBLog(6, "IOUSBPipeV2[%p]::ToEndpoint device %p(%s), interface %p, ep: 0x%x,0x%x,%d,%d", me, device, device->getName(), 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,"IOUSBPipeV2[%p]::ToEndpoint Device reports Out of spec MPS property and is TRUE", me);
me->_OUTOFSPECMPSOK = 0xFF;
}
if (propertyObj)
propertyObj->release();
}
if ( !me->InitToEndpoint(ed, sscd, 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,"IOUSBPipeV2[%p]::ToEndpoint CrossEndianProperty exists and is TRUE", me);
me->_CROSSENDIANCOMPATIBLE = true;
}
else
{
USBLog(6,"IOUSBPipeV2[%p]::ToEndpoint CrossEndianProperty exists and is FALSE", me);
me->_CROSSENDIANCOMPATIBLE = false;
}
}
if (propertyObj)
propertyObj->release();
}
return me;
}
#pragma mark IOUSBPipeV2 State
IOReturn
IOUSBPipeV2::Abort(UInt32 streamID)
{
IOUSBControllerV3 *controllerV3;
UInt32 maxStreamID = SupportsStreams();
USBLog(5,"IOUSBPipeV2[%p]::AbortPipe(0x%x)",this, (uint32_t)streamID);
if (_CORRECTSTATUS != 0)
{
USBLog(2, "IOUSBPipeV2[%p]::Abort setting status to 0", this);
}
_CORRECTSTATUS = 0;
controllerV3 = OSDynamicCast(IOUSBControllerV3, _controller);
if ( controllerV3 == NULL )
{
if(streamID != 0)
{
USBLog(2,"IOUSBPipeV2[%p]:Abort -- Requested an abort with stream ID, but this IOUSBController does not support it", this);
return kIOReturnUnsupported;
}
}
if(streamID == 0)
{
USBLog(5, "IOUSBPipeV2[%p]::Abort(streamID) - bad argument: stream ID zero (%d)", this, (uint32_t)streamID);
return kIOReturnBadArgument;
}
if(streamID != kUSBAllStreams)
{
if(_endpoint.transferType != kUSBBulk)
{
USBLog(5, "IOUSBPipeV2[%p]::Abort(streamID) - bad argument: abort stream (%d) on non bulk pipe", this, (uint32_t)streamID);
return kIOReturnBadArgument;
}
if(streamID > maxStreamID)
{
USBLog(5, "IOUSBPipeV2[%p]::Abort(streamID) - bad argument: stream ID greater than max (%d > %d)", this, (uint32_t)streamID, (uint32_t)maxStreamID);
return kIOReturnBadArgument;
}
}
if ( streamID != 0 )
{
return controllerV3->AbortPipe(streamID, _address, &_endpoint);
}
else
{
return _controller->AbortPipe(_address, &_endpoint);
}
}
#pragma mark Bulk Read
IOReturn
IOUSBPipeV2::Read(UInt32 streamID, IOMemoryDescriptor *buffer, UInt32 noDataTimeout, UInt32 completionTimeout, IOByteCount reqCount, IOUSBCompletion *completion, IOByteCount *bytesRead)
{
IOReturn err = kIOReturnSuccess;
IOUSBControllerV3 *controllerV3;
UInt32 maxStreamID = SupportsStreams();
controllerV3 = OSDynamicCast(IOUSBControllerV3, _controller);
if ( controllerV3 == NULL )
{
if(streamID != 0)
{
USBLog(2,"IOUSBPipeV2[%p]:Read -- Requested a Read with stream ID, but this IOUSBController does not support it", this);
return kIOReturnUnsupported;
}
}
if(streamID != 0)
{
if(_endpoint.transferType != kUSBBulk)
{
USBLog(5, "IOUSBPipeV2[%p]::Read - bad arguments: stream (%d) read on non bulk pipe", this, (uint32_t)streamID);
return kIOReturnBadArgument;
}
if(streamID > maxStreamID)
{
USBLog(5, "IOUSBPipeV2[%p]::Read - bad arguments: stream ID greater than max (%d > %d)", this, (uint32_t)streamID, (uint32_t)maxStreamID);
return kIOReturnBadArgument;
}
}
USBLog(7, "IOUSBPipeV2[%p]::Read (addr %d:%d type %d) - streamID = %d, reqCount = %qd", this, _address, _endpoint.number , _endpoint.transferType, streamID, (uint64_t)reqCount);
USBTrace_Start( kUSBTPipe, kTPBulkPipeRead, _address, _endpoint.number , _endpoint.transferType, reqCount );
if ((_endpoint.transferType != kUSBBulk) && (noDataTimeout || completionTimeout))
{
USBLog(5, "IOUSBPipeV2[%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, "IOUSBPipeV2[%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, "IOUSBPipeV2[%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;
if(streamID != 0)
{
err = controllerV3->Read(streamID, buffer, _address, &_endpoint, &tap, noDataTimeout, completionTimeout, reqCount);
}
else
{
err = _controller->Read(buffer, _address, &_endpoint, &tap, noDataTimeout, completionTimeout, reqCount);
}
if (err != kIOReturnSuccess)
{
if (err && (err != kIOUSBTransactionTimeout))
{
USBLog(2, "IOUSBPipe[%p]::Read(sync) returned 0x%x (%s) - stalling pipe", this, err, USBStringFromReturn(err));
_CORRECTSTATUS = kIOUSBPipeStalled;
}
}
}
else
{
if (completion->action == NULL)
{
USBLog(1, "IOUSBPipeV2[%p]::Read - completion has NULL action - returning kIOReturnBadArgument(%p)", this, (void*)kIOReturnBadArgument);
USBTrace(kUSBTPipe, kTPBulkPipeRead, (uintptr_t)this, kIOReturnBadArgument, 0, 1 );
return kIOReturnBadArgument;
}
if(streamID != 0)
{
err = controllerV3->Read(streamID, buffer, _address, &_endpoint, completion, noDataTimeout, completionTimeout, reqCount);
}
else
{
err = _controller->Read(buffer, _address, &_endpoint, completion, noDataTimeout, completionTimeout, reqCount);
}
}
if (err == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipeV2[%p]::Read - controller returned stalled pipe, changing status", this);
_CORRECTSTATUS = kIOUSBPipeStalled;
}
USBLog(7, "IOUSBPipeV2[%p]::Read (addr %d:%d type %d) - reqCount = %qd, returning: 0x%x", this, _address, _endpoint.number , _endpoint.transferType, (uint64_t)reqCount, err);
USBTrace_End( kUSBTPipe, kTPBulkPipeRead, (uintptr_t)this, _address, _endpoint.number, err);
return(err);
}
#pragma mark Bulk Write
IOReturn
IOUSBPipeV2::Write(UInt32 streamID, IOMemoryDescriptor *buffer, UInt32 noDataTimeout, UInt32 completionTimeout, IOByteCount reqCount, IOUSBCompletion *completion)
{
IOReturn err = kIOReturnSuccess;
IOUSBControllerV3 *controllerV3;
UInt32 maxStreamID = SupportsStreams();
controllerV3 = OSDynamicCast(IOUSBControllerV3, _controller);
if ( controllerV3 == NULL )
{
if(streamID != 0)
{
USBLog(2,"IOUSBPipeV2[%p]:write -- Requested a stream write, but this IOUSBController does not support it", this);
return kIOReturnUnsupported;
}
}
if(streamID != 0)
{
if(_endpoint.transferType != kUSBBulk)
{
USBLog(5, "IOUSBPipeV2[%p]::Write - bad arguments: stream (%d) read on non bulk pipe", this, (uint32_t)streamID);
return kIOReturnBadArgument;
}
if(streamID > maxStreamID)
{
USBLog(5, "IOUSBPipeV2[%p]::Write - bad arguments: stream ID greater than max (%d > %d)", this, (uint32_t)streamID, (uint32_t)maxStreamID);
return kIOReturnBadArgument;
}
}
USBLog(7, "IOUSBPipeV2[%p]::Write (addr %d:%d type %d) - streamID: %d, reqCount = %qd", this, _address, _endpoint.number , _endpoint.transferType, streamID, (uint64_t)reqCount);
USBTrace_Start( kUSBTPipe, kTPBulkPipeWrite, _address, _endpoint.number , _endpoint.transferType, reqCount );
if ((_endpoint.transferType != kUSBBulk) && (noDataTimeout || completionTimeout))
{
USBLog(5, "IOUSBPipeV2[%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, "IOUSBPipeV2[%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, "IOUSBPipeV2[%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;
if(streamID != 0)
{
err = controllerV3->Write(streamID, buffer, _address, &_endpoint, &tap, noDataTimeout, completionTimeout, reqCount);
}
else
{
err = _controller->Write(buffer, _address, &_endpoint, &tap, noDataTimeout, completionTimeout, reqCount);
}
if (err != kIOReturnSuccess)
{
if (err && (err != kIOUSBTransactionTimeout))
{
USBLog(2, "IOUSBPipe[%p]::Write(sync) returned 0x%x (%s) - stalling pipe", this, err, USBStringFromReturn(err));
_CORRECTSTATUS = kIOUSBPipeStalled;
}
}
}
else
{
if (completion->action == NULL)
{
USBLog(1, "IOUSBPipeV2[%p]::Write - completion has NULL action - returning kIOReturnBadArgument(%p)", this, (void*)kIOReturnBadArgument);
USBTrace(kUSBTPipe, kTPBulkPipeWrite, (uintptr_t)this, kIOReturnBadArgument, 0, 0 );
return kIOReturnBadArgument;
}
if ( streamID != 0 )
{
err = controllerV3->Write(streamID, buffer, _address, &_endpoint, completion, noDataTimeout, completionTimeout, reqCount);
}
else
{
err = _controller->Write(buffer, _address, &_endpoint, completion, noDataTimeout, completionTimeout, reqCount);
}
}
if (err == kIOUSBPipeStalled)
{
USBLog(2, "IOUSBPipeV2[%p]::Write - controller returned stalled pipe, changing status", this);
_CORRECTSTATUS = kIOUSBPipeStalled;
}
USBTrace_End( kUSBTPipe, kTPBulkPipeWrite, (uintptr_t)this, _address, _endpoint.number, err);
USBLog(7, "IOUSBPipeV2[%p]::Write (addr %d:%d type %d) - reqCount = %qd, returning 0x%x", this, _address, _endpoint.number , _endpoint.transferType, (uint64_t)reqCount, err);
return err;
}
IOReturn
IOUSBPipeV2::CreateStreams(UInt32 maxStreams)
{
IOUSBControllerV3 * controllerV3;
IOReturn ret;
controllerV3 = OSDynamicCast(IOUSBControllerV3, _controller);
if ( controllerV3 == NULL )
{
USBLog(2,"IOUSBPipeV2[%p]:CreateStreams -- Requested stream creation, but this IOUSBController does not support it", this);
return kIOReturnUnsupported;
}
ret = controllerV3->CreateStreams(_address, _endpoint.number, _endpoint.direction, maxStreams);
if(ret == kIOReturnSuccess)
{
_configuredStreams = maxStreams;
}
return(ret);
}
#pragma mark Accessors
UInt32
IOUSBPipeV2::SupportsStreams(void)
{
return ((1 << _maxStream) - 1);
}
UInt32
IOUSBPipeV2::GetConfiguredStreams(void)
{
if(_configuredStreams != 0)
{
USBLog(2,"IOUSBPipeV2[%p]:GetConfiguredStreams -- fn:%d, %d, %d - Configured streams: %d", this, _address, (uint32_t)_endpoint.number, _endpoint.direction, (uint32_t)_configuredStreams);
}
return(_configuredStreams);
}
UInt8
IOUSBPipeV2::GetMaxBurst(void)
{
return _maxBurst;
}
UInt8
IOUSBPipeV2::GetMult(void)
{
return _mult;
}
UInt16
IOUSBPipeV2::GetBytesPerInterval(void)
{
return _bytesPerInterval;
}
const IOUSBSuperSpeedEndpointCompanionDescriptor *
IOUSBPipeV2::GetSuperSpeedEndpointCompanionDescriptor()
{
return _sscd;
}
#pragma mark IOUSBPipe overrides
IOReturn
IOUSBPipeV2::SetPipePolicy(UInt16 maxPacketSize, UInt8 maxInterval)
{
UInt16 oldsize = _endpoint.maxPacketSize;
IOReturn err = kIOReturnSuccess;
IOUSBControllerV3 *controllerV3 = OSDynamicCast(IOUSBControllerV3, _controller);
USBLog(6, "IOUSBPipeV2[%p]::SetPipePolicy (addr %d:%d dir: %d, type: %d) - maxPacketSize: %d, maxInterval: %d", this, _address, _endpoint.number, _endpoint.direction, _endpoint.transferType, maxPacketSize, maxInterval);
if ((_SPEED == kUSBDeviceSpeedSuper) && (maxPacketSize != 0) && _sscd && _INTERFACE)
{
UInt32 calculatedMax = _INTERFACE->CalculateFullMaxPacketSize((IOUSBEndpointDescriptor*)&_endpoint, _sscd);
if (maxPacketSize != calculatedMax)
{
USBLog(1, "IOUSBPipeV2[%p]::SetPipePolicy - cannot change the MPS of a SS Int/Isoc pipe to anything other than 0 (%d vs %d)", this, maxPacketSize, _INTERFACE->CalculateFullMaxPacketSize((IOUSBEndpointDescriptor*)&_endpoint, _sscd));
return kIOReturnBadArgument;
}
}
if (_endpoint.transferType != kUSBIsoc)
{
return super::SetPipePolicy(maxPacketSize, maxInterval);
}
if (maxPacketSize <= _INTERFACE->CalculateFullMaxPacketSize((IOUSBEndpointDescriptor*)_descriptor, _sscd))
{
USBLog(6, "IOUSBPipeV2[%p]::SetPipePolicy - trying to change isoch pipe from %d to %d bytes", this, oldsize, maxPacketSize);
_endpoint.maxPacketSize = maxPacketSize;
if (controllerV3 && _maxBurst)
err = controllerV3->OpenPipe(_address, _SPEED, &_endpoint, 0, _maxBurst | (_mult << 8));
else
err = _controller->OpenPipe(_address, _SPEED, &_endpoint);
if (err)
{
USBLog(2, "IOUSBPipeV2[%p]::SetPipePolicy - new OpenPipe failed with 0x%x (%s) - returning old settings", this, err, USBStringFromReturn(err));
_endpoint.maxPacketSize = oldsize;
}
}
else
{
USBLog(2, "IOUSBPipeV2[%p]::SetPipePolicy - requested size (%d) larger than maxPacketSize in descriptor (%d)", this, maxPacketSize, _endpoint.maxPacketSize);
err = kIOReturnBadArgument;
}
return err;
}
#pragma mark Padding Slots
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 0);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 1);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 2);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 3);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 4);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 5);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 6);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 7);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 8);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 9);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 10);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 11);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 12);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 13);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 14);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 15);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 16);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 17);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 18);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 19);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 20);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 21);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 22);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 23);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 24);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 25);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 26);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 27);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 28);
OSMetaClassDefineReservedUnused(IOUSBPipeV2, 29);